Unit tests fixed.
This commit is contained in:
parent
648b1b3585
commit
473c38e643
@ -96,6 +96,11 @@ namespace eShopOnContainers.Core.ViewModels.Base
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RegisterSingleton<TInterface, T>() where TInterface : class where T : class, TInterface
|
||||||
|
{
|
||||||
|
_container.Register<TInterface, T>().AsSingleton();
|
||||||
|
}
|
||||||
|
|
||||||
public static T Resolve<T>() where T : class
|
public static T Resolve<T>() where T : class
|
||||||
{
|
{
|
||||||
return _container.Resolve<T>();
|
return _container.Resolve<T>();
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace eShopOnContainers.UnitTests.Helpers
|
|
||||||
{
|
|
||||||
public class PropertyChangeTracker
|
|
||||||
{
|
|
||||||
List<string> _notifications = new List<string>();
|
|
||||||
|
|
||||||
public PropertyChangeTracker(INotifyPropertyChanged changer)
|
|
||||||
{
|
|
||||||
changer.PropertyChanged += (sender, e) => _notifications.Add(e.PropertyName + ".Value");
|
|
||||||
}
|
|
||||||
|
|
||||||
//public string[] ChangedProperties
|
|
||||||
//{
|
|
||||||
// get { return _notifications.ToArray(); }
|
|
||||||
//}
|
|
||||||
|
|
||||||
public bool WaitForChange(string propertyName, int maxWaitMilliSeconds)
|
|
||||||
{
|
|
||||||
var startTime = DateTime.UtcNow;
|
|
||||||
while (!_notifications.Contains(propertyName))
|
|
||||||
{
|
|
||||||
if (startTime.AddMilliseconds(maxWaitMilliSeconds) < DateTime.UtcNow)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool WaitForChange(string propertyName, TimeSpan maxWait)
|
|
||||||
{
|
|
||||||
var startTime = DateTime.UtcNow;
|
|
||||||
while (!_notifications.Contains(propertyName))
|
|
||||||
{
|
|
||||||
if (startTime + maxWait < DateTime.UtcNow)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_notifications.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using eShopOnContainers.Core.Services.Settings;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests.Mocks
|
||||||
|
{
|
||||||
|
public class MockSettingsService : ISettingsService
|
||||||
|
{
|
||||||
|
string _accessTokenDefault = string.Empty;
|
||||||
|
string _idTokenDefault = string.Empty;
|
||||||
|
bool _useMocksDefault = true;
|
||||||
|
string _urlBaseDefault = "https://13.88.8.119";
|
||||||
|
bool _useFakeLocationDefault = false;
|
||||||
|
bool _allowGpsLocationDefault = false;
|
||||||
|
double _fakeLatitudeDefault = 47.604610d;
|
||||||
|
double _fakeLongitudeDefault = -122.315752d;
|
||||||
|
|
||||||
|
public string AuthAccessToken
|
||||||
|
{
|
||||||
|
get { return _accessTokenDefault; }
|
||||||
|
set { _accessTokenDefault = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AuthIdToken
|
||||||
|
{
|
||||||
|
get { return _idTokenDefault; }
|
||||||
|
set { _idTokenDefault = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseMocks
|
||||||
|
{
|
||||||
|
get { return _useMocksDefault; }
|
||||||
|
set { _useMocksDefault = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string UrlBase
|
||||||
|
{
|
||||||
|
get { return _urlBaseDefault; }
|
||||||
|
set { _urlBaseDefault = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseFakeLocation
|
||||||
|
{
|
||||||
|
get { return _useFakeLocationDefault; }
|
||||||
|
set { _useFakeLocationDefault = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Latitude
|
||||||
|
{
|
||||||
|
get { return _fakeLatitudeDefault.ToString(); }
|
||||||
|
set { _fakeLatitudeDefault = Convert.ToDouble(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Longitude
|
||||||
|
{
|
||||||
|
get { return _fakeLongitudeDefault.ToString(); }
|
||||||
|
set { _fakeLongitudeDefault = Convert.ToDouble(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AllowGpsLocation
|
||||||
|
{
|
||||||
|
get { return _allowGpsLocationDefault; }
|
||||||
|
set { _allowGpsLocationDefault = value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
namespace eShopOnContainers.UnitTests.Services
|
using System.Threading.Tasks;
|
||||||
{
|
using eShopOnContainers.Core;
|
||||||
using System.Threading.Tasks;
|
using eShopOnContainers.Core.Services.Marketing;
|
||||||
using Core;
|
using Xunit;
|
||||||
using Core.Helpers;
|
|
||||||
using Core.Services.Marketing;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests.Services
|
||||||
|
{
|
||||||
public class MarketingServiceTests
|
public class MarketingServiceTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -5,6 +5,8 @@ using eShopOnContainers.Core.Services.Catalog;
|
|||||||
using eShopOnContainers.Core.Models.Catalog;
|
using eShopOnContainers.Core.Models.Catalog;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using eShopOnContainers.Core.Services.Settings;
|
||||||
|
using eShopOnContainers.UnitTests.Mocks;
|
||||||
|
|
||||||
namespace eShopOnContainers.UnitTests
|
namespace eShopOnContainers.UnitTests
|
||||||
{
|
{
|
||||||
@ -13,6 +15,7 @@ namespace eShopOnContainers.UnitTests
|
|||||||
public CatalogViewModelTests()
|
public CatalogViewModelTests()
|
||||||
{
|
{
|
||||||
ViewModelLocator.UpdateDependencies(true);
|
ViewModelLocator.UpdateDependencies(true);
|
||||||
|
ViewModelLocator.RegisterSingleton<ISettingsService, MockSettingsService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -2,53 +2,56 @@
|
|||||||
using eShopOnContainers.Core.ViewModels;
|
using eShopOnContainers.Core.ViewModels;
|
||||||
using eShopOnContainers.Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
using eShopOnContainers.Core.Models.Navigation;
|
using eShopOnContainers.Core.Models.Navigation;
|
||||||
|
using eShopOnContainers.Core.Services.Settings;
|
||||||
|
using eShopOnContainers.UnitTests.Mocks;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace eShopOnContainers.UnitTests
|
namespace eShopOnContainers.UnitTests
|
||||||
{
|
{
|
||||||
public class MainViewModelTests
|
public class MainViewModelTests
|
||||||
{
|
{
|
||||||
public MainViewModelTests()
|
public MainViewModelTests()
|
||||||
{
|
{
|
||||||
ViewModelLocator.UpdateDependencies(true);
|
ViewModelLocator.UpdateDependencies(true);
|
||||||
}
|
ViewModelLocator.RegisterSingleton<ISettingsService, MockSettingsService>();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SettingsCommandIsNotNullWhenViewModelInstantiatedTest()
|
public void SettingsCommandIsNotNullWhenViewModelInstantiatedTest()
|
||||||
{
|
{
|
||||||
var mainViewModel = new MainViewModel();
|
var mainViewModel = new MainViewModel();
|
||||||
Assert.NotNull(mainViewModel.SettingsCommand);
|
Assert.NotNull(mainViewModel.SettingsCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ViewModelInitializationSendsChangeTabMessageTest()
|
public async Task ViewModelInitializationSendsChangeTabMessageTest()
|
||||||
{
|
{
|
||||||
bool messageReceived = false;
|
bool messageReceived = false;
|
||||||
var mainViewModel = new MainViewModel();
|
var mainViewModel = new MainViewModel();
|
||||||
var tabParam = new TabParameter { TabIndex = 2 };
|
var tabParam = new TabParameter { TabIndex = 2 };
|
||||||
|
|
||||||
Xamarin.Forms.MessagingCenter.Subscribe<MainViewModel, int>(this, MessageKeys.ChangeTab, (sender, arg) =>
|
Xamarin.Forms.MessagingCenter.Subscribe<MainViewModel, int>(this, MessageKeys.ChangeTab, (sender, arg) =>
|
||||||
{
|
{
|
||||||
messageReceived = true;
|
messageReceived = true;
|
||||||
});
|
});
|
||||||
await mainViewModel.InitializeAsync(tabParam);
|
await mainViewModel.InitializeAsync(tabParam);
|
||||||
|
|
||||||
Assert.True(messageReceived);
|
Assert.True(messageReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsBusyPropertyIsFalseWhenViewModelInstantiatedTest()
|
public void IsBusyPropertyIsFalseWhenViewModelInstantiatedTest()
|
||||||
{
|
{
|
||||||
var mainViewModel = new MainViewModel();
|
var mainViewModel = new MainViewModel();
|
||||||
Assert.False(mainViewModel.IsBusy);
|
Assert.False(mainViewModel.IsBusy);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task IsBusyPropertyIsTrueAfterViewModelInitializationTest()
|
public async Task IsBusyPropertyIsTrueAfterViewModelInitializationTest()
|
||||||
{
|
{
|
||||||
var mainViewModel = new MainViewModel();
|
var mainViewModel = new MainViewModel();
|
||||||
await mainViewModel.InitializeAsync(null);
|
await mainViewModel.InitializeAsync(null);
|
||||||
Assert.True(mainViewModel.IsBusy);
|
Assert.True(mainViewModel.IsBusy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,36 @@
|
|||||||
namespace eShopOnContainers.UnitTests.ViewModels
|
using System.Threading.Tasks;
|
||||||
{
|
using Xunit;
|
||||||
using System.Threading.Tasks;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
using Xunit;
|
using eShopOnContainers.Core.Services.Marketing;
|
||||||
using Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels;
|
||||||
using Core.Services.Marketing;
|
using eShopOnContainers.UnitTests.Mocks;
|
||||||
using Core.ViewModels;
|
using eShopOnContainers.Core.Services.Settings;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests.ViewModels
|
||||||
|
{
|
||||||
public class MarketingViewModelTests
|
public class MarketingViewModelTests
|
||||||
{
|
{
|
||||||
public MarketingViewModelTests()
|
public MarketingViewModelTests()
|
||||||
{
|
{
|
||||||
ViewModelLocator.UpdateDependencies(true);
|
ViewModelLocator.UpdateDependencies(true);
|
||||||
|
ViewModelLocator.RegisterSingleton<ISettingsService, MockSettingsService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCampaignsIsNullTest()
|
public void GetCampaignsIsNullTest()
|
||||||
{
|
{
|
||||||
|
var settingsService = new MockSettingsService();
|
||||||
var campaignService = new CampaignMockService();
|
var campaignService = new CampaignMockService();
|
||||||
var campaignViewModel = new CampaignViewModel(campaignService);
|
var campaignViewModel = new CampaignViewModel(settingsService, campaignService);
|
||||||
Assert.Null(campaignViewModel.Campaigns);
|
Assert.Null(campaignViewModel.Campaigns);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetCampaignsIsNotNullTest()
|
public async Task GetCampaignsIsNotNullTest()
|
||||||
{
|
{
|
||||||
|
var settingsService = new MockSettingsService();
|
||||||
var campaignService = new CampaignMockService();
|
var campaignService = new CampaignMockService();
|
||||||
var campaignViewModel = new CampaignViewModel(campaignService);
|
var campaignViewModel = new CampaignViewModel(settingsService, campaignService);
|
||||||
|
|
||||||
await campaignViewModel.InitializeAsync(null);
|
await campaignViewModel.InitializeAsync(null);
|
||||||
|
|
||||||
@ -35,24 +40,27 @@
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void GetCampaignDetailsCommandIsNotNullTest()
|
public void GetCampaignDetailsCommandIsNotNullTest()
|
||||||
{
|
{
|
||||||
|
var settingsService = new MockSettingsService();
|
||||||
var campaignService = new CampaignMockService();
|
var campaignService = new CampaignMockService();
|
||||||
var campaignViewModel = new CampaignViewModel(campaignService);
|
var campaignViewModel = new CampaignViewModel(settingsService, campaignService);
|
||||||
Assert.NotNull(campaignViewModel.GetCampaignDetailsCommand);
|
Assert.NotNull(campaignViewModel.GetCampaignDetailsCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCampaignDetailsByIdIsNullTest()
|
public void GetCampaignDetailsByIdIsNullTest()
|
||||||
{
|
{
|
||||||
|
var settingsService = new MockSettingsService();
|
||||||
var campaignService = new CampaignMockService();
|
var campaignService = new CampaignMockService();
|
||||||
var campaignViewModel = new CampaignDetailsViewModel(campaignService);
|
var campaignViewModel = new CampaignDetailsViewModel(settingsService, campaignService);
|
||||||
Assert.Null(campaignViewModel.Campaign);
|
Assert.Null(campaignViewModel.Campaign);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetCampaignDetailsByIdIsNotNullTest()
|
public async Task GetCampaignDetailsByIdIsNotNullTest()
|
||||||
{
|
{
|
||||||
|
var settingsService = new MockSettingsService();
|
||||||
var campaignService = new CampaignMockService();
|
var campaignService = new CampaignMockService();
|
||||||
var campaignDetailsViewModel = new CampaignDetailsViewModel(campaignService);
|
var campaignDetailsViewModel = new CampaignDetailsViewModel(settingsService, campaignService);
|
||||||
|
|
||||||
await campaignDetailsViewModel.InitializeAsync(1);
|
await campaignDetailsViewModel.InitializeAsync(1);
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
using Xunit;
|
using Xunit;
|
||||||
using eShopOnContainers.Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
using eShopOnContainers.UnitTests.Helpers;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace eShopOnContainers.UnitTests
|
namespace eShopOnContainers.UnitTests
|
||||||
{
|
{
|
||||||
@ -86,15 +84,12 @@ namespace eShopOnContainers.UnitTests
|
|||||||
bool invoked = false;
|
bool invoked = false;
|
||||||
var mockViewModel = new MockViewModel();
|
var mockViewModel = new MockViewModel();
|
||||||
|
|
||||||
PropertyChangedEventHandler handler = (sender, e) =>
|
mockViewModel.Forename.PropertyChanged += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.PropertyName.Equals("Value"))
|
if (e.PropertyName.Equals("Value"))
|
||||||
invoked = true;
|
invoked = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
mockViewModel.Forename.PropertyChanged += handler;
|
|
||||||
mockViewModel.Forename.Value = "John";
|
mockViewModel.Forename.Value = "John";
|
||||||
mockViewModel.Forename.PropertyChanged -= handler;
|
|
||||||
|
|
||||||
Assert.True(invoked);
|
Assert.True(invoked);
|
||||||
}
|
}
|
||||||
@ -105,15 +100,12 @@ namespace eShopOnContainers.UnitTests
|
|||||||
bool invoked = false;
|
bool invoked = false;
|
||||||
var mockViewModel = new MockViewModel();
|
var mockViewModel = new MockViewModel();
|
||||||
|
|
||||||
PropertyChangedEventHandler handler = (sender, e) =>
|
mockViewModel.Surname.PropertyChanged += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.PropertyName.Equals("Value"))
|
if (e.PropertyName.Equals("Value"))
|
||||||
invoked = true;
|
invoked = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
mockViewModel.Surname.PropertyChanged += handler;
|
|
||||||
mockViewModel.Surname.Value = "Smith";
|
mockViewModel.Surname.Value = "Smith";
|
||||||
mockViewModel.Surname.PropertyChanged -= handler;
|
|
||||||
|
|
||||||
Assert.True(invoked);
|
Assert.True(invoked);
|
||||||
}
|
}
|
||||||
|
@ -4,52 +4,58 @@ using eShopOnContainers.Core.ViewModels;
|
|||||||
using eShopOnContainers.Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
using eShopOnContainers.Core.Services.Order;
|
using eShopOnContainers.Core.Services.Order;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using eShopOnContainers.UnitTests.Mocks;
|
||||||
|
using eShopOnContainers.Core.Services.Settings;
|
||||||
|
|
||||||
namespace eShopOnContainers.UnitTests
|
namespace eShopOnContainers.UnitTests
|
||||||
{
|
{
|
||||||
public class OrderViewModelTests
|
public class OrderViewModelTests
|
||||||
{
|
{
|
||||||
public OrderViewModelTests()
|
public OrderViewModelTests()
|
||||||
{
|
{
|
||||||
ViewModelLocator.UpdateDependencies(true);
|
ViewModelLocator.UpdateDependencies(true);
|
||||||
}
|
ViewModelLocator.RegisterSingleton<ISettingsService, MockSettingsService>();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OrderPropertyIsNullWhenViewModelInstantiatedTest()
|
public void OrderPropertyIsNullWhenViewModelInstantiatedTest()
|
||||||
{
|
{
|
||||||
var orderService = new OrderMockService();
|
var settingsService = new MockSettingsService();
|
||||||
var orderViewModel = new OrderDetailViewModel(orderService);
|
var orderService = new OrderMockService();
|
||||||
Assert.Null(orderViewModel.Order);
|
var orderViewModel = new OrderDetailViewModel(settingsService, orderService);
|
||||||
}
|
Assert.Null(orderViewModel.Order);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task OrderPropertyIsNotNullAfterViewModelInitializationTest()
|
public async Task OrderPropertyIsNotNullAfterViewModelInitializationTest()
|
||||||
{
|
{
|
||||||
var orderService = new OrderMockService();
|
var settingsService = new MockSettingsService();
|
||||||
var orderViewModel = new OrderDetailViewModel(orderService);
|
var orderService = new OrderMockService();
|
||||||
|
var orderViewModel = new OrderDetailViewModel(settingsService, orderService);
|
||||||
|
|
||||||
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
|
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
|
||||||
await orderViewModel.InitializeAsync(order);
|
await orderViewModel.InitializeAsync(order);
|
||||||
|
|
||||||
Assert.NotNull(orderViewModel.Order);
|
Assert.NotNull(orderViewModel.Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SettingOrderPropertyShouldRaisePropertyChanged()
|
public async Task SettingOrderPropertyShouldRaisePropertyChanged()
|
||||||
{
|
{
|
||||||
bool invoked = false;
|
bool invoked = false;
|
||||||
var orderService = new OrderMockService();
|
var settingsService = new MockSettingsService();
|
||||||
var orderViewModel = new OrderDetailViewModel(orderService);
|
var orderService = new OrderMockService();
|
||||||
|
var orderViewModel = new OrderDetailViewModel(settingsService, orderService);
|
||||||
|
|
||||||
orderViewModel.PropertyChanged += (sender, e) =>
|
orderViewModel.PropertyChanged += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.PropertyName.Equals("Order"))
|
if (e.PropertyName.Equals("Order"))
|
||||||
invoked = true;
|
invoked = true;
|
||||||
};
|
};
|
||||||
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
|
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
|
||||||
await orderViewModel.InitializeAsync(order);
|
await orderViewModel.InitializeAsync(order);
|
||||||
|
|
||||||
Assert.True(invoked);
|
Assert.True(invoked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,9 +9,6 @@
|
|||||||
<PackageReference Include="xunit" Version="2.3.1" />
|
<PackageReference Include="xunit" Version="2.3.1" />
|
||||||
<PackageReference Include="xunit.runner.console" Version="2.3.1" />
|
<PackageReference Include="xunit.runner.console" Version="2.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Helpers\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\eShopOnContainers.Core\eShopOnContainers.Core.csproj" />
|
<ProjectReference Include="..\eShopOnContainers.Core\eShopOnContainers.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user