Unit testing with MVVM Light & DispatcherHelper - silverlight

I have a SL4 app that utilizes the MVVM Light Toolkit. Within a view model, I call a data service that retrieves data from an OData service. Within the VM, I am using the DispatcherHelper utility class (part of MVVM Light) to update the property on the VM from the data in the callback I pass into the data service. For instance, my view model method looks like this:
public string CurrentUserLogin {
get {
if (string.IsNullOrEmpty(_currentUserLogin))
RetrieveCurrentUserLogin();
return string.IsNullOrEmpty(_currentUserLogin) ? _currentUserLogin : _currentUserLogin.Replace(#"\\", #"\");
}
set {
if (_currentUserLogin != value) {
_currentUserLogin = value;
RaisePropertyChanged(CurrentUserLoginPropertyName);
}
}
}
private void RetrieveCurrentUserLogin() {
DataService.GetCurrentUserLogin(result => {
DispatcherHelper.UIDispatcher.BeginInvoke(() => {
CurrentUserLogin = result;
});
});
}
And here's what my data service looks like:
public void GetCurrentUserLogin(Action<string> callback) {
// create query request
var query = OnDemandContext.CreateQuery<string>("GetCurrentUserLogin");
var request = (HttpWebRequest)WebRequest.Create(query.RequestUri);
request.BeginGetResponse(asyncResult => {
var responseStream = request.EndGetResponse(asyncResult).GetResponseStream();
var responseDocument = XDocument.Load(responseStream);
callback(responseDocument.Root.Value);
}, null);
}
Everything works great when I run my SL application. However the problem I have is when I try to write unit tests against it using the SL Unit Testing Framework. I can test my data service without an issue, but it seems the DispatcherHelper is throwing a wrench into all my tests as the DispatcherHelper.UIDispatcher is always null when fired. I'm assuming it has something to do with the initlization (which is in my SL app's Application_Startup()). I tried initializing it in my test app but that isn't helping. I've also tried using DispatcherHelper.CheckBeginInvokeOnUI() but that has no effect on the issue.
Ideas?

AC,
I just created a simple SL UT project and I did this in the App.XAML.CS
private void Application_Startup(object sender, StartupEventArgs e)
{
RootVisual = UnitTestSystem.CreateTestPage();
DispatcherHelper.Initialize();
}
Then I set this as the test (in the tests.cs):
[TestMethod]
public void TestMethod1()
{
Assert.IsNotNull(DispatcherHelper.UIDispatcher, "UI Dispatcher should not be null");
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
// Do nothing
var x = 1;
});
}
That worked for me. I even set a break point on the "var x = 1;" and it hit the breakpoint. Does this solve your problem? (if so please mark it as the answer).

Related

Paypal Selenium automation Accept Cookies not working

It looks like paypal has updated their plugin lately and my code stopped working. I tried using Selenium IDE but when I record using it I do not see the Accept Cookie modal popup. I am able to get pass login as below, but I tried many different way to get to submit payment button with no luck. Help appreciated.
public IDictionary<string, object> vars { get; private set; }
vars = new Dictionary<string, object>();
_driver.SwitchTo().Frame(0);
vars["WindowHandles"] = _driver.WindowHandles;
_driver.FindElement(By.CssSelector(".paypal-button")).Click();
vars["win8061"] = waitForWindow(2000);
vars["root"] = _driver.CurrentWindowHandle;
_driver.SwitchTo().Window(vars["win8061"].ToString());
_driver.FindElement(By.Id("email")).SendKeys(paypalEmail);
_driver.FindElement(By.Id("btnNext")).Click();
_driver.FindElement(By.Id("password")).SendKeys(paypalPassword);
_driver.FindElement(By.Id("btnLogin")).Click();
//The problem is here!!!
var element = _driver.FindElement(By.Id("payment-submit-btn"));
Actions builder = new Actions(_driver);
builder.MoveToElement(element).Perform();
public string waitForWindow(int timeout)
{
try
{
Thread.Sleep(timeout);
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
}
var whNow = ((IReadOnlyCollection<object>)_driver.WindowHandles).ToList();
var whThen = ((IReadOnlyCollection<object>)vars["WindowHandles"]).ToList();
if (whNow.Count > whThen.Count)
{
return whNow.Except(whThen).First().ToString();
}
else
{
return whNow.First().ToString();
}
}
Had same issue recently after PayPal made some kind of changes to their "Pay" button. All of the sudden it stopped working. Below is what worked for me. There is no logic behind it, besides "just because it works".
After PayPal login; in your case after:
_driver.FindElement(By.Id("btnLogin")).Click();
Use:
Thread.Sleep(1000);
_driver.FindElement(By.Id("acceptAllButton")).Click();
try
{
_driver.FindElement(By.Id("payment-submit-btn")).Click();
}
catch
{
_driver.FindElement(By.Id("payment-submit-btn")).Click();
}

