using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NSubstitute; using NSubstitute.ExceptionExtensions; using NUnit.Framework; using Plugin.BLE.Abstractions.Contracts; using SharedBusinessLogic.Tests.Framework.Model.Device; using SharedBusinessLogic.Tests.Framework.Model.Services.Geolocation; using SharedBusinessLogic.Tests.Framework.Model.User.Account; using SharedBusinessLogic.Tests.Framework.Repository; using ShareeBike.Model; using ShareeBike.Model.Connector; using ShareeBike.Model.Device; using ShareeBike.Model.Services.CopriApi; using ShareeBike.Model.Settings; using ShareeBike.Repository; using ShareeBike.Repository.Exception; using ShareeBike.Services; using ShareeBike.Services.BluetoothLock; using ShareeBike.Services.BluetoothLock.Tdo; using ShareeBike.Services.Geolocation; using ShareeBike.Services.Permissions; using ShareeBike.View; using ShareeBike.ViewModel.Map; using ShareeBike.ViewModel.MyBikes; using ShareeBike.ViewModel.Settings; using Xamarin.Forms; using static ShareeBike.Repository.CopriCallsMemory; namespace SharedBusinessLogic.Tests.Fixtures.ObjectTests.ViewModel { [TestFixture] public class TestMyBikesPageViewModel { [Test] public async Task TestConstruct_Droid() { var geolocation = Substitute.For>(); var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); // Fake location permissions to be set permissions.CheckStatusAsync().Returns(Task.FromResult(Status.Granted)); geolocation.Active.IsGeolcationEnabled.Returns(true); // Fake gps to be on bluetooth.State.Returns(BluetoothState.On); // Fake bluetooth to be on // Fake bluetooth answer for locks with id 2200545 and 2200537. locksService.GetLocksStateAsync(Arg.Any>(), Arg.Any()).Returns( new List { { new LockInfoTdo.Builder { Id = 2200545, State = LockitLockingState.Open }.Build() }, { new LockInfoTdo.Builder { Id = 2200537, State = LockitLockingState.Closed }.Build() } } ); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "5781_d47fc786e740ef77d85a24bcb6f0ff97_oiF2kahH", new List { "300001", "300029" })), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo(MERCH_ID, "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.ShareeFr01_Set1, 1, sessionCookie)), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: null, locationServicesContainer: geolocation, locksService: locksService, // Cipher device: new DeviceMock(), // Permissions, specialFolder: new SpecialFolderMock(), cipher: null, theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behavior Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; var btDummy = bluetooth.Received().State; locksService.GetLocksStateAsync(Arg.Any>(), Arg.Any()); }); Assert.That(myBikes.StatusInfoText, Is.Empty); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That(bike1545.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.")); Assert.That(bike1537.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Close lock")); Assert.That(bike1545.LockitButtonText, Is.EqualTo("BookedOpen")); Assert.That(bike1537.LockitButtonText, Is.EqualTo("Open lock")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That(myBikes.NoBikesOccupiedText, Is.Empty); } [Test] public async Task TestConstruct_Droid_NoPermissions_OpenSettings() { var geolocation = Substitute.For>(); var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); // Fake location permissions not to be set permissions.CheckStatusAsync().Returns(Task.FromResult(Status.Denied)); // Fake anwser on question whether to open permissions dialog viewService.DisplayAlert(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()).Returns(true); locksService.GetLocksStateAsync(Arg.Any>(), Arg.Any()).Returns( new List { { new LockInfoTdo.Builder { Id = 2200545, State = LockitLockingState.Open }.Build() }, { new LockInfoTdo.Builder { Id = 2200537, State = LockitLockingState.Closed }.Build() } } ); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "5781_d47fc786e740ef77d85a24bcb6f0ff97_oiF2kahH", new List { "300001", "300029" })), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo(MERCH_ID, "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.ShareeFr01_Set1, 1, sessionCookie)), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: null, locationServicesContainer: geolocation, locksService: locksService, // Cipher device: new DeviceMock(), // Permissions, specialFolder: new SpecialFolderMock(), cipher: null, theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behavior Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; permissions.RequestAsync(); // Ask user from permissions. viewService.DisplayAlert( "Hint", "Please allow location sharing so that bike lock/locks can be managed.\r\nOpen sharing dialog?", "Yes", "No"); permissions.OpenAppSettings(); }); Assert.That(myBikes.StatusInfoText, Is.Empty, "Unexpected status info text detected."); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That(bike1545.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.")); Assert.That(bike1537.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That(myBikes.NoBikesOccupiedText, Is.Empty, "There must not be any bikes occupied."); } [Test] public async Task TestConstruct_Droid_NoPermissions() { var geolocation = Substitute.For>(); var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); // Fake location permissions not to be set permissions.CheckStatusAsync().Returns(Task.FromResult(Status.Denied)); permissions.OpenAppSettings().Throws(); // Ensures that method is not called and fixture succeeds. // Fake anwser on question whether to open permissions dialog viewService.DisplayAlert(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()).Returns(false); // Fake bluetooth answer for locks with id 2200545 and 2200537. var lockInfoTdo = new List { { new LockInfoTdo.Builder { Id = 2200545, State = LockitLockingState.Open }.Build() }, { new LockInfoTdo.Builder { Id = 2200537, State = LockitLockingState.Closed }.Build() } }; locksService.GetLocksStateAsync(Arg.Any>(), Arg.Any()).Returns(lockInfoTdo); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "5781_d47fc786e740ef77d85a24bcb6f0ff97_oiF2kahH", new List { "300001", "300029" })), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo(MERCH_ID, "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.ShareeFr01_Set1, 1, sessionCookie)), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: null, locationServicesContainer: geolocation, locksService: locksService, // Cipher device: new DeviceMock(), // Permissions, specialFolder: new SpecialFolderMock(), cipher: null, theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behavior Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; permissions.RequestAsync(); // Ask user from permissions. viewService.DisplayAlert( "Hint", "Please allow location sharing so that bike lock/locks can be managed.\r\nOpen sharing dialog?", "Yes", "No"); }); Assert.That(myBikes.StatusInfoText, Is.Empty, "Unexpected status info text detected."); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That(bike1545.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.")); Assert.That(bike1537.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That(myBikes.NoBikesOccupiedText, Is.Empty, "There must not be any bikes occupied."); } [Test] public async Task TestConstruct_Droid_GeolocationOff() { var geolocation = Substitute.For>(); var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); // Fake location permissions to be set permissions.CheckStatusAsync().Returns(Task.FromResult(Status.Granted)); geolocation.Active.IsGeolcationEnabled.Returns(false); // Fake gps to be off // Fake bluetooth answer for locks with id 2200545 and 2200537. locksService.GetLocksStateAsync(Arg.Any>(), Arg.Any()).Returns( new List { { new LockInfoTdo.Builder { Id = 2200545, State = LockitLockingState.Open }.Build() }, { new LockInfoTdo.Builder { Id = 2200537, State = LockitLockingState.Closed }.Build() } } ); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "5781_d47fc786e740ef77d85a24bcb6f0ff97_oiF2kahH", new List { "300001", "300029" })), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo(MERCH_ID, "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.ShareeFr01_Set1, 1, sessionCookie)), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: null, locationServicesContainer: geolocation, locksService: locksService, // Cipher device: new DeviceMock(), // Permissions, specialFolder: new SpecialFolderMock(), cipher: null, theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behavior Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; viewService.DisplayAlert( "Hint", "Please activate location so that bike lock can be found!", "OK"); }); Assert.That(myBikes.StatusInfoText, Is.Empty); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That(bike1545.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.")); Assert.That(bike1537.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That(myBikes.NoBikesOccupiedText, Is.Empty); } [Test] public async Task TestConstruct_Droid_BluetoothOff() { var geolocation = Substitute.For>(); var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); // Fake location permissions to be set permissions.CheckStatusAsync().Returns(Task.FromResult(Status.Granted)); geolocation.Active.IsGeolcationEnabled.Returns(true); // Fake gps to be on bluetooth.State.Returns(BluetoothState.Off); // Fake bluetooth to be off var lockInfoTdo = new List { { new LockInfoTdo.Builder { Id = 2200545, State = LockitLockingState.Open }.Build() }, { new LockInfoTdo.Builder { Id = 2200537, State = LockitLockingState.Closed }.Build() } }; locksService.GetLocksStateAsync(Arg.Any>(), Arg.Any()).Returns(lockInfoTdo); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "5781_d47fc786e740ef77d85a24bcb6f0ff97_oiF2kahH", new List { "300001", "300029" })), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo(MERCH_ID, "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.ShareeFr01_Set1, 1, sessionCookie)), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: null, locationServicesContainer: geolocation, locksService: locksService, // Cipher device: new DeviceMock(), // Permissions, specialFolder: new SpecialFolderMock(), cipher: null, theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behavior Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; var btDummy = bluetooth.Received().State; viewService.DisplayAlert( "Hint", "Please enable Bluetooth to manage bike lock/locks.", "OK"); }); Assert.That( myBikes.StatusInfoText, Is.Empty, "Status info text must be empty."); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That(bike1545.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.")); Assert.That(bike1537.StateText, Is.EqualTo($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That( myBikes.NoBikesOccupiedText, Is.Empty, "Label which informs that no bikes are reserved/ rented must be empty."); } [Test] public async Task TestConstruct_NoBikesOccupied() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "Invalid_SessionCookie", new List { "ShareeBike" })), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo(MERCH_ID, "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.Set2, 1, sessionCookie)), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: Substitute.For(), locationServicesContainer: Substitute.For>(), locksService: locksService, // Cipher device: new DeviceMock(), specialFolder: new SpecialFolderMock(), cipher: null, theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); Assert.That(new List { "Updating...", string.Empty }.Contains(myBikes.StatusInfoText), Is.True); Assert.That(myBikes.Count, Is.EqualTo(0)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.False, "If there are any bikes, list must be visible."); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.True); Assert.That(myBikes.NoBikesOccupiedText, Is.EqualTo("There are currently no bikes reserved/rented by user a@b.")); } [Test] public async Task TestConstruct_Offline() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "4da3044c8657a04ba60e2eaa753bc51a", new List { "ShareeBike" })), isConnectedFunc: () => false, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo(MERCH_ID, "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.Set2, 1, sessionCookie)), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: null, locationServicesContainer: Substitute.For>(), locksService: locksService, // Cipher device: new DeviceMock(), // Permissions specialFolder: new SpecialFolderMock(), cipher: null, // Offline theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(myBikes.FirstOrDefault(x => x.Id == "7").StateText, Is.EqualTo($"Rented since {DateTime.Parse("2017-11-28 13:06:55.147368+01"):dd. MMMM HH:mm}.")); Assert.That(myBikes.FirstOrDefault(x => x.Id == "8").StateText, Is.EqualTo($"Rented since {DateTime.Parse("2017-11-28 11:01:51.637747+01"):dd. MMMM HH:mm}.")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That(myBikes.NoBikesOccupiedText, Is.EqualTo(string.Empty)); } [Test] public async Task TestShareeBikeApp_WebConnectCommunicationError() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "4da3044c8657a04ba60e2eaa753bc51a", new List { "ShareeBike" })), isConnectedFunc: () => false, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ShareeBike.Model.Connector.Connector( uri, new AppContextInfo("oiF2kahH", "sharee.bike.test", new Version(3, 0, 267)), null /*UI language */, sessionCookie, mail, server: new CopriProviderHttps( uri, ShareeBikeApp.MerchantId, new AppContextInfo("oiF2kahH", "sharee.bike.test", new Version(3, 0, 267)), null /*UI language */, sessionCookie: sessionCookie, cacheServer: new CopriCallsCacheMemory(MERCH_ID, sessionCookie: sessionCookie), httpsServer: new ExceptionServer((msg) => new WebConnectFailureException(msg, new Exception("Source expection."))))), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: Substitute.For(), locationServicesContainer: Substitute.For>(), locksService: locksService, // Cipher device: new DeviceMock(), specialFolder: new SpecialFolderMock(), cipher: null, // Offline theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }) { IsReportLevelVerbose = true }; await myBikes.OnAppearingOrRefresh(); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(myBikes.FirstOrDefault(x => x.Id == "7").StateText, Is.EqualTo($"Rented since {DateTime.Parse("2017-11-28 13:06:55.147368+01"):dd. MMMM HH:mm}.")); Assert.That(myBikes.FirstOrDefault(x => x.Id == "8").StateText, Is.EqualTo($"Rented since {DateTime.Parse("2017-11-28 11:01:51.637747+01"):dd. MMMM HH:mm}.")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That(myBikes.NoBikesOccupiedText, Is.EqualTo(string.Empty)); } [Test] public async Task TestShareeBikeApp_GeneralPurposeError() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new ShareeBike.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "ShareeBike", FilterState.On }, { "Citybike", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new ShareeBike.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new ShareeBike.Model.User.Account.Account("a@b", "123456789", false, "4da3044c8657a04ba60e2eaa753bc51a", new List { "ShareeBike" })), isConnectedFunc: () => false, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ShareeBike.Model.Connector.Connector( uri, new AppContextInfo("oiF2kahH", "sharee.bike.test", new Version(3, 0, 267)), null /*UI language */, sessionCookie, mail, server: new CopriProviderHttps( uri, ShareeBikeApp.MerchantId, new AppContextInfo("oiF2kahH", "sharee.bike.test", new Version(3, 0, 267)), null /*UI language */, sessionCookie: sessionCookie, cacheServer: new CopriCallsCacheMemory(MERCH_ID, sessionCookie: sessionCookie), httpsServer: new ExceptionServer((msg) => new Exception(msg)))), merchantId: MERCH_ID, bluetoothService: Substitute.For(), locationPermissionsService: null, locationServicesContainer: Substitute.For>(), locksService: locksService, // Cipher device: new DeviceMock(), // Permissions. specialFolder: new SpecialFolderMock(), cipher: null, // Offline theme: null, postAction: (d, obj) => d(obj), currentVersion: new Version(3, 2, 0, 115), // Current app version lastVersion: new Version(3, 0, 173), // Current app version. Must be larger or equal 3.0.173 to whatsNewShownInVersion: null); // Whats new page was never shown. var myBikes = new MyBikesPageViewModel( shareeBikeApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Stations, shareeBikeApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }) { IsReportLevelVerbose = true }; await myBikes.OnAppearingOrRefresh(); Assert.That(myBikes.Count, Is.EqualTo(2)); Assert.That(myBikes.IsIdle, Is.True); Assert.That(myBikes.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(myBikes.FirstOrDefault(x => x.Id == "7").StateText, Is.EqualTo($"Rented since {DateTime.Parse("2017-11-28 13:06:55.147368+01"):dd. MMMM HH:mm}.")); Assert.That(myBikes.FirstOrDefault(x => x.Id == "8").StateText, Is.EqualTo($"Rented since {DateTime.Parse("2017-11-28 11:01:51.637747+01"):dd. MMMM HH:mm}.")); Assert.That(myBikes.IsNoBikesOccupiedVisible, Is.False); Assert.That(myBikes.NoBikesOccupiedText, Is.EqualTo(string.Empty)); } } }