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 TestFramework.Model.Device; using TestFramework.Model.Services.Geolocation; using TestFramework.Model.User.Account; using TestFramework.Repository; using TestTINKLib.Mocks.Connector; using TINK.Model; using TINK.Model.Connector; using TINK.Model.Device; using TINK.Model.Services.CopriApi; using TINK.Model.Settings; using TINK.Repository; using TINK.Repository.Exception; using TINK.Services; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Tdo; using TINK.Services.Geolocation; using TINK.Services.Permissions; using TINK.View; using TINK.ViewModel.Map; using TINK.ViewModel.MyBikes; using TINK.ViewModel.Settings; using Xamarin.Forms; using static TINK.Repository.CopriCallsMemory; namespace TestTINKLib.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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.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( tinkApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behaviour Received.InOrder(() => { permissions.CheckStatusAsync(); var glDummy = geolocation.Active.Received().IsGeolcationEnabled; var btDummy = bluetooth.Received().State; locksService.GetLocksStateAsync(Arg.Any>(), Arg.Any()); }); Assert.IsEmpty(myBikes.StatusInfoText); Assert.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.AreEqual($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.", bike1545.StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.", bike1537.StateText); Assert.AreEqual("Close lock", bike1545.LockitButtonText); Assert.AreEqual("Open lock", bike1537.LockitButtonText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); Assert.IsEmpty(myBikes.NoBikesOccupiedText); } [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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.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( tinkApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behaviour 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.IsEmpty(myBikes.StatusInfoText, "Unexpected status info text detected."); Assert.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.AreEqual($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.", bike1545.StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.", bike1537.StateText); Assert.AreEqual("Search lock", bike1545.LockitButtonText); Assert.AreEqual("Search lock", bike1537.LockitButtonText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); Assert.IsEmpty(myBikes.NoBikesOccupiedText, "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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.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( tinkApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behaviour 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.IsEmpty(myBikes.StatusInfoText, "Unexpected status info text detected."); Assert.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.AreEqual($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.", bike1545.StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.", bike1537.StateText); Assert.AreEqual("Search lock", bike1545.LockitButtonText); Assert.AreEqual("Search lock", bike1537.LockitButtonText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); Assert.IsEmpty(myBikes.NoBikesOccupiedText, "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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.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( tinkApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behaviour 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.IsEmpty(myBikes.StatusInfoText); Assert.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.AreEqual($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.", bike1545.StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.", bike1537.StateText); Assert.AreEqual("Search lock", bike1545.LockitButtonText); Assert.AreEqual("Search lock", bike1537.LockitButtonText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); Assert.IsEmpty(myBikes.NoBikesOccupiedText); } [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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.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( tinkApp.ActiveUser, permissions, /* permissions */ bluetooth, /* bluetooth */ Device.Android, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), geolocation.Active, // geolocation locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); // Verify behaviour 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.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); var bike1545 = myBikes.FirstOrDefault(x => x.Id == "1545") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; var bike1537 = myBikes.FirstOrDefault(x => x.Id == "1537") as TINK.ViewModel.Bikes.Bike.BluetoothLock.BikeViewModel; Assert.AreEqual($"Rented since {DateTime.Parse("2020-11-06 17:53:22.784681+01"):dd. MMMM HH:mm}.", bike1545.StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2020-10-12 08:38:12.374231+02"):dd. MMMM HH:mm}.", bike1537.StateText); Assert.AreEqual("Search lock", bike1545.LockitButtonText); Assert.AreEqual("Search lock", bike1537.LockitButtonText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); 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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.Model.User.Account.Account("a@b", "123456789", false, "Invalid_SessionCookie", new List { "TINK" })), 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( tinkApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), new GeolocationMock(), locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); Assert.IsTrue(new List { "Updating...", string.Empty }.Contains(myBikes.StatusInfoText)); Assert.AreEqual(0, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsFalse(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); Assert.IsTrue(myBikes.IsNoBikesOccupiedVisible); Assert.AreEqual("There are currently no bikes reserved/rented by user a@b.", myBikes.NoBikesOccupiedText); } [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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.Model.User.Account.Account("a@b", "123456789", false, "4da3044c8657a04ba60e2eaa753bc51a", new List { "TINK" })), 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( tinkApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), new GeolocationMock(), locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }); await myBikes.OnAppearingOrRefresh(); Assert.AreEqual("Offline.", myBikes.StatusInfoText); Assert.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); Assert.AreEqual($"Rented since {DateTime.Parse("2017-11-28 13:06:55.147368+01"):dd. MMMM HH:mm}.", myBikes.FirstOrDefault(x => x.Id == "7").StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2017-11-28 11:01:51.637747+01"):dd. MMMM HH:mm}.", myBikes.FirstOrDefault(x => x.Id == "8").StateText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); Assert.AreEqual(string.Empty, myBikes.NoBikesOccupiedText); } [Test] public async Task TestTinkApp_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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.Model.User.Account.Account("a@b", "123456789", false, "4da3044c8657a04ba60e2eaa753bc51a", new List { "TINK" })), isConnectedFunc: () => false, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new TINK.Model.Connector.Connector( uri, new AppContextInfo("oiF2kahH", "sharee.bike.test", new Version(3, 0, 267)), null /*UI language */, sessionCookie, mail, server: new CopriProviderHttps( uri, TinkApp.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( tinkApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), new GeolocationMock(), locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }) { IsReportLevelVerbose = true }; await myBikes.OnAppearingOrRefresh(); Assert.AreEqual("Connection interrupted, server unreachable.", myBikes.StatusInfoText); Assert.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); Assert.AreEqual($"Rented since {DateTime.Parse("2017-11-28 13:06:55.147368+01"):dd. MMMM HH:mm}.", myBikes.FirstOrDefault(x => x.Id == "7").StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2017-11-28 11:01:51.637747+01"):dd. MMMM HH:mm}.", myBikes.FirstOrDefault(x => x.Id == "8").StateText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); Assert.AreEqual(string.Empty, myBikes.NoBikesOccupiedText); } [Test] public async Task TestTinkApp_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 tinkApp = new TinkApp( new TINK.Model.Settings.Settings( new GroupFilterMapPage(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.Off } }), new GroupFilterSettings(new Dictionary { { "TINK", FilterState.On }, { "Konrad", FilterState.On } }), new StartupSettings(), new Uri("https://shareeapp-primary.copri-bike.de/APIjsonserver"), new TINK.Settings.PollingParameters(new TimeSpan(10000), true), Serilog.Events.LogEventLevel.Error, activeLockService: locksService.GetType().FullName, activeGeolocationService: typeof(GeolocationMock).FullName), new StoreMock(new TINK.Model.User.Account.Account("a@b", "123456789", false, "4da3044c8657a04ba60e2eaa753bc51a", new List { "TINK" })), isConnectedFunc: () => false, connectorFactory: (isConnected, uri, sessionCookie, mail, expiresAfter) => new TINK.Model.Connector.Connector( uri, new AppContextInfo("oiF2kahH", "sharee.bike.test", new Version(3, 0, 267)), null /*UI language */, sessionCookie, mail, server: new CopriProviderHttps( uri, TinkApp.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( tinkApp.ActiveUser, permissions, // Permissions, bluetooth, Device.iOS, () => tinkApp.GetIsConnected(), (isConnected) => tinkApp.GetConnector(isConnected), new GeolocationMock(), locksService, tinkApp.Stations, tinkApp.Polling, (d, obj) => d(obj), Substitute.For(), viewService, url => { }) { IsReportLevelVerbose = true }; await myBikes.OnAppearingOrRefresh(); Assert.AreEqual("Connection interrupted.", myBikes.StatusInfoText); Assert.AreEqual(2, myBikes.Count); Assert.IsTrue(myBikes.IsIdle); Assert.IsTrue(myBikes.IsBikesListVisible, "If there are any bikes, list must be visible."); Assert.AreEqual($"Rented since {DateTime.Parse("2017-11-28 13:06:55.147368+01"):dd. MMMM HH:mm}.", myBikes.FirstOrDefault(x => x.Id == "7").StateText); Assert.AreEqual($"Rented since {DateTime.Parse("2017-11-28 11:01:51.637747+01"):dd. MMMM HH:mm}.", myBikes.FirstOrDefault(x => x.Id == "8").StateText); Assert.IsFalse(myBikes.IsNoBikesOccupiedVisible); Assert.AreEqual(string.Empty, myBikes.NoBikesOccupiedText); } } }