Skip to content

Commit

Permalink
Rework problem details on device views (#774)
Browse files Browse the repository at this point in the history
* Rework pb details exceptions on devices listing #765

* Rework pb details exceptions on ConnectionStringDialog #765

* Rework pb details exceptions on DeleteDevicePage #765

* Rework pb details exceptions on DeviceDetailPage #765

* Rework pb details exceptions on CreateDevicePage #765

* Rework pb details exceptions on EditLoraDevice #765

* Add unit tests on pb details on devices views

* Add unit tests on ConnectionStringDialog

* Add mac os gitignore

* Update ConnectionStringDialogTests.cs

Remove OnClickCancelConnectionStringDialogMustBeCanceled unit test

* Rework pb details exceptions on devices listing #765

* Rework pb details exceptions on ConnectionStringDialog #765

* Rework pb details exceptions on DeleteDevicePage #765

* Rework pb details exceptions on DeviceDetailPage #765

* Rework pb details exceptions on CreateDevicePage #765

* Rework pb details exceptions on EditLoraDevice #765

* Add unit tests on pb details on devices views

* Add unit tests on ConnectionStringDialog

* Add mac os gitignore

* Update ConnectionStringDialogTests.cs

Remove OnClickCancelConnectionStringDialogMustBeCanceled unit test
  • Loading branch information
hocinehacherouf authored Jun 10, 2022
1 parent c8931a0 commit 002b61e
Show file tree
Hide file tree
Showing 11 changed files with 706 additions and 159 deletions.
40 changes: 39 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,42 @@ _site/
# Ignore folders generated by Bundler
.bundle/
vendor/
Gemfile.lock
Gemfile.lock

# Created by https://www.toptal.com/developers/gitignore/api/macos
# Edit at https://www.toptal.com/developers/gitignore?templates=macos

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

# End of https://www.toptal.com/developers/gitignore/api/macos
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Server.Tests.Unit.Pages
{
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Bunit;
using Client.Exceptions;
using Client.Models;
using Client.Pages.Devices;
using Client.Services;
using FluentAssertions;
using Helpers;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Models.v10;
using Moq;
using MudBlazor;
using MudBlazor.Interop;
using MudBlazor.Services;
using NUnit.Framework;
using RichardSzalay.MockHttp;

[TestFixture]
public class ConnectionStringDialogTests : IDisposable
{
private Bunit.TestContext testContext;
private MockHttpMessageHandler mockHttpClient;

private MockRepository mockRepository;
private Mock<IJSRuntime> mockJSRuntime;

[SetUp]
public void SetUp()
{
this.testContext = new Bunit.TestContext();

this.mockRepository = new MockRepository(MockBehavior.Strict);
this.mockJSRuntime = this.mockRepository.Create<IJSRuntime>();
this.mockHttpClient = this.testContext.Services
.AddMockHttpClient();

_ = this.testContext.Services.AddSingleton(new ClipboardService(this.mockJSRuntime.Object));

_ = this.testContext.Services.AddMudServices();

_ = this.testContext.JSInterop.SetupVoid("mudKeyInterceptor.connect", _ => true);
_ = this.testContext.JSInterop.SetupVoid("mudPopover.connect", _ => true);
_ = this.testContext.JSInterop.SetupVoid("Blazor._internal.InputFile.init", _ => true);
_ = this.testContext.JSInterop.Setup<BoundingClientRect>("mudElementRef.getBoundingClientRect", _ => true);
_ = this.testContext.JSInterop.Setup<IEnumerable<BoundingClientRect>>("mudResizeObserver.connect", _ => true);

this.mockHttpClient.AutoFlush = true;
}

private IRenderedComponent<TComponent> RenderComponent<TComponent>(params ComponentParameter[] parameters)
where TComponent : IComponent
{
return this.testContext.RenderComponent<TComponent>(parameters);
}

[Test]
public async Task ConnectionStringDialogMustBeRenderedOnShow()
{
// Arrange
var deviceId = Guid.NewGuid().ToString();

_ = this.mockHttpClient.When(HttpMethod.Get, $"/api/devices/{deviceId}/credentials")
.RespondJson(new EnrollmentCredentials());

var cut = RenderComponent<MudDialogProvider>();
var service = this.testContext.Services.GetService<IDialogService>() as DialogService;

var parameters = new DialogParameters
{
{
"deviceId", deviceId
}
};

// Act
await cut.InvokeAsync(() => service?.Show<ConnectionStringDialog>(string.Empty, parameters));

// Assert
_ = cut.Find("div.mud-dialog-container").Should().NotBeNull();
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

[Test]
public async Task OnInitializedAsyncShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingCredentials()
{
// Arrange
var deviceId = Guid.NewGuid().ToString();

_ = this.mockHttpClient.When(HttpMethod.Get, $"/api/devices/{deviceId}/credentials")
.Throw(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails()));

var cut = RenderComponent<MudDialogProvider>();
var service = this.testContext.Services.GetService<IDialogService>() as DialogService;

var parameters = new DialogParameters
{
{
"deviceId", deviceId
}
};

IDialogReference dialogReference = null;

// Act
await cut.InvokeAsync(() => dialogReference = service?.Show<ConnectionStringDialog>(string.Empty, parameters));

var result = await dialogReference.Result;

// Assert
_ = result.Cancelled.Should().BeFalse();
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ namespace AzureIoTHub.Portal.Server.Tests.Unit.Pages
using AzureIoTHub.Portal.Server.Tests.Unit.Helpers;
using Bunit;
using Bunit.TestDoubles;
using Client.Exceptions;
using Client.Models;
using FluentAssertions;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Moq;
Expand Down Expand Up @@ -156,6 +159,185 @@ public async Task ClickOnSaveShouldPostDeviceDetailsAsync()
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

[Test]
public void OnInitializedAsyncShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingDeviceTags()
{
var mockDeviceModel = new DeviceModel
{
ModelId = Guid.NewGuid().ToString(),
Description = Guid.NewGuid().ToString(),
SupportLoRaFeatures = false,
Name = Guid.NewGuid().ToString()
};

_ = this.mockHttpClient.When(HttpMethod.Get, $"/api/models")
.RespondJson(new DeviceModel[]
{
mockDeviceModel
});

_ = this.mockHttpClient.When(HttpMethod.Get, $"/api/settings/device-tags")
.Throw(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails()));

// Act
var cut = RenderComponent<CreateDevicePage>();

// Assert
_ = cut.Markup.Should().NotBeNullOrEmpty();
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

[Test]
public void OnInitializedAsyncShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingDeviceModels()
{
_ = this.mockHttpClient.When(HttpMethod.Get, $"/api/models")
.Throw(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails()));

// Act
var cut = RenderComponent<CreateDevicePage>();

// Assert
_ = cut.Markup.Should().NotBeNullOrEmpty();
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

[Test]
public async Task SaveShouldProcessProblemDetailsExceptionWhenIssueOccursOnCreatingDevice()
{
var mockDeviceModel = new DeviceModel
{
ModelId = Guid.NewGuid().ToString(),
Description = Guid.NewGuid().ToString(),
SupportLoRaFeatures = false,
Name = Guid.NewGuid().ToString()
};

var expectedDeviceDetails = new DeviceDetails
{
DeviceName = Guid.NewGuid().ToString(),
ModelId = mockDeviceModel.ModelId,
DeviceID = Guid.NewGuid().ToString(),
};


_ = this.mockHttpClient.When(HttpMethod.Post, $"{ApiBaseUrl}")
.With(m =>
{
Assert.IsAssignableFrom<ObjectContent<DeviceDetails>>(m.Content);
var objectContent = m.Content as ObjectContent<DeviceDetails>;
Assert.IsNotNull(objectContent);

Assert.IsAssignableFrom<DeviceDetails>(objectContent.Value);
var deviceDetails = objectContent.Value as DeviceDetails;
Assert.IsNotNull(deviceDetails);

Assert.AreEqual(expectedDeviceDetails.DeviceID, deviceDetails.DeviceID);
Assert.AreEqual(expectedDeviceDetails.DeviceName, deviceDetails.DeviceName);
Assert.AreEqual(expectedDeviceDetails.ModelId, deviceDetails.ModelId);

return true;
})
.Throw(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails()));

_ = this.mockHttpClient.When(HttpMethod.Get, "/api/models")
.RespondJson(new DeviceModel[]
{
mockDeviceModel
});

_ = this.mockHttpClient.When(HttpMethod.Get, "/api/settings/device-tags")
.RespondJson(new List<DeviceTag>
{
new()
{
Label = Guid.NewGuid().ToString(),
Name = Guid.NewGuid().ToString(),
Required = false,
Searchable = false
}
});

_ = this.mockHttpClient.When(HttpMethod.Get, $"/api/models/{mockDeviceModel.ModelId}/properties")
.RespondJson(Array.Empty<DeviceProperty>());

_ = this.mockHttpClient.When(HttpMethod.Post, $"{ApiBaseUrl}/{expectedDeviceDetails.DeviceID}/properties")
.RespondText(string.Empty);

var cut = RenderComponent<CreateDevicePage>();
Thread.Sleep(2500);
var saveButton = cut.WaitForElement("#SaveButton");

var mockDialogReference = new DialogReference(Guid.NewGuid(), this.mockDialogService.Object);

_ = this.mockDialogService.Setup(c => c.Show<ProcessingDialog>("Processing", It.IsAny<DialogParameters>()))
.Returns(mockDialogReference);

_ = this.mockDialogService.Setup(c => c.Close(It.Is<DialogReference>(x => x == mockDialogReference)));

// Act
cut.Find($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName);
cut.Find($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID);
await cut.Instance.ChangeModel(mockDeviceModel);

saveButton.Click();
Thread.Sleep(2500);

// Assert
_ = this.mockNavigationManager.Uri.Should().NotEndWith("/devices");
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

[Test]
public async Task ChangeModelShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingModelProperties()
{
var mockDeviceModel = new DeviceModel
{
ModelId = Guid.NewGuid().ToString(),
Description = Guid.NewGuid().ToString(),
SupportLoRaFeatures = false,
Name = Guid.NewGuid().ToString()
};

var expectedDeviceDetails = new DeviceDetails
{
DeviceName = Guid.NewGuid().ToString(),
ModelId = mockDeviceModel.ModelId,
DeviceID = Guid.NewGuid().ToString(),
};

_ = this.mockHttpClient.When(HttpMethod.Get, "/api/models")
.RespondJson(new DeviceModel[]
{
mockDeviceModel
});

_ = this.mockHttpClient.When(HttpMethod.Get, "/api/settings/device-tags")
.RespondJson(new List<DeviceTag>
{
new()
{
Label = Guid.NewGuid().ToString(),
Name = Guid.NewGuid().ToString(),
Required = false,
Searchable = false
}
});

_ = this.mockHttpClient.When(HttpMethod.Get, $"/api/models/{mockDeviceModel.ModelId}/properties")
.Throw(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails()));

var cut = RenderComponent<CreateDevicePage>();
Thread.Sleep(2500);

// Act
cut.Find($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName);
cut.Find($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID);
await cut.Instance.ChangeModel(mockDeviceModel);

// Assert
this.mockHttpClient.VerifyNoOutstandingExpectation();
}

public void Dispose()
{
Dispose(true);
Expand Down
Loading

0 comments on commit 002b61e

Please sign in to comment.