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.BikesAtStation; using ShareeBike.ViewModel.Map; using ShareeBike.ViewModel.Settings; using Xamarin.Forms; using static ShareeBike.Repository.CopriCallsMemory; namespace SharedBusinessLogic.Tests.Fixtures.ObjectTests.ViewModel { [TestFixture] public class TestBikesAtStationPageViewModel { [Test] public async Task TestConstruct_Droid_NotLoggedIn() { var geolocation = Substitute.For>(); var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.Off }, { FilterHelper.CITYBIKE, FilterState.On } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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("", null, false, null, new List())), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo("MyMerchId", "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory001(sessionCookie)), merchantId: "MyMerchId", 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.Android, new ShareeBike.Model.Stations.StationNS.Station("FR103", new List(), null), // Was station id 103 before switching from data provider CopriCallsMemory(SampleSets.ShareeFr01_Set1, 1, ...) () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); Assert.That(bikesAtStation.StatusInfoText, Is.Empty); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(2)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bikeFR9999 = bikesAtStation.FirstOrDefault(x => x.Id == "FR9999") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; // Was id 1315 var bikeFR9998 = bikesAtStation.FirstOrDefault(x => x.Id == "FR9998") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; // Was id 1543 Assert.That( bikeFR9999, Is.Not.Null); Assert.That( bikeFR9998, Is.Not.Null); Assert.That(bikeFR9999.StateText, Is.EqualTo("Available.")); Assert.That(bikeFR9998.StateText, Is.EqualTo("Available.")); Assert.That(bikeFR9999.LockitButtonText, Is.EqualTo("NotLoggedIn")); Assert.That(bikeFR9998.LockitButtonText, Is.EqualTo("NotLoggedIn")); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.True); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } [Test] public async Task TestConstruct_Droid_NoBikes() { var geolocation = Substitute.For>(); var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.Off } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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("MyMerchId", "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.ShareeFr01_Set1, 1, sessionCookie)), merchantId: "MyMerchId", 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.Android, new ShareeBike.Model.Stations.StationNS.Station("102", new List(), null /* position*/, operatorUri: new Uri("https://shareeapp-fr01.copri.eu//APIjsonserver")), // Station 102 () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); Assert.That(bikesAtStation.StatusInfoText, Is.Empty); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(0)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.False, "If there are any bikes, list must be visible."); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.True); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo("There are currently no bicycles available at this station.")); } [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(); // 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 Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.Off } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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, // Instance geolocation mocks both parts 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.Android, new ShareeBike.Model.Stations.StationNS.Station("103", new List(), null), // Station 103 () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.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(bikesAtStation.StatusInfoText, Is.Empty); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(4)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = bikesAtStation.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = bikesAtStation.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1315 = bikesAtStation.FirstOrDefault(x => x.Id == "1315") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1543 = bikesAtStation.FirstOrDefault(x => x.Id == "1543") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That( bike1545, Is.Not.Null); Assert.That( bike1537, Is.Not.Null); Assert.That( bike1315, Is.Not.Null); Assert.That( bike1543, Is.Not.Null); 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(bike1315.StateText, Is.EqualTo("Available.")); Assert.That(bike1543.StateText, Is.EqualTo("Available.")); 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(bike1315.LockitButtonText, Is.EqualTo("DisposableDisconnected")); Assert.That(bike1543.LockitButtonText, Is.EqualTo("DisposableDisconnected")); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.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(); // 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); // 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() } } ); bluetooth.State.Returns(BluetoothState.On); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.Off } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.Android, new ShareeBike.Model.Stations.StationNS.Station("103", new List(), null), // Station 103 () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); // Verify behavior Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; // permissions.RequestAsync(); // Ask user from permissions (disabled, since no secondary request allowed). 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(new List { "Updating...", string.Empty }.Contains(bikesAtStation.StatusInfoText), Is.True); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(4)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = bikesAtStation.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = bikesAtStation.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1315 = bikesAtStation.FirstOrDefault(x => x.Id == "1315") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1543 = bikesAtStation.FirstOrDefault(x => x.Id == "1543") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That( bike1545, Is.Not.Null); Assert.That( bike1537, Is.Not.Null); Assert.That( bike1315, Is.Not.Null); Assert.That( bike1543, Is.Not.Null); 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(bike1315.StateText, Is.EqualTo("Available.")); Assert.That(bike1543.StateText, Is.EqualTo("Available.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1315.LockitButtonText, Is.EqualTo("DisposableDisconnected")); Assert.That(bike1543.LockitButtonText, Is.EqualTo("DisposableDisconnected")); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } [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(); // 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. 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 Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.Off } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.Android, new ShareeBike.Model.Stations.StationNS.Station("103", new List(), null), // Station 103 () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); // Verify behavior Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; //permissions.RequestAsync(); // Ask user from permissions (disabled, since no secondary request allowed). viewService.DisplayAlert( "Hint", "Please allow location sharing so that bike lock/locks can be managed.\r\nOpen sharing dialog?", "Yes", "No"); }); Assert.That(bikesAtStation.StatusInfoText, Is.Empty); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(4)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = bikesAtStation.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = bikesAtStation.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1315 = bikesAtStation.FirstOrDefault(x => x.Id == "1315") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1543 = bikesAtStation.FirstOrDefault(x => x.Id == "1543") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That( bike1545, Is.Not.Null); Assert.That( bike1537, Is.Not.Null); Assert.That( bike1315, Is.Not.Null); Assert.That( bike1543, Is.Not.Null); 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(bike1315.StateText, Is.EqualTo("Available.")); Assert.That(bike1543.StateText, Is.EqualTo("Available.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1315.LockitButtonText, Is.EqualTo("DisposableDisconnected")); Assert.That(bike1543.LockitButtonText, Is.EqualTo("DisposableDisconnected")); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } [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(); // Fake location permissions to be set permissions.CheckStatusAsync().Returns(Task.FromResult(Status.Granted)); geolocation.Active.IsGeolcationEnabled.Returns(false); // Fake gps to be off 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 Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.Off } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.Android, new ShareeBike.Model.Stations.StationNS.Station("103", new List(), null), // Station 103 () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.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(bikesAtStation.StatusInfoText, Is.Empty); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(4)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = bikesAtStation.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = bikesAtStation.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1315 = bikesAtStation.FirstOrDefault(x => x.Id == "1315") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1543 = bikesAtStation.FirstOrDefault(x => x.Id == "1543") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That( bike1545, Is.Not.Null); Assert.That( bike1537, Is.Not.Null); Assert.That( bike1315, Is.Not.Null); Assert.That( bike1543, Is.Not.Null); 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(bike1315.StateText, Is.EqualTo("Available.")); Assert.That(bike1543.StateText, Is.EqualTo("Available.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1315.LockitButtonText, Is.EqualTo("DisposableDisconnected")); Assert.That(bike1543.LockitButtonText, Is.EqualTo("DisposableDisconnected")); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.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(); // 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 // 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 Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.Off } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.Android, new ShareeBike.Model.Stations.StationNS.Station("103", new List(), null), // Station 103 () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.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(bikesAtStation.StatusInfoText, Is.Empty); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(4)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); var bike1545 = bikesAtStation.FirstOrDefault(x => x.Id == "1545") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = bikesAtStation.FirstOrDefault(x => x.Id == "1537") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1315 = bikesAtStation.FirstOrDefault(x => x.Id == "1315") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1543 = bikesAtStation.FirstOrDefault(x => x.Id == "1543") as ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.That( bike1545, Is.Not.Null); Assert.That( bike1537, Is.Not.Null); Assert.That( bike1315, Is.Not.Null); Assert.That( bike1543, Is.Not.Null); 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(bike1315.StateText, Is.EqualTo("Available.")); Assert.That(bike1543.StateText, Is.EqualTo("Available.")); Assert.That(bike1545.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1537.ButtonText, Is.EqualTo("Connect lock")); Assert.That(bike1315.LockitButtonText, Is.EqualTo("DisposableDisconnected")); Assert.That(bike1543.LockitButtonText, Is.EqualTo("DisposableDisconnected")); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } [Test] public async Task TestConstructCitybike() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); bluetooth.State.Returns(BluetoothState.On); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.Off } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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(), 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 CopriCallsMemory001(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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.iOS, new ShareeBike.Model.Stations.StationNS.Station("FR101", new List(), null), // Was 31 before switching CopriCallsMemory(SampleSets.Set2, 1, ...) () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); Assert.That(new List { "Updating...", string.Empty }.Contains(bikesAtStation.StatusInfoText), Is.True); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(3)); // Was 1 before switch. Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR1003").StateText, Is.EqualTo("Available.")); // Was 52 before switch. Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR1002").StateText, Is.EqualTo("Available.")); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR1001").StateText, Is.EqualTo("Available.")); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.True); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } [Test] public async Task TestConstructCitybike_NoBikes() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); bluetooth.State.Returns(BluetoothState.On); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); const string MERCH_ID = "MyMerchId"; var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.Off }, { FilterHelper.CITYBIKE, FilterState.On } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.CITYBIKE, FilterState.On } }), new StartupSettings(), new Uri("https://tinkwwp.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(), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo("MyMerchId", "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory(MERCH_ID, SampleSets.Set2, 1, sessionCookie)), merchantId: "MyMerchId", 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.iOS, new ShareeBike.Model.Stations.StationNS.Station("4", new List(), null), () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); Assert.That(new List { "Updating...", string.Empty }.Contains(bikesAtStation.StatusInfoText), Is.True); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(0)); Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.False, "If there are any bikes, list must be visible."); // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.True); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo("There are currently no bicycles available at this station.")); } [Test] public async Task TestConstruct_LoggedIn() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); bluetooth.State.Returns(BluetoothState.On); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.Off }, { FilterHelper.CITYBIKE, FilterState.On } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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: locksService.GetType().FullName), new StoreMock(new ShareeBike.Model.User.Account.Account( LoginSessionCopriInfo.JavaministerHardwareNr1.Mail, LoginSessionCopriInfo.JavaministerHardwareNr1.Pwd, false, "6103_112e96b36ba33de245943c5ffaf369cd_", // Was 4da3044c8657a04ba60e2eaa753bc51a before switching from data provider CopriCallsMemory(SampleSets.Set2, 1, ...) new List { FilterHelper.CARGOBIKE })), isConnectedFunc: () => true, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo("MyMerchId", "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory001(sessionCookie)), merchantId: "MyMerchId", 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.iOS, new ShareeBike.Model.Stations.StationNS.Station("FR103", new List(), null), // Was station id 4 before switching from data provider CopriCallsMemory(SampleSets.Set2, 1, ...) () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); Assert.That(new List { "Updating...", string.Empty }.Contains(bikesAtStation.StatusInfoText), Is.True); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(4)); // Count of bikes was 4 before switching from data provider CopriCallsMemory(SampleSets.Set2, 1, ...) Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText, Is.EqualTo("Available.")); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText, Is.EqualTo("Available.")); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR1004").StateText, Is.EqualTo("Max. reservation time expired.")); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR1544").StateText, Is.EqualTo($"Rented since {DateTime.Parse("2021-11-06 18:57:25.445447+01"):dd. MMMM HH:mm}.")); // Was 7 // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.False); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } [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(); bluetooth.State.Returns(BluetoothState.On); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.Off }, { FilterHelper.CITYBIKE, FilterState.On } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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(), isConnectedFunc: () => false, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new ConnectorCache(new AppContextInfo("MyMerchId", "MyApp", new Version(1, 2)), null /*UI language */, sessionCookie, mail, server: new CopriCallsMemory001(sessionCookie)), merchantId: "MyMerchId", bluetoothService: Substitute.For(), locationPermissionsService: Substitute.For(), locationServicesContainer: Substitute.For>(), locksService: locksService, // Cipher device: new DeviceMock(), specialFolder: new SpecialFolderMock(), cipher: null, // Not connected. 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.iOS, new ShareeBike.Model.Stations.StationNS.Station("FR103", new List(), null), // Was station id 4 before switching from data provider CopriCallsMemory(SampleSets.Set2, 1 () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService); await bikesAtStation.OnAppearingOrRefresh(); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(2)); // Count of bikes was 3. There is no more bike with id 26. Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText, Is.EqualTo("Available.")); // Was id 5 Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9998").StateText, Is.EqualTo("Available.")); // Was is 14 // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.True); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } [Test] public async Task TestConstruct_WebConnectCommunicationError() { var locksService = Substitute.For(); var timeOut = Substitute.For(); var viewService = Substitute.For(); var permissions = Substitute.For(); var bluetooth = Substitute.For(); bluetooth.State.Returns(BluetoothState.On); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.Off }, { FilterHelper.CITYBIKE, FilterState.On } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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(), 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 CopriCallsCacheMemory001(sessionCookie: sessionCookie), httpsServer: new ExceptionServer((msg) => new WebConnectFailureException(msg, new Exception("Source expection."))))), merchantId: "MyMerchId", bluetoothService: Substitute.For(), locationPermissionsService: Substitute.For(), locationServicesContainer: Substitute.For>(), locksService: locksService, // Cipher device: new DeviceMock(), specialFolder: new SpecialFolderMock(), cipher: null, // Not connected. 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.iOS, new ShareeBike.Model.Stations.StationNS.Station("FR103", new List(), null), // Was station id 4 before switching from data provider CopriCallsCacheMemory() () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService) { IsReportLevelVerbose = true }; await bikesAtStation.OnAppearingOrRefresh(); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(2)); // Count of bikes was 3. There is no more bike with id 26. Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText, Is.EqualTo("Available.")); // Was id 5 Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9998").StateText, Is.EqualTo("Available.")); // Was is 14 // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.True); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, 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(); bluetooth.State.Returns(BluetoothState.On); locksService.TimeOut.Returns(timeOut); timeOut.MultiConnect.Returns(new TimeSpan(0)); var shareeBikeApp = new ShareeBikeApp( new Settings( new GroupFilterMapPage(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.Off }, { FilterHelper.CITYBIKE, FilterState.On } }), new GroupFilterSettings(new Dictionary { { FilterHelper.CARGOBIKE, FilterState.On }, { FilterHelper.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(), 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 CopriCallsCacheMemory001(sessionCookie: sessionCookie), httpsServer: new ExceptionServer((msg) => new Exception(msg)))), merchantId: "MyMerchId", bluetoothService: Substitute.For(), locationPermissionsService: Substitute.For(), locationServicesContainer: Substitute.For>(), locksService: locksService, // Cipher device: new DeviceMock(), specialFolder: new SpecialFolderMock(), cipher: null, // Not connected. 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 bikesAtStation = new BikesAtStationPageViewModel( shareeBikeApp.ActiveUser, permissions, bluetooth, Device.iOS, new ShareeBike.Model.Stations.StationNS.Station("FR103", new List(), null), // Was station id 4 before switching from data provider CopriCallsCacheMemory() () => shareeBikeApp.GetIsConnected(), (isConnected) => shareeBikeApp.GetConnector(isConnected), new GeolocationMock(), locksService, shareeBikeApp.Polling, (url) => { }, (d, obj) => d(obj), Substitute.For(), viewService) { IsReportLevelVerbose = true }; await bikesAtStation.OnAppearingOrRefresh(); // Verify list of bikes Assert.That(bikesAtStation.Count, Is.EqualTo(2)); // Count of bikes was 3. There is no more bike with id 26. Assert.That(bikesAtStation.IsIdle, Is.True); Assert.That(bikesAtStation.IsBikesListVisible, Is.True, "If there are any bikes, list must be visible."); Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9999").StateText, Is.EqualTo("Available.")); // Was id 5 Assert.That(bikesAtStation.FirstOrDefault(x => x.Id == "FR9998").StateText, Is.EqualTo("Available.")); // Was id 14. // Login hint/ no bikes frame Assert.That(bikesAtStation.IsLoginRequiredHintVisible, Is.True); Assert.That(bikesAtStation.IsNoBikesAtStationVisible, Is.False); Assert.That(bikesAtStation.NoBikesAtStationText, Is.EqualTo(string.Empty)); } } }