Is it possible to send a message from a WPF app that is hosting a CefSharp control, to the web app running in CefSharp like WebView2 can do

With WebVeiw2 you can send a message to a web app running in it using WebView2Ctrl?.CoreWebView2?.PostWebMessageAsJson(message).
Is there a way of doing this in CefSharp
Create a class (I used JavascriptCallbackMessenger) to Set and Run the callbacks.
public class JavascriptCallbackMessenger
{
private IJavascriptCallback _callback;
public void SetCallBack(IJavascriptCallback callback)
{
_callback = callback;
}
public void RunCallback(string message)
{
if (_callback != null && _callback.CanExecute)
{
_callback.ExecuteAsync(message);
}
}
}
Create an instance of JavascriptCallbackMessenger and register it with the CefSharp control
CefSharpCtrl.JavascriptObjectRepository.Register(JavascriptCallbackMessengerName, _messenger, true, BindingOptions.DefaultBinder);
Set the callback in Javascript as follows (I'm not a JS developer, but this was my solution).
(async function() {
const cefSharp = (window as any).CefSharp;
await cefSharp.BindObjectAsync(JavascriptCallbackMessengerName);
window.javascriptCallbackMessenger.setCallBack(function(message: string)
{
console.log("messageHandler: " + message);
})
})();
I was using typescript, so I had to extend the Window with the newly created variable.
declare global {
interface Window { javascriptCallbackMessenger: any; }
}
Apologies, but the formatting seems to be a bit "off"!

Nancy testing GetModel<T> throws KeyNotFoundException

I'm trying to test that the model returned from my Nancy application is as expected. I have followed the docs here but whenever I call the GetModel<T> extension method it throws a KeyNotFoundException.
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
I know what the error means but I'm failing to see why it's being thrown.
Here's my module
public class SanityModule : NancyModule
{
public SanityModule()
{
Get["sanity-check"] = _ => Negotiate.WithModel(new SanityViewModel { Id = 1 })
.WithStatusCode(HttpStatusCode.OK);
}
}
my view model
public class SanityViewModel
{
public int Id { get; set; }
}
and here's my test
[TestFixture]
public class SanityModuleTests
{
[Test]
public void Sanity_Check()
{
// Arrange
var browser = new Browser(with =>
{
with.Module<SanityModule>();
with.ViewFactory<TestingViewFactory>();
});
// Act
var result = browser.Get("/sanity-check", with =>
{
with.HttpRequest();
with.Header("accept", "application/json");
});
var model = result.GetModel<SanityViewModel>();
// Asset
model.Id.ShouldBeEquivalentTo(1);
}
}
Debugging this test shows that the module is hit and completes just fine. Running the application shows that the response is as expected.
Can anyone shed some light on this?
Thanks to the lovely guys, albertjan and the.fringe.ninja, in the Nancy Jabbr room we've got an explanation as to what's going on here.
TL;DR It makes sense for this to not work but the error message should be more descriptive. There is a workaround below.
The issue here is that I am requesting the response as application/json whilst using TestingViewFactory.
Let's take a look at the implementation of GetModel<T>();
public static TType GetModel<TType>(this BrowserResponse response)
{
return (TType)response.Context.Items[TestingViewContextKeys.VIEWMODEL];
}
This is simply grabbing the view model from the NancyContext and casting it to your type. This is where the error is thrown, as there is no view model in NancyContext. This is because the view model is added to NancyContext in the RenderView method of TestingViewFactory.
public Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
{
// Intercept and store interesting stuff
viewLocationContext.Context.Items[TestingViewContextKeys.VIEWMODEL] = model;
viewLocationContext.Context.Items[TestingViewContextKeys.VIEWNAME] = viewName;
viewLocationContext.Context.Items[TestingViewContextKeys.MODULENAME] = viewLocationContext.ModuleName;
viewLocationContext.Context.Items[TestingViewContextKeys.MODULEPATH] = viewLocationContext.ModulePath;
return this.decoratedViewFactory.RenderView(viewName, model, viewLocationContext);
}
My test is requesting json so RenderView will not be called. This means you can only use GetModel<T> if you use a html request.
Workaround
My application is an api so I do not have any views so changing the line
with.Header("accept", "application/json");
to
with.Header("accept", "text/html");
will throw a ViewNotFoundException. To avoid this I need to implement my own IViewFactory. (this comes from the.fringe.ninja)
public class TestViewFactory : IViewFactory
{
#region IViewFactory Members
public Nancy.Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
{
viewLocationContext.Context.Items[Fixtures.SystemUnderTest.ViewModelKey] = model;
return new HtmlResponse();
}
#endregion
}
Then it is simply a case of updating
with.ViewFactory<TestingViewFactory>();
to
with.ViewFactory<TestViewFactory>();
Now GetModel<T> should work without needing a view.

Exception thrown when using TaskSourceCompletion in Silverlight 5

I'm trying to wrap the Event Async Programming model used in RIA Services in a Task.
I have followed the standard way of using a TaskCompletionSource and implemented the following extension method:
public static Task<IEnumerable<T>> LoadAsync<T>(this DomainContext source, EntityQuery<T> query) where T : Entity
{
TaskCompletionSource<IEnumerable<T>> taskCompletionSource = new TaskCompletionSource<IEnumerable<T>>();
source.Load(
query,
loadOperation =>
{
if (loadOperation.HasError && !loadOperation.IsErrorHandled)
{
taskCompletionSource.TrySetException(loadOperation.Error);
loadOperation.MarkErrorAsHandled();
}
else if (loadOperation.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
taskCompletionSource.TrySetResult(loadOperation.Entities);
}
},
null);
return taskCompletionSource.Task;
}
I then use this in the following way:
var task = _context.LoadAsync(_context.GetPlayersQuery());
task.Start();
task.Result;
The problem though is that I get an InvalidOperationException stating that "Start may not be called on a promise-style task". I have tried not starting the task, but then the loadOperation callback never fires.
Can anyone see what I am doing wrong here?
Thanks in advance
Problem is solved. Under the hood the DomainContext.Load() method is already operating in an asynchronous manner. There must have been some conflict with trying to wrap an already asynchronous method in a task.
However, even if I still follow the EAP correctly with the code below, I still get the InvalidOperationException of 'start cannot be called on a promise-style task'
public static Task<IEnumerable<T>> LoadAsync<T>(this DomainContext source, EntityQuery<T> query) where T : Entity
{
TaskCompletionSource<IEnumerable<T>> taskCompletionSource = new TaskCompletionSource<IEnumerable<T>>();
var loadOperation = source.Load(query);
loadOperation.Completed += (obj, args) =>
{
if (loadOperation.HasError && !loadOperation.IsErrorHandled)
{
taskCompletionSource.TrySetException(loadOperation.Error);
loadOperation.MarkErrorAsHandled();
}
else if (loadOperation.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
taskCompletionSource.TrySetResult(loadOperation.Entities);
}
};
return taskCompletionSource.Task;
}
Try this instead
var result = await _context.LoadAsync(_context.GetPlayersQuery());
Try using
task.ContinuewWith(Action<Task<T>> continuation)
That worked for me, as I too got that exception when using task.Start

Rhino Mocks and PRISM EventAggregator

I need to do something that seems quite simple, but I cant seem to achieve it.
I need to be able to write a unit test that calls the action and filter delegates of any subscription to an eventaggregator event.
For example, in my class that needs to be tested I have the following code:
this.eventAggregator.GetEvent<RiskDataViewsJournalChangedEvent>().Subscribe(
this.OnViewRequestPublished, ThreadOption.UIThread, false, this.EventFilter);
and I want my test to call the this.OnViewRequestPublished method and this.EventFilter method.
Ive tried using an instance of the EventAggregator class in my test but the events never get fired without a dispatcher present, which is not helpfull in a unit test.
Therefore I want to use Rhino Mocks, but I cant get my head around how to achieve what I need.
Thanks
Dean
Ive solved it myself - here is my code (ignore the EventToken references, this is something ive created myself for event filtering).
[Test]
public void RiskDataGridViewModelEventSubscriptionTests()
{
// event token
var tok = new EventToken();
// mock event aggregator
var agg = MockRepository.GenerateStub<IEventAggregator>();
// my target class subscribes to 3 events in its constructor
var evt1 = MockRepository.GenerateStub<RiskDataViewsJournalChangedEvent>();
var evt2 = MockRepository.GenerateStub<RiskDataViewsJournalChangingEvent>();
var evt3 = MockRepository.GenerateStub<RiskDataViewRequestPublishEvent>();
// ensure mocked event classes are returned
agg.Stub(x => x.GetEvent<RiskDataViewsJournalChangedEvent>()).Return(evt1);
agg.Stub(x => x.GetEvent<RiskDataViewsJournalChangingEvent>()).Return(evt2);
agg.Stub(x => x.GetEvent<RiskDataViewRequestPublishEvent>()).Return(evt3);
// instantiate target class - in my class the events get subscribed to in the constructor
new RiskDataGridViewModel(agg, tok, null);
// get the parameters passed to the subscribe method
var args1 = evt1.GetArgumentsForCallsMadeOn(s => s.Subscribe(null));
var args2 = evt2.GetArgumentsForCallsMadeOn(s => s.Subscribe(null));
var args3 = evt3.GetArgumentsForCallsMadeOn(s => s.Subscribe(null));
// invoke filters
((Predicate<EventParameter<IRiskDataViewResultItem>>)args1[0][3]).Invoke(new EventParameter<IRiskDataViewResultItem>(null, tok));
((Predicate<EventParameter>)args2[0][3]).Invoke(new EventParameter(null, tok));
((Predicate<EventParameter<ViewRequest>>)args3[0][3]).Invoke(new EventParameter<ViewRequest>(null, tok));
// invoke methods
((Action<EventParameter<IRiskDataViewResultItem>>)args1[0][0]).Invoke(new EventParameter<IRiskDataViewResultItem>(null, tok));
((Action<EventParameter>)args2[0][0]).Invoke(new EventParameter(null, tok));
((Action<EventParameter<ViewRequest>>)args3[0][0]).Invoke(new EventParameter<ViewRequest>(null, tok));
}
/// <remarks>Event.Publish does not work in unit tests when subscribed with ThreadOption.UIThread </remarks>
public void FireGlobalEvent<TEvent, TEventArgs>(TEventArgs args)
where TEvent : CompositePresentationEvent<TEventArgs>, new ()
{
var globalEvent = GetInstance<IEventAggregator>().GetEvent<TEvent>();
var subscriptions = (IEnumerable)globalEvent.GetType().GetProperty("Subscriptions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(globalEvent);
foreach (DispatcherEventSubscription<TEventArgs> subscription in subscriptions.Cast<object>().ToArray())
{
subscription.Action.Invoke(args);
}
}

Resources