// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Interactivity.UnitTests { using System; using System.Collections.Generic; using System.Linq; using Perspex.Interactivity; using Perspex.VisualTree; using Xunit; public class InteractiveTests { [Fact] public void Direct_Event_Should_Go_Straight_To_Source() { var ev = new RoutedEvent("test", RoutingStrategies.Direct, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Name); var target = this.CreateTree(ev, handler, RoutingStrategies.Direct); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "2b" }, invoked); } [Fact] public void Direct_Event_Should_Have_Route_Set_To_Direct() { var ev = new RoutedEvent("test", RoutingStrategies.Direct, typeof(RoutedEventArgs), typeof(TestInteractive)); bool called = false; EventHandler handler = (s, e) => { Assert.Equal(RoutingStrategies.Direct, e.Route); called = true; }; var target = this.CreateTree(ev, handler, RoutingStrategies.Direct); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.True(called); } [Fact] public void Bubbling_Event_Should_Bubble_Up() { var ev = new RoutedEvent("test", RoutingStrategies.Bubble, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Name); var target = this.CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "2b", "1" }, invoked); } [Fact] public void Tunneling_Event_Should_Tunnel() { var ev = new RoutedEvent("test", RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Name); var target = this.CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "1", "2b" }, invoked); } [Fact] public void Tunneling_Bubbling_Event_Should_Tunnel_Then_Bubble_Up() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Name); var target = this.CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "1", "2b", "2b", "1" }, invoked); } [Fact] public void Events_Should_Have_Route_Set() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(e.Route); var target = this.CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { RoutingStrategies.Tunnel, RoutingStrategies.Tunnel, RoutingStrategies.Bubble, RoutingStrategies.Bubble, }, invoked); } [Fact] public void Direct_Subscription_Should_Not_Catch_Tunneling_Or_Bubbling() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var count = 0; EventHandler handler = (s, e) => { ++count; }; var target = this.CreateTree(ev, handler, RoutingStrategies.Direct); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(0, count); } [Fact] public void Bubbling_Subscription_Should_Not_Catch_Tunneling() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var count = 0; EventHandler handler = (s, e) => { Assert.Equal(RoutingStrategies.Bubble, e.Route); ++count; }; var target = this.CreateTree(ev, handler, RoutingStrategies.Bubble); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(2, count); } [Fact] public void Tunneling_Subscription_Should_Not_Catch_Bubbling() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var count = 0; EventHandler handler = (s, e) => { Assert.Equal(RoutingStrategies.Tunnel, e.Route); ++count; }; var target = this.CreateTree(ev, handler, RoutingStrategies.Tunnel); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(2, count); } [Fact] public void Event_Should_Should_Keep_Propogating_To_HandedEventsToo_Handlers() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => { invoked.Add(((TestInteractive)s).Name); e.Handled = true; }; var target = this.CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel, true); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "1", "2b", "2b", "1" }, invoked); } [Fact] public void Direct_Class_Handlers_Should_Be_Called() { var ev = new RoutedEvent( "test", RoutingStrategies.Direct, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Name); var target = this.CreateTree(ev, null, 0); ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Direct); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "2b" }, invoked); } [Fact] public void Tunneling_Class_Handlers_Should_Be_Called() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Name); var target = this.CreateTree(ev, null, 0); ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Tunnel); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "1", "2b" }, invoked); } [Fact] public void Bubbling_Class_Handlers_Should_Be_Called() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive)); var invoked = new List(); EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Name); var target = this.CreateTree(ev, null, 0); ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Bubble); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.Equal(new[] { "2b", "1" }, invoked); } [Fact] public void Typed_Class_Handlers_Should_Be_Called() { var ev = new RoutedEvent( "test", RoutingStrategies.Bubble | RoutingStrategies.Tunnel, typeof(TestInteractive)); var target = this.CreateTree(ev, null, 0); ev.AddClassHandler(x => x.ClassHandler, RoutingStrategies.Bubble); var args = new RoutedEventArgs(ev, target); target.RaiseEvent(args); Assert.True(target.ClassHandlerInvoked); Assert.True(target.GetVisualParent().ClassHandlerInvoked); } private TestInteractive CreateTree( RoutedEvent ev, EventHandler handler, RoutingStrategies handlerRoutes, bool handledEventsToo = false) { TestInteractive target; var tree = new TestInteractive { Name = "1", Children = new[] { new TestInteractive { Name = "2a", }, (target = new TestInteractive { Name = "2b", Children = new[] { new TestInteractive { Name = "3", }, }, }), } }; if (handler != null) { foreach (var i in tree.GetSelfAndVisualDescendents().Cast()) { i.AddHandler(ev, handler, handlerRoutes, handledEventsToo); } } return target; } private class TestInteractive : Interactive { public string Name { get; set; } public bool ClassHandlerInvoked { get; private set; } public IEnumerable Children { get { return ((IVisual)this).VisualChildren.AsEnumerable(); } set { this.AddVisualChildren(value.Cast()); } } public void ClassHandler(RoutedEventArgs e) { this.ClassHandlerInvoked = true; } } } }