diff --git a/src/AzureIoTHub.Portal.Client/Components/Devices/EditDevice.razor b/src/AzureIoTHub.Portal.Client/Components/Devices/EditDevice.razor new file mode 100644 index 000000000..1ba801cc9 --- /dev/null +++ b/src/AzureIoTHub.Portal.Client/Components/Devices/EditDevice.razor @@ -0,0 +1,815 @@ +@using AzureIoTHub.Portal.Client.Pages.Devices +@using AzureIoTHub.Portal.Client.Validators +@using AzureIoTHub.Portal.Shared.Models +@using AzureIoTHub.Portal.Models +@using AzureIoTHub.Portal.Models.v10 +@using AzureIoTHub.Portal.Models.v10.LoRaWAN +@using AzureIoTHub.Portal.Shared.Models.v10.Filters; +@using Microsoft.AspNetCore.WebUtilities + +@implements IDisposable + +@inject ISnackbar Snackbar +@inject IDialogService DialogService +@inject IDeviceModelsClientService DeviceModelsClientService +@inject ILoRaWanDeviceModelsClientService LoRaWanDeviceModelsClientService +@inject IDeviceTagSettingsClientService DeviceTagSettingsClientService +@inject IDeviceClientService DeviceClientService +@inject ILoRaWanDeviceClientService LoRaWanDeviceClientService +@inject IDeviceLayoutService DeviceLayoutService +@inject NavigationManager NavigationManager + + + + + + + + @if (context.Equals(CreateEditMode.Create)) + { + @(string.IsNullOrEmpty(Device.DeviceName) ? Device.DeviceID : Device.DeviceName) + } + else if (context.Equals(CreateEditMode.Edit)) + { + Model: @DeviceModel.Name + @(string.IsNullOrEmpty(Device.DeviceName) ? Device.DeviceID : Device.DeviceName) + } + + + +
+ +
+
+ @if (context.Equals(CreateEditMode.Edit)) + { + + @if (isLoaded && (!IsLoRa || !(Device is LoRaDeviceDetails))) + { + Connect + } + + } +
+ @if (context.Equals(CreateEditMode.Create)) + { + + + @saveButtonText + + Save + Save and add new + Save and duplicate + + + + } + else if (context.Equals(CreateEditMode.Edit)) + { + + Delete device + + @saveButtonText + + Save + Duplicate + + + + } +
+ + + + + + + + Details + + + @if (context.Equals(CreateEditMode.Create)) + { + + @if (duplicateDevice) + { + + } + else + { + this.DeviceModel) + Variant="Variant.Outlined" + ToStringFunc="@(x => x?.Name)" + ResetValueOnEmptyText=true + Immediate=true + Clearable=true + CoerceText=true + CoerceValue=false> + + @context.Name + + @((!string.IsNullOrEmpty(@context.Description) && @context.Description.Length > 100) ? @context.Description.Substring(0, 100) + "..." : @context.Description) + + + + @if (Device.ModelId == null && displayValidationErrorMessages) + { +

The Model is required.

+ } + } +
+ } + + @if (context.Equals(CreateEditMode.Create)) + { + + @if (IsLoRa) + { + + } + else + { + + } + + } + else if (context.Equals(CreateEditMode.Edit)) + { + + + + } + + @if (context.Equals(CreateEditMode.Create)) + { + + + + } + + + + + + + + Status + + + + Enabled + The device can connect to the platform. + + + Disabled + The device cannot connect to the platform. + + + +
+
+
+
+
+ + + + Tags + + + @foreach (DeviceTagDto tag in TagList) + { + if (context.Equals(CreateEditMode.Create)) + { + + + + } + else if (context.Equals(CreateEditMode.Edit)) + { + + @if (!Device.Tags.ContainsKey(tag.Name)) + { + Device.Tags.Add(tag.Name, ""); + } + + + } + } + + + + + + + + + + Labels + + + @if (context.Equals(CreateEditMode.Create)) + { + + } + else if (context.Equals(CreateEditMode.Edit)) + { + + } + + + + + + + @if (!IsLoRa && Properties.Any()) + { + + + + Properties + + + @foreach (var item in Properties.OrderBy(c => c.Order)) + { + switch (item.PropertyType) + { + case DevicePropertyType.Boolean: + + + + break; + case DevicePropertyType.Double: + + string.IsNullOrEmpty(c) || double.TryParse(c, out var result)) + Clearable="true" /> + + break; + case DevicePropertyType.Float: + + string.IsNullOrEmpty(c) || float.TryParse(c, out var result)) + Clearable="true" /> + + break; + case DevicePropertyType.Integer: + + string.IsNullOrEmpty(c) || int.TryParse(c, out var result)) + Clearable="true" /> + + break; + case DevicePropertyType.Long: + + string.IsNullOrEmpty(c) || long.TryParse(c, out var result)) + Clearable="true" /> + + break; + case DevicePropertyType.String: + + + + break; + } + } + + + + + + } +
+
+ + @if (context.Equals(CreateEditMode.Create)) + { + if (IsLoRa) + { + + + + } + } + else if (context.Equals(CreateEditMode.Edit)) + { + if (IsLoRa && Device != null && Commands != null) + { + + + + } + } +
+
+
+
+ +@code { + [Parameter] + public CreateEditMode context { get; set; } + + [CascadingParameter] + public Error Error { get; set; } = default!; + + private MudForm form = default!; + + private DeviceDetailsValidator standardValidator = new DeviceDetailsValidator(); + private LoRaDeviceDetailsValidator loraValidator = new LoRaDeviceDetailsValidator(); + + private LoRaDeviceModelDto LoRaDeviceModelDto { get; set; } = default!; + + private IDeviceDetails Device { get; set; } = new DeviceDetails(); + + public PatternMask maskLoRaDeviceID = new PatternMask("XXXXXXXXXXXXXXXX") + { + MaskChars = new[] { new MaskChar('X', @"[0-9a-fA-F]") }, + CleanDelimiters = false, + Transformation = AllUpperCase + }; + + private static char AllUpperCase(char c) => c.ToString().ToUpperInvariant()[0]; + + private IDeviceModel _deviceModel = default!; + + private IDeviceModel DeviceModel + { + get + { + return _deviceModel; + } + set + { + if (context.Equals(CreateEditMode.Create)) + { + Task.Run(async () => await ChangeModel(value)); + } + else if (context.Equals(CreateEditMode.Edit)) + { + _deviceModel = value; + } + } + } + + private IEnumerable TagList { get; set; } = Array.Empty(); + private List Properties = new List(); + + private bool displayValidationErrorMessages; + + private bool duplicateDevice; + + private DeviceSaveAction deviceSaveAction = DeviceSaveAction.Save; + private string saveButtonText = "Save"; + + [Parameter] + public string DeviceID { get; set; } = default!; + + private bool isLoaded; + + private bool isProcessing; + + private IEnumerable Commands { get; set; } = Array.Empty(); + + private void setPropertiesWithContext() + { + if (context.Equals(CreateEditMode.Create)) + { + TagList = new List(); + + displayValidationErrorMessages = false; + } + else if (context.Equals(CreateEditMode.Edit)) + { + DeviceModel = new DeviceModelDto(); + + isLoaded = false; + + TagList = Array.Empty(); + } + } + + [Parameter] + //[SupplyParameterFromQuery] + public bool IsLoRa { get; set; } + + //private void setIsLoRaWithContext() + //{ + // if (context.Equals(CreateEditMode.Create)) + // { + // IsLoRa = Device is LoRaDeviceDetails; + // } + // else if (context.Equals(CreateEditMode.Edit)) + // { + // var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri); + + // IsLoRa = QueryHelpers.ParseQuery(uri.Query).TryGetValue("isLora", out var isLora); + // } + //} + + protected override async Task OnInitializedAsync() + { + setPropertiesWithContext(); + //setIsLoRaWithContext(); + + try + { + if (context.Equals(CreateEditMode.Create)) + { + DeviceLayoutService.RefreshDeviceOccurred += DeviceServiceOnRefreshDeviceOccurred!; + Device = DeviceLayoutService.GetSharedDevice() ?? this.Device; + DeviceModel = DeviceLayoutService.GetSharedDeviceModel() ?? this.DeviceModel; + + // Enable device by default + Device.IsEnabled = true; + + // Gets the custom tags that can be set when creating a device + TagList = await DeviceTagSettingsClientService.GetDeviceTags(); + + foreach (var tag in TagList) + { + Device.Tags.TryAdd(tag.Name, string.Empty); + } + } + else if (context.Equals(CreateEditMode.Edit)) + { + isProcessing = true; + + if (IsLoRa) + { + Device = await LoRaWanDeviceClientService.GetDevice(DeviceID); + Commands = await LoRaWanDeviceModelsClientService.GetDeviceModelCommands(Device.ModelId); + DeviceModel = await LoRaWanDeviceModelsClientService.GetDeviceModel(Device.ModelId); + } + else + { + Device = await DeviceClientService.GetDevice(DeviceID); + Properties = (await DeviceClientService.GetDeviceProperties(DeviceID)).ToList(); + DeviceModel = await DeviceModelsClientService.GetDeviceModel(Device.ModelId); + } + + TagList = await DeviceTagSettingsClientService.GetDeviceTags(); + + isLoaded = true; + isProcessing = false; + } + } + catch (ProblemDetailsException exception) + { + Error?.ProcessProblemDetails(exception); + } + } + + private void ProcessActionOnDevice() + { + switch (deviceSaveAction) + { + case DeviceSaveAction.Save: + Save(); + break; + case DeviceSaveAction.Duplicate: + DeviceLayoutService.DuplicateSharedDevice(Device); + DeviceLayoutService.DuplicateSharedDeviceModel(DeviceModel); + NavigationManager.NavigateTo("devices/new"); + break; + } + } + + public void Dispose() + { + DeviceLayoutService.ResetSharedDevice(); + DeviceLayoutService.ResetSharedDeviceModel(); + DeviceLayoutService.RefreshDeviceOccurred -= DeviceServiceOnRefreshDeviceOccurred!; + } + + /// + /// Sends a POST request to the DevicesController, to add the new device to the Azure IoT Hub + /// + public async void Save() + { + try + { + isProcessing = true; + + if (context.Equals(CreateEditMode.Create)) + { + await form.Validate(); + + if (CheckTagsError() || CheckGeneralValidation() || CheckLoRaValidation()) + { + Snackbar.Add("One or more validation errors occurred", Severity.Error); + + // Allows to display ValidationError messages for the MudAutocomplete field. + displayValidationErrorMessages = true; + + isProcessing = false; + + return; + } + + if (IsLoRa) + await LoRaWanDeviceClientService.CreateDevice((Device as LoRaDeviceDetails)!); + else + { + await DeviceClientService.CreateDevice((Device as DeviceDetails)!); + await DeviceClientService.SetDeviceProperties(Device.DeviceID, Properties); + } + + // Prompts a snack bar to inform the action was successful + Snackbar.Add($"Device {Device.DeviceID} has been successfully created!\r\nPlease note that changes might take some minutes to be visible in the list...", Severity.Success); + + ProcessPostDeviceCreation(); + } + else if (context.Equals(CreateEditMode.Edit)) + { + await form.Validate(); + + if (CheckTagsError() || CheckGeneralValidation() || CheckLoRaValidation()) + { + Snackbar.Add("One or more validation errors occurred", Severity.Error); + + isProcessing = false; + + return; + } + + if (IsLoRa) + { + await LoRaWanDeviceClientService.UpdateDevice((Device as LoRaDeviceDetails)!); + } + else + { + await DeviceClientService.UpdateDevice((Device as DeviceDetails)!); + + await DeviceClientService.SetDeviceProperties(DeviceID, Properties.ToList()); + } + + // Prompts a snack bar to inform the action was successful + Snackbar.Add($"Device {Device.DeviceName} has been successfully updated!\r\nPlease note that changes might take some minutes to be visible in the list...", Severity.Success, null); + + NavigationManager.NavigateTo("devices"); + } + } + catch (ProblemDetailsException exception) + { + Error?.ProcessProblemDetails(exception); + } + finally + { + isProcessing = false; + if (context.Equals(CreateEditMode.Edit)) StateHasChanged(); + } + } + + public async Task ShowConnectionString() + { + var parameters = new DialogParameters(); + parameters.Add(nameof(ConnectionStringDialog.deviceId), this.DeviceID); + + _ = await DialogService.Show("Device Credentials", parameters).Result; + } + + private void ProcessPostDeviceCreation() + { + switch (deviceSaveAction) + { + case DeviceSaveAction.Save: + NavigationManager.NavigateTo("devices"); + break; + case DeviceSaveAction.SaveAndAddNew: + Device = DeviceLayoutService.ResetSharedDevice(TagList.ToList()); + DeviceModel = DeviceLayoutService.ResetSharedDeviceModel(); + break; + case DeviceSaveAction.SaveAndDuplicate: + Device = DeviceLayoutService.DuplicateSharedDevice((Device as DeviceDetails)!); + DeviceModel = DeviceLayoutService.DuplicateSharedDeviceModel(DeviceModel); + break; + } + } + + private void DeviceServiceOnRefreshDeviceOccurred(object sender, EventArgs e) + { + Device = DeviceLayoutService.GetSharedDevice(); + DeviceModel = DeviceLayoutService.GetSharedDeviceModel(); + StateHasChanged(); + } + + private bool CheckTagsError() + { + bool tagValidationError = false; + + foreach (DeviceTagDto tag in TagList) + { + if (tag.Required && string.IsNullOrEmpty(Device.Tags[tag.Name])) + { + tagValidationError = true; + } + } + return tagValidationError; + } + + private bool CheckLoRaValidation() + { + if (!IsLoRa) + { + return false; + } + + if (this.Device is LoRaDeviceDetails loRaDeviceDetails) + { + return !this.loraValidator.Validate(loRaDeviceDetails).IsValid; + } + + return true; + } + + private bool CheckGeneralValidation() + { + if (!IsLoRa && this.Device is DeviceDetails deviceDetails) + { + return !this.standardValidator.Validate(deviceDetails).IsValid; + } + + return CheckLoRaValidation(); + } + + /// + /// Prompts a pop-up windows to confirm the device's deletion. + /// + /// Device to delete from the hub + /// + private async Task DeleteDevice() + { + isProcessing = true; + + var parameters = new DialogParameters + { + {"deviceID", Device.DeviceID}, + {"deviceName", Device.DeviceName}, + {"IsLoRaWan", IsLoRa} + }; + var result = await DialogService.Show("Confirm Deletion", parameters).Result; + + isProcessing = false; + + if (result.Canceled) + { + return; + } + + // Go back to the list of devices + NavigationManager.NavigateTo("devices"); + } + + private async Task> Search(string value) + { + var filter = new DeviceModelFilter + { + SearchText = value, + PageNumber = 0, + PageSize = 100, + OrderBy = new string[] + { + string.Empty + } + }; + return (await DeviceModelsClientService.GetDeviceModels(filter)).Items.ToList(); + } + + private void SetSaveButtonText(DeviceSaveAction saveAction) + { + if (context.Equals(CreateEditMode.Create)) + { + deviceSaveAction = saveAction; + saveButtonText = deviceSaveAction switch + { + DeviceSaveAction.Save => "Save", + DeviceSaveAction.SaveAndAddNew => "Save and add new", + DeviceSaveAction.SaveAndDuplicate => "Save and duplicate", + _ => saveButtonText + }; + } + else if (context.Equals(CreateEditMode.Edit)) + { + deviceSaveAction = saveAction; + saveButtonText = deviceSaveAction switch + { + DeviceSaveAction.Save => "Save", + DeviceSaveAction.Duplicate => "Duplicate", + _ => saveButtonText + }; + } + } + + internal async Task ChangeModel(IDeviceModel model) + { + try + { + Properties.Clear(); + + _deviceModel = model; + + Device = new DeviceDetails + { + DeviceID = Device.DeviceID, + ModelId = model?.ModelId, + ImageUrl = model?.ImageUrl, + DeviceName = Device.DeviceName, + IsEnabled = Device.IsEnabled, + Tags = Device.Tags + }; + + if (model == null || string.IsNullOrWhiteSpace(model.ModelId)) + { + return; + } + + if (model.SupportLoRaFeatures) + { + var loRaDeviceModelResult = await LoRaWanDeviceModelsClientService.GetDeviceModel(model.ModelId); + + this.Device = new LoRaDeviceDetails + { + DeviceID = this.Device.DeviceID, + ModelId = model.ModelId, + ImageUrl = model.ImageUrl, + DeviceName = this.Device.DeviceName, + IsEnabled = this.Device.IsEnabled, + Tags = this.Device.Tags, + SensorDecoder = loRaDeviceModelResult.SensorDecoder, + UseOTAA = loRaDeviceModelResult.UseOTAA + }; + + LoRaDeviceModelDto = loRaDeviceModelResult; + + IsLoRa = true; + } + else + { + var properties = await DeviceModelsClientService.GetDeviceModelModelProperties(model.ModelId); + + Properties.AddRange(properties.Select(x => new DevicePropertyValue + { + DisplayName = x.DisplayName, + IsWritable = x.IsWritable, + Name = x.Name, + Order = x.Order, + PropertyType = x.PropertyType + })); + + IsLoRa = false; + } + } + catch (ProblemDetailsException exception) + { + Error?.ProcessProblemDetails(exception); + } + finally + { + await InvokeAsync(StateHasChanged); + } + } +} diff --git a/src/AzureIoTHub.Portal.Client/Enums/CreateEditMode.cs b/src/AzureIoTHub.Portal.Client/Enums/CreateEditMode.cs new file mode 100644 index 000000000..94873bfaf --- /dev/null +++ b/src/AzureIoTHub.Portal.Client/Enums/CreateEditMode.cs @@ -0,0 +1,11 @@ +// 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.Client.Enums +{ + public enum CreateEditMode + { + Create, + Edit + } +} diff --git a/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor b/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor index 7674f9b22..81c466ec7 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor @@ -1,547 +1,7 @@ @page "/devices/new" -@using AzureIoTHub.Portal.Client.Pages.Devices -@using AzureIoTHub.Portal.Client.Validators -@using AzureIoTHub.Portal.Models -@using AzureIoTHub.Portal.Models.v10 -@using AzureIoTHub.Portal.Models.v10.LoRaWAN -@using AzureIoTHub.Portal.Shared.Models -@using AzureIoTHub.Portal.Shared.Models.v10; -@using AzureIoTHub.Portal.Shared.Models.v10.Filters; -@using Microsoft.AspNetCore.Components - -@attribute [Authorize] -@inject ISnackbar Snackbar -@inject NavigationManager NavManager -@inject IDeviceModelsClientService DeviceModelsClientService -@inject ILoRaWanDeviceModelsClientService LoRaWanDeviceModelsClientService -@inject IDeviceTagSettingsClientService DeviceTagSettingsClientService -@inject IDeviceClientService DeviceClientService -@inject ILoRaWanDeviceClientService LoRaWanDeviceClientService -@inject IDeviceLayoutService DeviceLayoutService - -@implements IDisposable - Create Device - - - - - - - @(string.IsNullOrEmpty(Device.DeviceName) ? Device.DeviceID : Device.DeviceName) - - - -
- -
-
-
- - - @saveButtonText - - Save - Save and add new - Save and duplicate - - - -
- - - - - - - - Details - - - - @if (duplicateDevice) - { - - } - else - { - this.DeviceModel) - Variant="Variant.Outlined" - ToStringFunc="@(x => x?.Name)" - ResetValueOnEmptyText=true - Immediate=true - Clearable=true - CoerceText=true - CoerceValue=false> - - @context.Name - - @((!string.IsNullOrEmpty(@context.Description) && @context.Description.Length > 100) ? @context.Description.Substring(0, 100) + "..." : @context.Description) - - - - @if (Device.ModelId == null && displayValidationErrorMessages) - { -

The Model is required.

- } - } -
- - @if (IsLoRa) - { - - } - else - { - - } - - - - - - - - - - Status - - - - Enabled - The device can connect to the platform. - - - Disabled - The device cannot connect to the platform. - - - -
-
-
-
-
- - - - Tags - - - @foreach (DeviceTagDto tag in TagList) - { - - - - } - - - - - - - - - - Labels - - - - - - - - @if (!IsLoRa && Properties.Any()) - { - - - - Properties - - - @foreach (var item in Properties.OrderBy(c => c.Order)) - { - switch (item.PropertyType) - { - case DevicePropertyType.Boolean: - - - - break; - case DevicePropertyType.Double: - - string.IsNullOrEmpty(c) || double.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.Float: - - string.IsNullOrEmpty(c) || float.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.Integer: - - string.IsNullOrEmpty(c) || int.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.Long: - - string.IsNullOrEmpty(c) || long.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.String: - - - - break; - } - } - - - - - - } -
-
- @if (IsLoRa) - { - - - - } -
-
-
-
+ @code { - [CascadingParameter] - public Error Error { get; set; } = default!; - - private IDeviceDetails Device = new DeviceDetails(); - - private MudForm form = default!; - - private DeviceDetailsValidator standardValidator = new DeviceDetailsValidator(); - private LoRaDeviceDetailsValidator loraValidator = new LoRaDeviceDetailsValidator(); - private LoRaDeviceModelDto LoRaDeviceModelDto { get; set; } = default!; - - private bool IsLoRa - { - get - { - return Device is LoRaDeviceDetails; - } - } - - public PatternMask maskLoRaDeviceID = new PatternMask("XXXXXXXXXXXXXXXX") - { - MaskChars = new[] { new MaskChar('X', @"[0-9a-fA-F]") }, - CleanDelimiters = false, - Transformation = AllUpperCase - }; - - private static char AllUpperCase(char c) => c.ToString().ToUpperInvariant()[0]; - - private IDeviceModel _deviceModel = default!; - - private IDeviceModel DeviceModel - { - get => _deviceModel; - set { Task.Run(async () => await ChangeModel(value)); } - } - - private IEnumerable TagList { get; set; } = new List(); - private List Properties = new List(); - - private bool displayValidationErrorMessages = false; - - private bool isProcessing; - private bool duplicateDevice; - - private DeviceSaveAction deviceSaveAction = DeviceSaveAction.Save; - private string saveButtonText = "Save"; - - protected override async Task OnInitializedAsync() - { - try - { - DeviceLayoutService.RefreshDeviceOccurred += DeviceServiceOnRefreshDeviceOccurred!; - Device = DeviceLayoutService.GetSharedDevice() ?? this.Device; - DeviceModel = DeviceLayoutService.GetSharedDeviceModel() ?? this.DeviceModel; - - // Enable device by default - Device.IsEnabled = true; - - // Gets the custom tags that can be set when creating a device - TagList = await DeviceTagSettingsClientService.GetDeviceTags(); - - foreach (var tag in TagList) - { - Device.Tags.TryAdd(tag.Name, string.Empty); - } - } - catch (ProblemDetailsException exception) - { - Error?.ProcessProblemDetails(exception); - } - } - - public void Dispose() - { - DeviceLayoutService.ResetSharedDevice(); - DeviceLayoutService.ResetSharedDeviceModel(); - DeviceLayoutService.RefreshDeviceOccurred -= DeviceServiceOnRefreshDeviceOccurred!; - } - - /// - /// Sends a POST request to the DevicesController, to add the new device to the Azure IoT Hub - /// - public async void Save() - { - try - { - isProcessing = true; - - await form.Validate(); - - if (CheckTagsError() || CheckGeneralValidation() || CheckLoRaValidation()) - { - Snackbar.Add("One or more validation errors occurred", Severity.Error); - - // Allows to display ValidationError messages for the MudAutocomplete field. - displayValidationErrorMessages = true; - - isProcessing = false; - - return; - } - - if (IsLoRa) - await LoRaWanDeviceClientService.CreateDevice((Device as LoRaDeviceDetails)!); - else - { - await DeviceClientService.CreateDevice((Device as DeviceDetails)!); - await DeviceClientService.SetDeviceProperties(Device.DeviceID, Properties); - } - - // Prompts a snack bar to inform the action was successful - Snackbar.Add($"Device {Device.DeviceID} has been successfully created!\r\nPlease note that changes might take some minutes to be visible in the list...", Severity.Success); - - ProcessPostDeviceCreation(); - } - catch (ProblemDetailsException exception) - { - Error?.ProcessProblemDetails(exception); - } - finally - { - isProcessing = false; - } - } - - private void ProcessPostDeviceCreation() - { - switch (deviceSaveAction) - { - case DeviceSaveAction.Save: - NavManager.NavigateTo("devices"); - break; - case DeviceSaveAction.SaveAndAddNew: - Device = DeviceLayoutService.ResetSharedDevice(TagList.ToList()); - DeviceModel = DeviceLayoutService.ResetSharedDeviceModel(); - break; - case DeviceSaveAction.SaveAndDuplicate: - Device = DeviceLayoutService.DuplicateSharedDevice((Device as DeviceDetails)!); - DeviceModel = DeviceLayoutService.DuplicateSharedDeviceModel(DeviceModel); - break; - } - } - - private void DeviceServiceOnRefreshDeviceOccurred(object sender, EventArgs e) - { - Device = DeviceLayoutService.GetSharedDevice(); - DeviceModel = DeviceLayoutService.GetSharedDeviceModel(); - StateHasChanged(); - } - - private bool CheckTagsError() - { - bool tagValidationError = false; - - foreach (DeviceTagDto tag in TagList) - { - if (tag.Required && string.IsNullOrEmpty(Device.Tags[tag.Name])) - { - tagValidationError = true; - } - } - return tagValidationError; - } - - private bool CheckLoRaValidation() - { - if (!IsLoRa) - { - return false; - } - - if (this.Device is LoRaDeviceDetails loRaDeviceDetails) - { - return !this.loraValidator.Validate(loRaDeviceDetails).IsValid; - } - - return true; - } - - private bool CheckGeneralValidation() - { - if (!IsLoRa && this.Device is DeviceDetails deviceDetails) - { - return !this.standardValidator.Validate(deviceDetails).IsValid; - } - - return CheckLoRaValidation(); - } - - /// - /// Allows to autocomplete the Device Model field in the form. - /// - /// Text entered in the field - /// Item of the device model list that matches the user's value - private async Task> Search(string value) - { - var filter = new DeviceModelFilter - { - SearchText = value, - PageNumber = 0, - PageSize = 100, - OrderBy = new string[] - { - string.Empty - } - }; - return (await DeviceModelsClientService.GetDeviceModels(filter)).Items.ToList(); - } - - private void SetSaveButtonText(DeviceSaveAction saveAction) - { - deviceSaveAction = saveAction; - saveButtonText = deviceSaveAction switch - { - DeviceSaveAction.Save => "Save", - DeviceSaveAction.SaveAndAddNew => "Save and add new", - DeviceSaveAction.SaveAndDuplicate => "Save and duplicate", - _ => saveButtonText - }; - } - - internal async Task ChangeModel(IDeviceModel model) - { - try - { - Properties.Clear(); - - _deviceModel = model; - - Device = new DeviceDetails - { - DeviceID = Device.DeviceID, - ModelId = model?.ModelId!, - ImageUrl = model?.ImageUrl!, - DeviceName = Device.DeviceName, - IsEnabled = Device.IsEnabled, - Tags = Device.Tags - }; - - if (model == null || string.IsNullOrWhiteSpace(model.ModelId)) - { - return; - } - - if (model.SupportLoRaFeatures) - { - var loRaDeviceModelResult = await LoRaWanDeviceModelsClientService.GetDeviceModel(model.ModelId); - - this.Device = new LoRaDeviceDetails - { - DeviceID = this.Device.DeviceID, - ModelId = model.ModelId, - ImageUrl = model.ImageUrl, - DeviceName = this.Device.DeviceName, - IsEnabled = this.Device.IsEnabled, - Tags = this.Device.Tags, - SensorDecoder = loRaDeviceModelResult.SensorDecoder, - UseOTAA = loRaDeviceModelResult.UseOTAA - }; - - LoRaDeviceModelDto = loRaDeviceModelResult; - } - else - { - var properties = await DeviceModelsClientService.GetDeviceModelModelProperties(model.ModelId); - - Properties.AddRange(properties.Select(x => new DevicePropertyValue - { - DisplayName = x.DisplayName, - IsWritable = x.IsWritable, - Name = x.Name, - Order = x.Order, - PropertyType = x.PropertyType - })); - } - } - catch (ProblemDetailsException exception) - { - Error?.ProcessProblemDetails(exception); - } - finally - { - await InvokeAsync(StateHasChanged); - } - } } - diff --git a/src/AzureIoTHub.Portal.Client/Pages/Devices/DeviceDetailPage.razor b/src/AzureIoTHub.Portal.Client/Pages/Devices/DeviceDetailPage.razor index 07cc10339..fae06f5e7 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/Devices/DeviceDetailPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/Devices/DeviceDetailPage.razor @@ -1,232 +1,16 @@ @page "/devices/{DeviceID}" -@using AzureIoTHub.Portal.Client.Pages.Devices -@using AzureIoTHub.Portal.Client.Validators -@using AzureIoTHub.Portal.Models -@using AzureIoTHub.Portal.Models.v10 -@using AzureIoTHub.Portal.Models.v10.LoRaWAN -@using AzureIoTHub.Portal.Shared.Models -@attribute [Authorize] @inject NavigationManager NavManager -@inject ISnackbar Snackbar -@inject IDialogService DialogService -@inject IDeviceModelsClientService DeviceModelsClientService -@inject ILoRaWanDeviceModelsClientService LoRaWanDeviceModelsClientService -@inject IDeviceTagSettingsClientService DeviceTagSettingsClientService -@inject IDeviceClientService DeviceClientService -@inject ILoRaWanDeviceClientService LoRaWanDeviceClientService -@inject IDeviceLayoutService DeviceLayoutService - + - Device Details + Device Details - - - - - - - Model: @DeviceModel.Name - @(string.IsNullOrEmpty(Device.DeviceName) ? Device.DeviceID : Device.DeviceName) - - - -
- -
+ -
- - @if (isLoaded && (!IsLoRa || !(Device is LoRaDeviceDetails))) - { - Connect - } - -
- - - Delete device - - @saveButtonText - - Save - Duplicate - - - -
- - - - - - - - - Details - - - - - - - - - - - Status - - - - Enabled - The device can connect to the platform. - - - Disabled - The device cannot connect to the platform. - - - - - - - - - - - - Tags - - - @foreach (DeviceTagDto tag in TagList) - { - - @if (!Device.Tags.ContainsKey(tag.Name)) - { - Device.Tags.Add(tag.Name, ""); - } - - - } - - - - - - - - - - Labels - - - - - - - - @if (!IsLoRa && Properties.Any()) - { - - - - Properties - - - @foreach (var item in Properties.OrderBy(c => c.Order)) - { - switch (item.PropertyType) - { - case DevicePropertyType.Boolean: - - - - break; - case DevicePropertyType.Double: - - string.IsNullOrEmpty(c) || double.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.Float: - - string.IsNullOrEmpty(c) || float.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.Integer: - - string.IsNullOrEmpty(c) || int.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.Long: - - string.IsNullOrEmpty(c) || long.TryParse(c, out var result)) - Clearable="true" /> - - break; - case DevicePropertyType.String: - - - - break; - } - - } - - - - - - } - - - @if (IsLoRa && Device != null && Commands != null) - { - - - - } - - -
-
@code { - [CascadingParameter] - public Error Error { get; set; } = default!; - - private MudForm form = default!; - [Parameter] [SupplyParameterFromQuery] public bool IsLoRa @@ -235,206 +19,8 @@ set; } - private DeviceDetailsValidator standardValidator = new DeviceDetailsValidator(); - private LoRaDeviceDetailsValidator loraValidator = new LoRaDeviceDetailsValidator(); - [Parameter] public string DeviceID { get; set; } = default!; - private IDeviceDetails Device { get; set; } = new DeviceDetails(); - - private IDeviceModel DeviceModel { get; set; } = new DeviceModelDto(); - - private bool isLoaded = false; - - private bool isProcessing; - - private DeviceSaveAction deviceSaveAction = DeviceSaveAction.Save; - private string saveButtonText = "Save"; - private void Return() => NavManager.NavigateTo("devices"); - - private IEnumerable Commands { get; set; } = Array.Empty(); - - private IEnumerable TagList { get; set; } = Array.Empty(); - - private IEnumerable Properties = Array.Empty(); - - protected override async Task OnInitializedAsync() - { - try - { - isProcessing = true; - - if (IsLoRa) - { - Device = await LoRaWanDeviceClientService.GetDevice(DeviceID); - Commands = await LoRaWanDeviceModelsClientService.GetDeviceModelCommands(Device.ModelId); - DeviceModel = await LoRaWanDeviceModelsClientService.GetDeviceModel(Device.ModelId); - } - else - { - Device = await DeviceClientService.GetDevice(DeviceID); - Properties = await DeviceClientService.GetDeviceProperties(DeviceID); - DeviceModel = await DeviceModelsClientService.GetDeviceModel(Device.ModelId); - } - - TagList = await DeviceTagSettingsClientService.GetDeviceTags(); - - isLoaded = true; - isProcessing = false; - } - catch (ProblemDetailsException exception) - { - Error?.ProcessProblemDetails(exception); - } - } - - private void ProcessActionOnDevice() - { - switch (deviceSaveAction) - { - case DeviceSaveAction.Save: - Save(); - break; - case DeviceSaveAction.Duplicate: - DeviceLayoutService.DuplicateSharedDevice(Device); - DeviceLayoutService.DuplicateSharedDeviceModel(DeviceModel); - NavManager.NavigateTo("devices/new"); - break; - } - } - - /// - /// Sends a POST request to the DevicesController, to add the new device to the Azure IoT Hub - /// - public async void Save() - { - try - { - isProcessing = true; - - await form.Validate(); - - if (CheckTagsError() || CheckGeneralValidation() || CheckLoRaValidation()) - { - Snackbar.Add("One or more validation errors occurred", Severity.Error); - - isProcessing = false; - - return; - } - - if (IsLoRa) - { - await LoRaWanDeviceClientService.UpdateDevice((Device as LoRaDeviceDetails)!); - } - else - { - await DeviceClientService.UpdateDevice((Device as DeviceDetails)!); - - await DeviceClientService.SetDeviceProperties(DeviceID, Properties.ToList()); - } - - // Prompts a snack bar to inform the action was successful - Snackbar.Add($"Device {Device.DeviceName} has been successfully updated!\r\nPlease note that changes might take some minutes to be visible in the list...", Severity.Success, null); - - NavManager.NavigateTo("devices"); - } - catch (ProblemDetailsException exception) - { - Error?.ProcessProblemDetails(exception); - } - finally - { - isProcessing = false; - StateHasChanged(); - } - } - - public async Task ShowConnectionString() - { - var parameters = new DialogParameters(); - parameters.Add(nameof(ConnectionStringDialog.deviceId), this.DeviceID); - - _ = await DialogService.Show("Device Credentials", parameters).Result; - } - - private bool CheckTagsError() - { - bool tagValidationError = false; - - foreach (DeviceTagDto tag in TagList) - { - if (tag.Required && string.IsNullOrEmpty(Device.Tags[tag.Name])) - { - tagValidationError = true; - } - } - return tagValidationError; - } - - private bool CheckLoRaValidation() - { - if(!IsLoRa) - { - return false; - } - - if (this.Device is LoRaDeviceDetails loRaDeviceDetails) - { - return !this.loraValidator.Validate(loRaDeviceDetails).IsValid; - } - - return true; - } - - private bool CheckGeneralValidation() - { - if (!IsLoRa && this.Device is DeviceDetails deviceDetails) - { - return !this.standardValidator.Validate(deviceDetails).IsValid; - } - - return CheckLoRaValidation(); - } - - /// - /// Prompts a pop-up windows to confirm the device's deletion. - /// - /// Device to delete from the hub - /// - private async Task DeleteDevice() - { - isProcessing = true; - - var parameters = new DialogParameters - { - {"deviceID", Device.DeviceID}, - {"deviceName", Device.DeviceName}, - {"IsLoRaWan", IsLoRa} - }; - var result = await DialogService.Show("Confirm Deletion", parameters).Result; - - isProcessing = false; - - if (result.Canceled) - { - return; - } - - // Go back to the list of devices - NavManager.NavigateTo("devices"); - } - - private void SetSaveButtonText(DeviceSaveAction saveAction) - { - deviceSaveAction = saveAction; - saveButtonText = deviceSaveAction switch - { - DeviceSaveAction.Save => "Save", - DeviceSaveAction.Duplicate => "Duplicate", - _ => saveButtonText - }; - } } diff --git a/src/AzureIoTHub.Portal.Client/Validators/LoRaDeviceDetailsValidator.cs b/src/AzureIoTHub.Portal.Client/Validators/LoRaDeviceDetailsValidator.cs index d67d6ecd0..5c5361728 100644 --- a/src/AzureIoTHub.Portal.Client/Validators/LoRaDeviceDetailsValidator.cs +++ b/src/AzureIoTHub.Portal.Client/Validators/LoRaDeviceDetailsValidator.cs @@ -46,6 +46,12 @@ public LoRaDeviceDetailsValidator() .NotEmpty() .When(x => !x.UseOTAA) .WithMessage("DevAddr is required."); + + _ = RuleFor(x => x.DeviceID) + .NotEmpty() + .Length(1, 16) + .Matches("[A-F0-9]{16}") + .WithMessage("DeviceID is required. It should be a 16 bit hex string."); } public Func>> ValidateValue => async (model, propertyName) => diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Components/Devices/EditDeviceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Components/Devices/EditDeviceTests.cs new file mode 100644 index 000000000..4e6a1a16c --- /dev/null +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Components/Devices/EditDeviceTests.cs @@ -0,0 +1,1318 @@ +// 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.Tests.Unit.Client.Components.Devices +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using AutoFixture; + using AzureIoTHub.Portal.Client.Components.Devices; + using AzureIoTHub.Portal.Client.Enums; + using AzureIoTHub.Portal.Client.Exceptions; + using AzureIoTHub.Portal.Client.Models; + using AzureIoTHub.Portal.Client.Pages.Devices; + using AzureIoTHub.Portal.Client.Services; + using AzureIoTHub.Portal.Models.v10; + using AzureIoTHub.Portal.Models.v10.LoRaWAN; + using AzureIoTHub.Portal.Shared.Models; + using AzureIoTHub.Portal.Shared.Models.v1._0; + using AzureIoTHub.Portal.Shared.Models.v10.Filters; + using AzureIoTHub.Portal.Tests.Unit.UnitTests.Bases; + using AzureIoTHub.Portal.Tests.Unit.UnitTests.Mocks; + using Bunit; + using Bunit.TestDoubles; + using FluentAssertions; + using Microsoft.Extensions.DependencyInjection; + using Moq; + using MudBlazor; + using MudBlazor.Services; + using NUnit.Framework; + + [TestFixture] + public class EditDeviceTests : BlazorUnitTest + { + private Mock mockDialogService; + //private Mock mockSnackbarService; + private FakeNavigationManager mockNavigationManager; + private Mock mockDeviceModelsClientService; + private Mock mockLoRaWanDeviceModelsClientService; + private Mock mockDeviceTagSettingsClientService; + private Mock mockDeviceClientService; + private Mock mockLoRaWanDeviceClientService; + + public override void Setup() + { + base.Setup(); + + this.mockDialogService = MockRepository.Create(); + //this.mockSnackbarService = MockRepository.Create(); + this.mockDeviceModelsClientService = MockRepository.Create(); + this.mockLoRaWanDeviceModelsClientService = MockRepository.Create(); + this.mockDeviceTagSettingsClientService = MockRepository.Create(); + this.mockDeviceClientService = MockRepository.Create(); + this.mockLoRaWanDeviceClientService = MockRepository.Create(); + + _ = Services.AddSingleton(this.mockDialogService.Object); + //_ = Services.AddSingleton(this.mockSnackbarService.Object); + _ = Services.AddSingleton(this.mockDeviceModelsClientService.Object); + _ = Services.AddSingleton(this.mockLoRaWanDeviceModelsClientService.Object); + _ = Services.AddSingleton(this.mockDeviceTagSettingsClientService.Object); + _ = Services.AddSingleton(this.mockDeviceClientService.Object); + _ = Services.AddSingleton(this.mockLoRaWanDeviceClientService.Object); + + _ = Services.AddSingleton(); + + Services.Add(new ServiceDescriptor(typeof(IResizeObserver), new MockResizeObserver())); + + this.mockNavigationManager = Services.GetRequiredService(); + } + + [Test] + public async Task ClickOnSaveShouldPostDeviceDetailsAsync() + { + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + .Returns(Task.CompletedTask); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceClientService + .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) + .Returns(Task.CompletedTask); + + var cut = RenderComponent(); + var saveButton = cut.WaitForElement("#SaveButton"); + + // Act + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices")); + } + + [Test] + public async Task DeviceShouldNotBeCreatedWhenModelIsNotValid() + { + var mockDeviceModel = new DeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + Description = Guid.NewGuid().ToString(), + SupportLoRaFeatures = false, + Name = Guid.NewGuid().ToString() + }; + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List()); + + var cut = RenderComponent(); + var saveButton = cut.WaitForElement("#SaveButton"); + + // Act + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(string.Empty); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(string.Empty); + await cut.Instance.ChangeModel(mockDeviceModel); + + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void OnInitializedAsyncShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingDeviceTags() + { + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); + + // Act + var cut = RenderComponent(); + + // Assert + cut.WaitForAssertion(() => cut.Markup.Should().NotBeNullOrEmpty()); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public async Task SaveShouldProcessProblemDetailsExceptionWhenIssueOccursOnCreatingDevice() + { + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List()); + + // Act + var cut = RenderComponent(); + var saveButton = cut.WaitForElement("#SaveButton"); + + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("devices")); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public async Task ChangeModelShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingModelProperties() + { + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); + + var cut = RenderComponent(); + + // Act + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public async Task ClickOnSaveShouldCreateDeviceInEditDevice() + { + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + .Returns(Task.CompletedTask); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceClientService + .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) + .Returns(Task.CompletedTask); + + var popoverProvider = RenderComponent(); + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Create)); + var saveButton = cut.WaitForElement("#SaveButton"); + + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + var mudButtonGroup = cut.FindComponent(); + + mudButtonGroup.Find(".mud-menu button").Click(); + + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(3)); + + var items = popoverProvider.FindAll("div.mud-list-item"); + + // Click on Save and Duplicate + items[0].Click(); + + // Act + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices")); + } + + [Test] + public async Task ClickOnSaveShouldUpdateDeviceInEditDevice() + { + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceClientService.Setup(service => service.GetDevice(It.IsAny())) + .ReturnsAsync(new DeviceDetails()); + + _ = this.mockDeviceClientService.Setup(service => service.GetDeviceProperties(It.IsAny())) + .ReturnsAsync(new List()); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModel(It.IsAny())) + .ReturnsAsync(new DeviceModelDto()); + + _ = this.mockDeviceClientService.Setup(service => service.UpdateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + .Returns(Task.CompletedTask); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceClientService + .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) + .Returns(Task.CompletedTask); + + var popoverProvider = RenderComponent(); + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, expectedDeviceDetails.DeviceID)); + var saveButton = cut.WaitForElement("#saveButton"); + + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + var mudButtonGroup = cut.FindComponent(); + + mudButtonGroup.Find(".mud-menu button").Click(); + + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(2)); + + var items = popoverProvider.FindAll("div.mud-list-item"); + + // Click on Save + items[0].Click(); + + // Act + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices")); + } + + [Test] + public async Task ClickOnSaveAndAddNewShouldCreateDeviceAndResetEditDevice() + { + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + .Returns(Task.CompletedTask); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceClientService + .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) + .Returns(Task.CompletedTask); + + var popoverProvider = RenderComponent(); + var cut = RenderComponent(); + var saveButton = cut.WaitForElement("#SaveButton"); + + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + var mudButtonGroup = cut.FindComponent(); + + mudButtonGroup.Find(".mud-menu button").Click(); + + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(3)); + + var items = popoverProvider.FindAll("div.mud-list-item"); + + // Click on Save and New + items[1].Click(); + + // Act + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceName)}").TextContent.Should().BeEmpty()); + cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceID)}").TextContent.Should().BeEmpty()); + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("/devices")); + } + + [Test] + public async Task ClickOnSaveAndDuplicateShouldCreateDeviceAndDuplicateDeviceDetailsInEditDevice() + { + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + .Returns(Task.CompletedTask); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceClientService + .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) + .Returns(Task.CompletedTask); + + var popoverProvider = RenderComponent(); + var cut = RenderComponent(); + var saveButton = cut.WaitForElement("#SaveButton"); + + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + var mudButtonGroup = cut.FindComponent(); + + mudButtonGroup.Find(".mud-menu button").Click(); + + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(3)); + + var items = popoverProvider.FindAll("div.mud-list-item"); + + // Click on Save and Duplicate + items[2].Click(); + + // Act + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceName)}").TextContent.Should().BeEmpty()); + cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceID)}").TextContent.Should().BeEmpty()); + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("/devices")); + } + + [Test] + public void SearchDeviceModelsInputExistingDeviceModelNameDeviceModelReturned() + { + // Arrange + var deviceModels = Fixture.CreateMany(2).ToList(); + var expectedDeviceModel = deviceModels.First(); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModels(It.Is(x => expectedDeviceModel.Name.Equals(x.SearchText, StringComparison.Ordinal)))) + .ReturnsAsync(new PaginationResult + { + Items = deviceModels.Where(x => expectedDeviceModel.Name.Equals(x.Name, StringComparison.Ordinal)) + }); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List()); + + var popoverProvider = RenderComponent(); + + var cut = RenderComponent(); + + var autocompleteComponent = cut.FindComponent>(); + + // Act + autocompleteComponent.Find("input").Input(expectedDeviceModel.Name); + + // Assert + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-popover-open").Count.Should().Be(1)); + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(1)); + + var items = popoverProvider.FindComponents().ToArray(); + _ = items.Length.Should().Be(1); + _ = items.First().Markup.Should().Contain(expectedDeviceModel.Name); + items.First().Find("div.mud-list-item").Click(); + + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void DisplayDeviceToDuplicateSelectorRenderCorrectly() + { + // Arrange + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List()); + + var cut = RenderComponent(); + + // Act + cut.WaitForElement("#duplicate-device-switch").Change(true); + + // Assert + cut.WaitForAssertion(() => cut.Find("label.mud-input-label").TextContent.Should().Be("Search a device to duplicate")); + } + + [Test] + public void DisplayValidationErrorMessageRenderCorrectly() + { + // Arrange + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List()); + + var cut = RenderComponent(); + + // Act + cut.WaitForElement("#SaveButton").Click(); + + // Assert + cut.WaitForAssertion(() => cut.Find("p.validation-error-message").TextContent.Should().Be("The Model is required.")); + } + + [Test] + public async Task DisplayPropertiesRenderCorrectly() + { + // Arrange + var mockDeviceModel = new DeviceModelDto + { + 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.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); + + _ = this.mockDeviceModelsClientService + .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) + .ReturnsAsync(new List + { + new() + { + Name = Guid.NewGuid().ToString(), + DisplayName = Guid.NewGuid().ToString(), + Order = 1, + PropertyType = Models.DevicePropertyType.Boolean, + IsWritable = true + }, + new() + { + Name = Guid.NewGuid().ToString(), + DisplayName = Guid.NewGuid().ToString(), + Order = 1, + PropertyType = Models.DevicePropertyType.Float, + IsWritable = true + }, + new() + { + Name = Guid.NewGuid().ToString(), + DisplayName = Guid.NewGuid().ToString(), + Order = 1, + PropertyType = Models.DevicePropertyType.Double, + IsWritable = true + }, + new() + { + Name = Guid.NewGuid().ToString(), + DisplayName = Guid.NewGuid().ToString(), + Order = 1, + PropertyType = Models.DevicePropertyType.Integer, + IsWritable = true + }, + new() + { + Name = Guid.NewGuid().ToString(), + DisplayName = Guid.NewGuid().ToString(), + Order = 1, + PropertyType = Models.DevicePropertyType.Long, + IsWritable = true + }, + new() + { + Name = Guid.NewGuid().ToString(), + DisplayName = Guid.NewGuid().ToString(), + Order = 1, + PropertyType = Models.DevicePropertyType.String, + IsWritable = true + } + }); + + var cut = RenderComponent(); + + // Act + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); + cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); + await cut.Instance.ChangeModel(mockDeviceModel); + + // Assert + cut.WaitForAssertion(() => cut.FindAll("#device-properties > .mud-grid-item").Count.Should().Be(6)); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public async Task DisplayCreateLoraDeviceComponentRenderCorrectly() + { + // Arrange + var mockDeviceModel = new DeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + Description = Guid.NewGuid().ToString(), + SupportLoRaFeatures = true, + Name = Guid.NewGuid().ToString() + }; + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List()); + + _ = this.mockLoRaWanDeviceModelsClientService + .Setup(service => service.GetDeviceModel(It.IsAny())) + .ReturnsAsync(new LoRaDeviceModelDto()); + + _ = this.mockLoRaWanDeviceClientService.Setup(service => service.GetGatewayIdList()) + .ReturnsAsync(new LoRaGatewayIDList()); + + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Create)); + + // Act + await cut.Instance.ChangeModel(mockDeviceModel); + cut.WaitForElement("div.mud-tooltip-root:nth-child(2) > div.mud-tab").Click(); + + // Assert + cut.WaitForAssertion(() => cut.Find("div.mud-tooltip-root:nth-child(2) > div.mud-tab").TextContent.Should().Be("LoRaWAN")); + } + + [Test] + public void ShouldLoadDeviceDetails() + { + // Arrange + var deviceId = Guid.NewGuid().ToString(); + var modelId = Guid.NewGuid().ToString(); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(deviceId)) + .ReturnsAsync(new DeviceDetails() { ModelId = modelId }); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(deviceId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId)) + .ReturnsAsync(new DeviceModelDto()); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List()); + + // Act + var page = RenderComponent(parameters => parameters.Add(p => p.DeviceID, deviceId)); + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, deviceId)); + + // Assert + _ = page.WaitForElement("#returnButton"); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void ShouldLoadLoRaDeviceDetails() + { + + // Arrange + var deviceId = Guid.NewGuid().ToString(); + var modelId = Guid.NewGuid().ToString(); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(deviceId)) + .ReturnsAsync(new DeviceDetails()); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(deviceId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(null)) + .ReturnsAsync(new DeviceModelDto()); + + _ = this.mockLoRaWanDeviceClientService + .Setup(service => service.GetDevice(deviceId)) + .ReturnsAsync(new LoRaDeviceDetails() { ModelId = modelId }); + + _ = this.mockLoRaWanDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId)) + .ReturnsAsync(new LoRaDeviceModelDto()); + + _ = this.mockLoRaWanDeviceModelsClientService.Setup(service => service.GetDeviceModelCommands(modelId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List()); + + // Act + var page = RenderComponent(parameters => parameters.Add(p => p.DeviceID, deviceId)); + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, deviceId).Add(p => p.IsLoRa, true)); + + // Assert + _ = page.WaitForElement("#returnButton"); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void ReturnButtonMustNavigateToPreviousPage() + { + + // Arrange + var deviceId = Guid.NewGuid().ToString(); + var modelId = Guid.NewGuid().ToString(); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(deviceId)) + .ReturnsAsync(new DeviceDetails() { ModelId = modelId }); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(deviceId)) + .ReturnsAsync(new List()); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId)) + .ReturnsAsync(new DeviceModelDto()); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List()); + + + // Act + var page = RenderComponent(parameters => parameters.Add(p => p.DeviceID, deviceId)); + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, deviceId)); + var returnButton = page.WaitForElement("#returnButton"); + + returnButton.Click(); + + // Assert + page.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices")); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + //[Test] + //public void ClickOnSaveShouldPutDeviceDetails() + //{ + // var mockDeviceModel = new DeviceModelDto + // { + // ModelId = Guid.NewGuid().ToString(), + // Description = Guid.NewGuid().ToString(), + // SupportLoRaFeatures = false, + // Name = Guid.NewGuid().ToString() + // }; + + // var mockTag = new DeviceTagDto + // { + // Label = Guid.NewGuid().ToString(), + // Name = Guid.NewGuid().ToString(), + // Required = false, + // Searchable = false + // }; + + // var mockDeviceDetails = new DeviceDetails + // { + // DeviceName = Guid.NewGuid().ToString(), + // ModelId = mockDeviceModel.ModelId, + // DeviceID = Guid.NewGuid().ToString(), + // Tags = new Dictionary() + // { + // {mockTag.Name,Guid.NewGuid().ToString()} + // } + // }; + + // _ = this.mockDeviceClientService.Setup(service => service.UpdateDevice(It.Is(details => mockDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + // .Returns(Task.CompletedTask); + + // _ = this.mockDeviceClientService + // .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) + // .ReturnsAsync(mockDeviceDetails); + + // _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) + // .ReturnsAsync(mockDeviceModel); + + // _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + // .ReturnsAsync(new List + // { + // mockTag + // }); + + // _ = this.mockDeviceClientService + // .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) + // .ReturnsAsync(new List()); + + // _ = this.mockDeviceClientService + // .Setup(service => service.SetDeviceProperties(mockDeviceDetails.DeviceID, It.IsAny>())) + // .Returns(Task.CompletedTask); + + // _ = this.mockSnackbarService.Setup(c => c.Add(It.IsAny(), Severity.Success, It.IsAny>(), It.IsAny())).Returns((Snackbar)null); + + // // Act + // var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); + // cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceModelDto.Name)}").InnerHtml.Should().NotBeEmpty()); + + // var saveButton = cut.WaitForElement("#saveButton"); + // saveButton.Click(); + + // // Assert + // cut.WaitForState(() => this.mockNavigationManager.Uri.EndsWith("devices", StringComparison.OrdinalIgnoreCase), 3.Seconds()); + // cut.WaitForAssertion(() => MockRepository.VerifyAll()); + //} + + [Test] + public void SaveShouldProcessProblemDetailsExceptionWhenIssueOccursOnUpdatingDevice() + { + var mockDeviceModel = new DeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + Description = Guid.NewGuid().ToString(), + SupportLoRaFeatures = false, + Name = Guid.NewGuid().ToString() + }; + + var mockTag = new DeviceTagDto + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + }; + + var mockDeviceDetails = new DeviceDetails + { + DeviceName = Guid.NewGuid().ToString(), + ModelId = mockDeviceModel.ModelId, + DeviceID = Guid.NewGuid().ToString(), + Tags = new Dictionary() + { + {mockTag.Name,Guid.NewGuid().ToString()} + } + }; + + _ = this.mockDeviceClientService.Setup(service => service.UpdateDevice(It.Is(details => mockDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) + .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) + .ReturnsAsync(mockDeviceDetails); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) + .ReturnsAsync(mockDeviceModel); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + mockTag + }); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) + .ReturnsAsync(new List()); + + // Act + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); + + + var saveButton = cut.WaitForElement("#saveButton"); + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("devices")); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + //[Test] + //public void ClickOnSaveShouldDisplaySnackbarIfValidationError() + //{ + // var mockDeviceModel = new DeviceModelDto + // { + // ModelId = Guid.NewGuid().ToString(), + // Description = Guid.NewGuid().ToString(), + // SupportLoRaFeatures = false, + // Name = Guid.NewGuid().ToString() + // }; + + // var mockTag = new DeviceTagDto + // { + // Label = Guid.NewGuid().ToString(), + // Name = Guid.NewGuid().ToString(), + // Required = false, + // Searchable = false + // }; + + // var mockDeviceDetails = new DeviceDetails + // { + // DeviceName = Guid.NewGuid().ToString(), + // ModelId = mockDeviceModel.ModelId, + // DeviceID = Guid.NewGuid().ToString(), + // Tags = new Dictionary() + // { + // {mockTag.Name,Guid.NewGuid().ToString()} + // } + // }; + + // _ = this.mockDeviceClientService + // .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) + // .ReturnsAsync(mockDeviceDetails); + + // _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) + // .ReturnsAsync(mockDeviceModel); + + // _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + // .ReturnsAsync(new List + // { + // mockTag + // }); + + // _ = this.mockDeviceClientService + // .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) + // .ReturnsAsync(new List()); + + // _ = this.mockSnackbarService.Setup(c => c.Add(It.IsAny(), Severity.Error, It.IsAny>(), It.IsAny())).Returns((Snackbar)null); + + // // Act + // var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); + + // cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(""); + // var saveButton = cut.WaitForElement("#saveButton"); + // saveButton.Click(); + + // // Assert + // cut.WaitForAssertion(() => MockRepository.VerifyAll()); + //} + + [Test] + public void ClickOnConnectShouldDisplayDeviceCredentials() + { + var mockDeviceModel = new DeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + Description = Guid.NewGuid().ToString(), + SupportLoRaFeatures = false, + Name = Guid.NewGuid().ToString() + }; + + var mockTag = new DeviceTagDto + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + }; + + var mockDeviceDetails = new DeviceDetails + { + DeviceName = Guid.NewGuid().ToString(), + ModelId = mockDeviceModel.ModelId, + DeviceID = Guid.NewGuid().ToString(), + Tags = new Dictionary() + { + {mockTag.Name,Guid.NewGuid().ToString()} + } + }; + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) + .ReturnsAsync(mockDeviceDetails); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) + .ReturnsAsync(mockDeviceModel); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + mockTag + }); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) + .ReturnsAsync(new List()); + + var mockDialogReference = new DialogReference(Guid.NewGuid(), this.mockDialogService.Object); + _ = this.mockDialogService.Setup(c => c.Show(It.IsAny(), It.IsAny())) + .Returns(mockDialogReference); + + // Act + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); + + var connectButton = cut.WaitForElement("#connectButton"); + connectButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void ClickOnDeleteShouldDisplayConfirmationDialogAndReturnIfAborted() + { + var mockDeviceModel = new DeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + Description = Guid.NewGuid().ToString(), + SupportLoRaFeatures = false, + Name = Guid.NewGuid().ToString() + }; + + var mockTag = new DeviceTagDto + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + }; + + var mockDeviceDetails = new DeviceDetails + { + DeviceName = Guid.NewGuid().ToString(), + ModelId = mockDeviceModel.ModelId, + DeviceID = Guid.NewGuid().ToString(), + Tags = new Dictionary() + { + {mockTag.Name,Guid.NewGuid().ToString()} + } + }; + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) + .ReturnsAsync(mockDeviceDetails); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) + .ReturnsAsync(mockDeviceModel); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + mockTag + }); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) + .ReturnsAsync(new List()); + + var mockDialogReference = MockRepository.Create(); + _ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Cancel()); + _ = this.mockDialogService.Setup(c => c.Show(It.IsAny(), It.IsAny())) + .Returns(mockDialogReference.Object); + + // Act + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); + + var deleteButton = cut.WaitForElement("#deleteButton"); + deleteButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void ClickOnDeleteShouldDisplayConfirmationDialogAndRedirectIfConfirmed() + { + var mockDeviceModel = new DeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + Description = Guid.NewGuid().ToString(), + SupportLoRaFeatures = false, + Name = Guid.NewGuid().ToString() + }; + + var mockTag = new DeviceTagDto + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + }; + + var mockDeviceDetails = new DeviceDetails + { + DeviceName = Guid.NewGuid().ToString(), + ModelId = mockDeviceModel.ModelId, + DeviceID = Guid.NewGuid().ToString(), + Tags = new Dictionary() + { + {mockTag.Name,Guid.NewGuid().ToString()} + } + }; + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) + .ReturnsAsync(mockDeviceDetails); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) + .ReturnsAsync(mockDeviceModel); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + mockTag + }); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) + .ReturnsAsync(new List()); + + var mockDialogReference = MockRepository.Create(); + _ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Ok("Ok")); + _ = this.mockDialogService.Setup(c => c.Show(It.IsAny(), It.IsAny())) + .Returns(mockDialogReference.Object); + + // Act + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); + + var deleteButton = cut.WaitForElement("#deleteButton"); + deleteButton.Click(); + + // Assert + cut.WaitForState(() => this.mockNavigationManager.Uri.EndsWith("/devices", StringComparison.OrdinalIgnoreCase)); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + + [Test] + public void ClickOnDuplicateShouldDuplicateDeviceDetailAndRedirectToCreateDevicePage() + { + var mockDeviceModel = new DeviceModelDto + { + ModelId = Guid.NewGuid().ToString(), + Description = Guid.NewGuid().ToString(), + SupportLoRaFeatures = false, + Name = Guid.NewGuid().ToString() + }; + + var mockTag = new DeviceTagDto + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + }; + + var mockDeviceDetails = new DeviceDetails + { + DeviceName = Guid.NewGuid().ToString(), + ModelId = mockDeviceModel.ModelId, + DeviceID = Guid.NewGuid().ToString(), + Tags = new Dictionary() + { + {mockTag.Name,Guid.NewGuid().ToString()} + } + }; + + _ = this.mockDeviceClientService + .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) + .ReturnsAsync(mockDeviceDetails); + + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) + .ReturnsAsync(mockDeviceModel); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) + .ReturnsAsync(new List + { + mockTag + }); + + _ = this.mockDeviceClientService + .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) + .ReturnsAsync(new List()); + + var popoverProvider = RenderComponent(); + var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); + cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceModelDto.Name)}").InnerHtml.Should().NotBeEmpty()); + + var saveButton = cut.WaitForElement("#saveButton"); + + var mudButtonGroup = cut.FindComponent(); + + mudButtonGroup.Find(".mud-menu button").Click(); + popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(2)); + + var items = popoverProvider.FindAll("div.mud-list-item"); + + // Click on Duplicate + items[1].Click(); + + // Act + saveButton.Click(); + + // Assert + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices/new")); + } + } +} diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/CreateDevicePageTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/CreateDevicePageTests.cs index 49e1fdbdc..cd273b1c1 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/CreateDevicePageTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/CreateDevicePageTests.cs @@ -5,364 +5,52 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices { using System; using System.Collections.Generic; - using System.Threading.Tasks; - using AzureIoTHub.Portal.Client.Exceptions; - using AzureIoTHub.Portal.Client.Models; using AzureIoTHub.Portal.Client.Pages.Devices; using AzureIoTHub.Portal.Client.Services; using Models.v10; using UnitTests.Bases; using Bunit; - using Bunit.TestDoubles; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Moq; - using MudBlazor; using MudBlazor.Services; using NUnit.Framework; using UnitTests.Mocks; - using AutoFixture; - using AzureIoTHub.Portal.Shared.Models.v10.Filters; - using System.Linq; - using AzureIoTHub.Portal.Shared.Models; [TestFixture] public class CreateDevicePageTests : BlazorUnitTest { - private Mock mockDialogService; - private FakeNavigationManager mockNavigationManager; - - private Mock mockDeviceModelsClientService; - private Mock mockLoRaWanDeviceModelsClientService; private Mock mockDeviceTagSettingsClientService; - private Mock mockDeviceClientService; private Mock mockLoRaWanDeviceClientService; + private Mock mockDeviceClientService; + private Mock mockLoRaWanDeviceModelsClientService; + private Mock mockDeviceModelsClientService; public override void Setup() { base.Setup(); - this.mockDialogService = MockRepository.Create(); - this.mockDeviceModelsClientService = MockRepository.Create(); - this.mockLoRaWanDeviceModelsClientService = MockRepository.Create(); this.mockDeviceTagSettingsClientService = MockRepository.Create(); - this.mockDeviceClientService = MockRepository.Create(); this.mockLoRaWanDeviceClientService = MockRepository.Create(); + this.mockDeviceClientService = MockRepository.Create(); + this.mockLoRaWanDeviceModelsClientService = MockRepository.Create(); + this.mockDeviceModelsClientService = MockRepository.Create(); - _ = Services.AddSingleton(this.mockDialogService.Object); - _ = Services.AddSingleton(this.mockDeviceModelsClientService.Object); - _ = Services.AddSingleton(this.mockLoRaWanDeviceModelsClientService.Object); _ = Services.AddSingleton(this.mockDeviceTagSettingsClientService.Object); - _ = Services.AddSingleton(this.mockDeviceClientService.Object); _ = Services.AddSingleton(this.mockLoRaWanDeviceClientService.Object); + _ = Services.AddSingleton(this.mockDeviceClientService.Object); + _ = Services.AddSingleton(this.mockLoRaWanDeviceModelsClientService.Object); + _ = Services.AddSingleton(this.mockDeviceModelsClientService.Object); _ = Services.AddSingleton(); Services.Add(new ServiceDescriptor(typeof(IResizeObserver), new MockResizeObserver())); - - this.mockNavigationManager = Services.GetRequiredService(); - } - - [Test] - public async Task ClickOnSaveShouldPostDeviceDetailsAsync() - { - var mockDeviceModel = new DeviceModelDto - { - 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) - .Returns(Task.CompletedTask); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - new() - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - } - }); - - _ = this.mockDeviceModelsClientService - .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) - .ReturnsAsync(new List()); - - _ = this.mockDeviceClientService - .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) - .Returns(Task.CompletedTask); - - var cut = RenderComponent(); - var saveButton = cut.WaitForElement("#SaveButton"); - - // Act - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); - await cut.Instance.ChangeModel(mockDeviceModel); - - saveButton.Click(); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices")); } [Test] - public async Task DeviceShouldNotBeCreatedWhenModelIsNotValid() + public void CreateDevicePageShouldRenderCorrectly() { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - new() - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - } - }); - - _ = this.mockDeviceModelsClientService - .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) - .ReturnsAsync(new List()); - - var cut = RenderComponent(); - var saveButton = cut.WaitForElement("#SaveButton"); - - // Act - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(string.Empty); - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(string.Empty); - await cut.Instance.ChangeModel(mockDeviceModel); - - saveButton.Click(); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void OnInitializedAsyncShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingDeviceTags() - { - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); - - // Act - var cut = RenderComponent(); - - // Assert - cut.WaitForAssertion(() => cut.Markup.Should().NotBeNullOrEmpty()); - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public async Task SaveShouldProcessProblemDetailsExceptionWhenIssueOccursOnCreatingDevice() - { - var mockDeviceModel = new DeviceModelDto - { - 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) - .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - new() - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - } - }); - - _ = this.mockDeviceModelsClientService - .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) - .ReturnsAsync(new List()); - - // Act - var cut = RenderComponent(); - var saveButton = cut.WaitForElement("#SaveButton"); - - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); - await cut.Instance.ChangeModel(mockDeviceModel); - - saveButton.Click(); - - // Assert - cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("devices")); - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public async Task ChangeModelShouldProcessProblemDetailsExceptionWhenIssueOccursOnGettingModelProperties() - { - var mockDeviceModel = new DeviceModelDto - { - 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.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - new() - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - } - }); - - _ = this.mockDeviceModelsClientService - .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) - .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); - - var cut = RenderComponent(); - - // Act - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); - await cut.Instance.ChangeModel(mockDeviceModel); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public async Task ClickOnSaveAndAddNewShouldCreateDeviceAndResetCreateDevicePage() - { - var mockDeviceModel = new DeviceModelDto - { - 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) - .Returns(Task.CompletedTask); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - new() - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - } - }); - - _ = this.mockDeviceModelsClientService - .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) - .ReturnsAsync(new List()); - - _ = this.mockDeviceClientService - .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) - .Returns(Task.CompletedTask); - - var popoverProvider = RenderComponent(); - var cut = RenderComponent(); - var saveButton = cut.WaitForElement("#SaveButton"); - - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); - await cut.Instance.ChangeModel(mockDeviceModel); - - var mudButtonGroup = cut.FindComponent(); - - mudButtonGroup.Find(".mud-menu button").Click(); - - popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(3)); - - var items = popoverProvider.FindAll("div.mud-list-item"); - - // Click on Save and New - items[1].Click(); - - // Act - saveButton.Click(); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceName)}").TextContent.Should().BeEmpty()); - cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceID)}").TextContent.Should().BeEmpty()); - cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("/devices")); - } - - [Test] - public async Task ClickOnSaveAndDuplicateShouldCreateDeviceAndDuplicateDeviceDetailsInCreateDevicePage() - { - var mockDeviceModel = new DeviceModelDto - { - 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.mockDeviceClientService.Setup(service => service.CreateDevice(It.Is(details => expectedDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) - .Returns(Task.CompletedTask); - + // Arrange _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) .ReturnsAsync(new List { @@ -375,78 +63,11 @@ public async Task ClickOnSaveAndDuplicateShouldCreateDeviceAndDuplicateDeviceDet } }); - _ = this.mockDeviceModelsClientService - .Setup(service => service.GetDeviceModelModelProperties(mockDeviceModel.ModelId)) - .ReturnsAsync(new List()); - - _ = this.mockDeviceClientService - .Setup(service => service.SetDeviceProperties(expectedDeviceDetails.DeviceID, It.IsAny>())) - .Returns(Task.CompletedTask); - - var popoverProvider = RenderComponent(); - var cut = RenderComponent(); - var saveButton = cut.WaitForElement("#SaveButton"); - - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(expectedDeviceDetails.DeviceName); - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceID)}").Change(expectedDeviceDetails.DeviceID); - await cut.Instance.ChangeModel(mockDeviceModel); - - var mudButtonGroup = cut.FindComponent(); - - mudButtonGroup.Find(".mud-menu button").Click(); - - popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(3)); - - var items = popoverProvider.FindAll("div.mud-list-item"); - - // Click on Save and Duplicate - items[2].Click(); - // Act - saveButton.Click(); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceName)}").TextContent.Should().BeEmpty()); - cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceDetails.DeviceID)}").TextContent.Should().BeEmpty()); - cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("/devices")); - } - - [Test] - public void SearchDeviceModels_InputExisingDeviceModelName_DeviceModelReturned() - { - // Arrange - var deviceModels = Fixture.CreateMany(2).ToList(); - var expectedDeviceModel = deviceModels.First(); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModels(It.Is(x => expectedDeviceModel.Name.Equals(x.SearchText)))) - .ReturnsAsync(new PaginationResult - { - Items = deviceModels.Where(x => expectedDeviceModel.Name.Equals(x.Name, StringComparison.Ordinal)) - }); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List()); - - var popoverProvider = RenderComponent(); - var cut = RenderComponent(); - var autocompleteComponent = cut.FindComponent>(); - - // Act - autocompleteComponent.Find("input").Input(expectedDeviceModel.Name); - // Assert - popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-popover-open").Count.Should().Be(1)); - popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(1)); - - var items = popoverProvider.FindComponents().ToArray(); - _ = items.Length.Should().Be(1); - _ = items.First().Markup.Should().Contain(expectedDeviceModel.Name); - items.First().Find("div.mud-list-item").Click(); - - cut.WaitForAssertion(() => MockRepository.VerifyAll()); + cut.WaitForAssertion(() => cut.Find("#SaveButton").TextContent.Should().Be("Save")); } } } diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/DeviceDetailPageTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/DeviceDetailPageTests.cs index 59687755d..787ea4282 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/DeviceDetailPageTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/Devices/DeviceDetailPageTests.cs @@ -5,9 +5,6 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices { using System; using System.Collections.Generic; - using System.Threading.Tasks; - using AzureIoTHub.Portal.Client.Exceptions; - using AzureIoTHub.Portal.Client.Models; using AzureIoTHub.Portal.Client.Pages.Devices; using AzureIoTHub.Portal.Client.Services; using Models.v10; @@ -15,15 +12,12 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.Devices using Bunit; using Bunit.TestDoubles; using FluentAssertions; - using FluentAssertions.Extensions; using Microsoft.Extensions.DependencyInjection; using Moq; using MudBlazor; using MudBlazor.Services; using NUnit.Framework; using UnitTests.Mocks; - using Models.v10.LoRaWAN; - using AzureIoTHub.Portal.Client.Pages.DeviceModels; [TestFixture] public class DeviceDetailPageTests : BlazorUnitTest @@ -68,541 +62,39 @@ public override void Setup() } [Test] - public void ShouldLoadDeviceDetails() + public void DeviceDetailPageShouldRenderCorrectly() { // Arrange var deviceId = Guid.NewGuid().ToString(); - var modelId = Guid.NewGuid().ToString(); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(deviceId)) - .ReturnsAsync(new DeviceDetails() { ModelId = modelId }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(deviceId)) - .ReturnsAsync(new List()); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId)) - .ReturnsAsync(new DeviceModelDto()); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List()); - - // Act - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", deviceId)); - - // Assert - _ = cut.WaitForElement("#returnButton"); - } - - [Test] - public void ShouldLoadLoRaDeviceDetails() - { - - // Arrange - var deviceId = Guid.NewGuid().ToString(); - var modelId = Guid.NewGuid().ToString(); - - _ = this.mockLoRaWanDeviceClientService - .Setup(service => service.GetDevice(deviceId)) - .ReturnsAsync(new LoRaDeviceDetails() { ModelId = modelId }); - - _ = this.mockLoRaWanDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId)) - .ReturnsAsync(new LoRaDeviceModelDto()); - - _ = this.mockLoRaWanDeviceModelsClientService.Setup(service => service.GetDeviceModelCommands(modelId)) - .ReturnsAsync(new List()); _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List()); - - // Act - var cut = RenderComponent( - ComponentParameter.CreateParameter("DeviceID", deviceId), - ComponentParameter.CreateParameter(nameof(DeviceModelDetailPage.IsLoRa), true)); - - // Assert - _ = cut.WaitForElement("#returnButton"); - } - - [Test] - public void ReturnButtonMustNavigateToPreviousPage() - { - - // Arrange - var deviceId = Guid.NewGuid().ToString(); - var modelId = Guid.NewGuid().ToString(); + .ReturnsAsync(new List + { + new() + { + Label = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + Required = false, + Searchable = false + } + }); _ = this.mockDeviceClientService .Setup(service => service.GetDevice(deviceId)) - .ReturnsAsync(new DeviceDetails() { ModelId = modelId }); + .ReturnsAsync(new DeviceDetails()); _ = this.mockDeviceClientService .Setup(service => service.GetDeviceProperties(deviceId)) .ReturnsAsync(new List()); - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(modelId)) + _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(null)) .ReturnsAsync(new DeviceModelDto()); - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List()); - - // Act var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", deviceId)); - var returnButton = cut.WaitForElement("#returnButton"); - - returnButton.Click(); - - // Assert - cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices")); - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void ClickOnSaveShouldPutDeviceDetails() - { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - var mockTag = new DeviceTagDto - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - }; - - var mockDeviceDetails = new DeviceDetails - { - DeviceName = Guid.NewGuid().ToString(), - ModelId = mockDeviceModel.ModelId, - DeviceID = Guid.NewGuid().ToString(), - Tags = new Dictionary() - { - {mockTag.Name,Guid.NewGuid().ToString()} - } - }; - - _ = this.mockDeviceClientService.Setup(service => service.UpdateDevice(It.Is(details => mockDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) - .Returns(Task.CompletedTask); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) - .ReturnsAsync(mockDeviceDetails); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) - .ReturnsAsync(mockDeviceModel); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - mockTag - }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) - .ReturnsAsync(new List()); - - _ = this.mockDeviceClientService - .Setup(service => service.SetDeviceProperties(mockDeviceDetails.DeviceID, It.IsAny>())) - .Returns(Task.CompletedTask); - - _ = this.mockSnackbarService.Setup(c => c.Add(It.IsAny(), Severity.Success, It.IsAny>(), It.IsAny())).Returns((Snackbar)null); - - // Act - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID)); - cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceModelDto.Name)}").InnerHtml.Should().NotBeEmpty()); - - var saveButton = cut.WaitForElement("#saveButton"); - saveButton.Click(); - - // Assert - cut.WaitForState(() => this.mockNavigationManager.Uri.EndsWith("devices", StringComparison.OrdinalIgnoreCase), 3.Seconds()); - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void SaveShouldProcessProblemDetailsExceptionWhenIssueOccursOnUpdatingDevice() - { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - var mockTag = new DeviceTagDto - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - }; - - var mockDeviceDetails = new DeviceDetails - { - DeviceName = Guid.NewGuid().ToString(), - ModelId = mockDeviceModel.ModelId, - DeviceID = Guid.NewGuid().ToString(), - Tags = new Dictionary() - { - {mockTag.Name,Guid.NewGuid().ToString()} - } - }; - - _ = this.mockDeviceClientService.Setup(service => service.UpdateDevice(It.Is(details => mockDeviceDetails.DeviceID.Equals(details.DeviceID, StringComparison.Ordinal)))) - .ThrowsAsync(new ProblemDetailsException(new ProblemDetailsWithExceptionDetails())); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) - .ReturnsAsync(mockDeviceDetails); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) - .ReturnsAsync(mockDeviceModel); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - mockTag - }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) - .ReturnsAsync(new List()); - - // Act - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID)); - - var saveButton = cut.WaitForElement("#saveButton"); - saveButton.Click(); - - // Assert - cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().NotEndWith("devices")); - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void ClickOnSaveShouldDisplaySnackbarIfValidationError() - { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - var mockTag = new DeviceTagDto - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - }; - - var mockDeviceDetails = new DeviceDetails - { - DeviceName = Guid.NewGuid().ToString(), - ModelId = mockDeviceModel.ModelId, - DeviceID = Guid.NewGuid().ToString(), - Tags = new Dictionary() - { - {mockTag.Name,Guid.NewGuid().ToString()} - } - }; - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) - .ReturnsAsync(mockDeviceDetails); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) - .ReturnsAsync(mockDeviceModel); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - mockTag - }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) - .ReturnsAsync(new List()); - - _ = this.mockSnackbarService.Setup(c => c.Add(It.IsAny(), Severity.Error, It.IsAny>(), It.IsAny())).Returns((Snackbar)null); - - // Act - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID)); - - cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(""); - var saveButton = cut.WaitForElement("#saveButton"); - saveButton.Click(); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void ClickOnConnectShouldDisplayDeviceCredentials() - { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - var mockTag = new DeviceTagDto - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - }; - - var mockDeviceDetails = new DeviceDetails - { - DeviceName = Guid.NewGuid().ToString(), - ModelId = mockDeviceModel.ModelId, - DeviceID = Guid.NewGuid().ToString(), - Tags = new Dictionary() - { - {mockTag.Name,Guid.NewGuid().ToString()} - } - }; - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) - .ReturnsAsync(mockDeviceDetails); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) - .ReturnsAsync(mockDeviceModel); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - mockTag - }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) - .ReturnsAsync(new List()); - - var mockDialogReference = new DialogReference(Guid.NewGuid(), this.mockDialogService.Object); - _ = this.mockDialogService.Setup(c => c.Show(It.IsAny(), It.IsAny())) - .Returns(mockDialogReference); - - // Act - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID)); - - var connectButton = cut.WaitForElement("#connectButton"); - connectButton.Click(); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void ClickOnDeleteShouldDisplayConfirmationDialogAndReturnIfAborted() - { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - var mockTag = new DeviceTagDto - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - }; - - var mockDeviceDetails = new DeviceDetails - { - DeviceName = Guid.NewGuid().ToString(), - ModelId = mockDeviceModel.ModelId, - DeviceID = Guid.NewGuid().ToString(), - Tags = new Dictionary() - { - {mockTag.Name,Guid.NewGuid().ToString()} - } - }; - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) - .ReturnsAsync(mockDeviceDetails); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) - .ReturnsAsync(mockDeviceModel); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - mockTag - }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) - .ReturnsAsync(new List()); - - var mockDialogReference = MockRepository.Create(); - _ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Cancel()); - _ = this.mockDialogService.Setup(c => c.Show(It.IsAny(), It.IsAny())) - .Returns(mockDialogReference.Object); - - // Act - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID)); - - var deleteButton = cut.WaitForElement("#deleteButton"); - deleteButton.Click(); - - // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void ClickOnDeleteShouldDisplayConfirmationDialogAndRedirectIfConfirmed() - { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - var mockTag = new DeviceTagDto - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - }; - - var mockDeviceDetails = new DeviceDetails - { - DeviceName = Guid.NewGuid().ToString(), - ModelId = mockDeviceModel.ModelId, - DeviceID = Guid.NewGuid().ToString(), - Tags = new Dictionary() - { - {mockTag.Name,Guid.NewGuid().ToString()} - } - }; - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) - .ReturnsAsync(mockDeviceDetails); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) - .ReturnsAsync(mockDeviceModel); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - mockTag - }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) - .ReturnsAsync(new List()); - - var mockDialogReference = MockRepository.Create(); - _ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Ok("Ok")); - _ = this.mockDialogService.Setup(c => c.Show(It.IsAny(), It.IsAny())) - .Returns(mockDialogReference.Object); - - // Act - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID)); - - var deleteButton = cut.WaitForElement("#deleteButton"); - deleteButton.Click(); - - // Assert - cut.WaitForState(() => this.mockNavigationManager.Uri.EndsWith("/devices", StringComparison.OrdinalIgnoreCase)); - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - } - - [Test] - public void ClickOnDuplicateShouldDuplicateDeviceDetailAndRedirectToCreateDevicePage() - { - var mockDeviceModel = new DeviceModelDto - { - ModelId = Guid.NewGuid().ToString(), - Description = Guid.NewGuid().ToString(), - SupportLoRaFeatures = false, - Name = Guid.NewGuid().ToString() - }; - - var mockTag = new DeviceTagDto - { - Label = Guid.NewGuid().ToString(), - Name = Guid.NewGuid().ToString(), - Required = false, - Searchable = false - }; - - var mockDeviceDetails = new DeviceDetails - { - DeviceName = Guid.NewGuid().ToString(), - ModelId = mockDeviceModel.ModelId, - DeviceID = Guid.NewGuid().ToString(), - Tags = new Dictionary() - { - {mockTag.Name,Guid.NewGuid().ToString()} - } - }; - - _ = this.mockDeviceClientService - .Setup(service => service.GetDevice(mockDeviceDetails.DeviceID)) - .ReturnsAsync(mockDeviceDetails); - - _ = this.mockDeviceModelsClientService.Setup(service => service.GetDeviceModel(mockDeviceDetails.ModelId)) - .ReturnsAsync(mockDeviceModel); - - _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()) - .ReturnsAsync(new List - { - mockTag - }); - - _ = this.mockDeviceClientService - .Setup(service => service.GetDeviceProperties(mockDeviceDetails.DeviceID)) - .ReturnsAsync(new List()); - - var popoverProvider = RenderComponent(); - var cut = RenderComponent(ComponentParameter.CreateParameter("DeviceID", mockDeviceDetails.DeviceID)); - cut.WaitForAssertion(() => cut.Find($"#{nameof(DeviceModelDto.Name)}").InnerHtml.Should().NotBeEmpty()); - - var saveButton = cut.WaitForElement("#saveButton"); - - var mudButtonGroup = cut.FindComponent(); - - mudButtonGroup.Find(".mud-menu button").Click(); - popoverProvider.WaitForAssertion(() => popoverProvider.FindAll("div.mud-list-item").Count.Should().Be(2)); - - var items = popoverProvider.FindAll("div.mud-list-item"); - - // Click on Duplicate - items[1].Click(); - - // Act - saveButton.Click(); // Assert - cut.WaitForAssertion(() => MockRepository.VerifyAll()); - cut.WaitForAssertion(() => this.mockNavigationManager.Uri.Should().EndWith("/devices/new")); + cut.WaitForAssertion(() => cut.Find("#saveButton").TextContent.Should().Be("Save")); } } }