diff --git a/src/DocumentationBrowserViewExtension/DocumentationBrowserView.xaml.cs b/src/DocumentationBrowserViewExtension/DocumentationBrowserView.xaml.cs
index 766261cab34..b3990d9d288 100644
--- a/src/DocumentationBrowserViewExtension/DocumentationBrowserView.xaml.cs
+++ b/src/DocumentationBrowserViewExtension/DocumentationBrowserView.xaml.cs
@@ -7,6 +7,7 @@
using System.Windows.Controls;
using Dynamo.Logging;
using Dynamo.Utilities;
+using DynamoUtilities;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
@@ -21,7 +22,7 @@ public partial class DocumentationBrowserView : UserControl, IDisposable
private readonly DocumentationBrowserViewModel viewModel;
private const string VIRTUAL_FOLDER_MAPPING = "appassets";
static readonly string HTML_IMAGE_PATH_PREFIX = @"http://";
- private bool hasBeenInitialized;
+ internal AsyncMethodState initState = AsyncMethodState.NotStarted;
private ScriptingObject comScriptingObject;
private string fontStylePath = "Dynamo.Wpf.Views.GuidedTour.HtmlPages.Resources.ArtifaktElement-Regular.woff";
@@ -104,26 +105,22 @@ private void ShouldAllowNavigation(object sender, CoreWebView2NavigationStarting
///
public void NavigateToPage(Uri link)
{
- InitializeAsync();
+ Dispatcher.Invoke(InitializeAsync);
}
protected virtual void Dispose(bool disposing)
{
// Cleanup
this.viewModel.LinkChanged -= NavigateToPage;
- this.documentationBrowser.NavigationStarting -= ShouldAllowNavigation;
- this.documentationBrowser.DpiChanged -= DocumentationBrowser_DpiChanged;
-
- if (this.documentationBrowser.CoreWebView2 != null)
+ if (this.documentationBrowser != null)
{
- this.documentationBrowser.CoreWebView2.WebMessageReceived -= CoreWebView2OnWebMessageReceived;
- }
+ this.documentationBrowser.NavigationStarting -= ShouldAllowNavigation;
+ this.documentationBrowser.DpiChanged -= DocumentationBrowser_DpiChanged;
+ if (this.documentationBrowser.CoreWebView2 != null)
+ {
+ this.documentationBrowser.CoreWebView2.WebMessageReceived -= CoreWebView2OnWebMessageReceived;
+ }
- // Note to test writers
- // Disposing the document browser will cause future tests
- // that uses the Browser component to crash
- if (!Models.DynamoModel.IsTestMode)
- {
this.documentationBrowser.Dispose();
}
}
@@ -159,8 +156,9 @@ async void InitializeAsync()
}
// Only initialize once
- if (!hasBeenInitialized)
+ if (initState == AsyncMethodState.NotStarted)
{
+ initState = AsyncMethodState.Started;
if (!string.IsNullOrEmpty(WebBrowserUserDataFolder))
{
//This indicates in which location will be created the WebView2 cache folder
@@ -169,9 +167,9 @@ async void InitializeAsync()
UserDataFolder = WebBrowserUserDataFolder
};
}
+
//Initialize the CoreWebView2 component otherwise we can't navigate to a web page
await documentationBrowser.EnsureCoreWebView2Async();
-
this.documentationBrowser.CoreWebView2.WebMessageReceived += CoreWebView2OnWebMessageReceived;
comScriptingObject = new ScriptingObject(this.viewModel);
@@ -181,7 +179,7 @@ async void InitializeAsync()
this.documentationBrowser.CoreWebView2.Settings.IsZoomControlEnabled = true;
this.documentationBrowser.CoreWebView2.Settings.AreDevToolsEnabled = true;
- hasBeenInitialized = true;
+ initState = AsyncMethodState.Done;
}
if(Directory.Exists(VirtualFolderPath))
@@ -192,10 +190,7 @@ async void InitializeAsync()
htmlContent = ResourceUtilities.LoadResourceAndReplaceByKey(htmlContent, "#fontStyle", fontStylePath);
- Dispatcher.BeginInvoke(new Action(() =>
- {
- this.documentationBrowser.NavigateToString(htmlContent);
- }));
+ this.documentationBrowser.NavigateToString(htmlContent);
}
private void CoreWebView2OnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
@@ -209,6 +204,11 @@ private void CoreWebView2OnWebMessageReceived(object sender, CoreWebView2WebMess
///
public void Dispose()
{
+ if (initState == AsyncMethodState.Started)
+ {
+ Log("DocumentationBrowserView is being disposed but async initialization is still not done");
+ }
+
Dispose(true);
GC.SuppressFinalize(this);
}
diff --git a/src/DocumentationBrowserViewExtension/DocumentationBrowserViewExtension.cs b/src/DocumentationBrowserViewExtension/DocumentationBrowserViewExtension.cs
index b56498864e5..3934a1c6377 100644
--- a/src/DocumentationBrowserViewExtension/DocumentationBrowserViewExtension.cs
+++ b/src/DocumentationBrowserViewExtension/DocumentationBrowserViewExtension.cs
@@ -178,7 +178,7 @@ public override void Loaded(ViewLoadedParams viewLoadedParams)
public override void Shutdown()
{
- Dispose();
+ // Do nothing for now
}
private void OnInsertFile(object sender, InsertDocumentationLinkEventArgs e)
diff --git a/src/DynamoCoreWpf/Views/GuidedTour/PopupWindow.xaml.cs b/src/DynamoCoreWpf/Views/GuidedTour/PopupWindow.xaml.cs
index c007b7d3014..fe5f47fe0f3 100644
--- a/src/DynamoCoreWpf/Views/GuidedTour/PopupWindow.xaml.cs
+++ b/src/DynamoCoreWpf/Views/GuidedTour/PopupWindow.xaml.cs
@@ -105,7 +105,9 @@ private void PopupWindow_Opened(object sender, EventArgs e)
{
if (hostControlInfo.HtmlPage != null && !string.IsNullOrEmpty(hostControlInfo.HtmlPage.FileName))
{
- ContentRichTextBox.Visibility = Visibility.Hidden;
+ ContentRichTextBox.Visibility = Visibility.Hidden;
+
+ // Opened event ensures the webview2 will be visible when added to the popup layout tree.
InitWebView2Component();
}
}
diff --git a/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs b/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs
index 51b781759c7..44918de435b 100644
--- a/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs
+++ b/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs
@@ -304,6 +304,8 @@ protected override async void OnContentRendered(EventArgs e)
{
UserDataFolder = webBrowserUserDataFolder.FullName
};
+
+ //ContentRendered ensures that the webview2 component is visible.
await webView.EnsureCoreWebView2Async();
// Context menu disabled
webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
diff --git a/src/DynamoUtilities/AsyncMethodState.cs b/src/DynamoUtilities/AsyncMethodState.cs
new file mode 100644
index 00000000000..9385dbe40bf
--- /dev/null
+++ b/src/DynamoUtilities/AsyncMethodState.cs
@@ -0,0 +1,14 @@
+
+namespace DynamoUtilities
+{
+ ///
+ /// Simple representation of the states of an async method
+ /// Used for identifying issues like Dispose called before async method is finished.
+ ///
+ internal enum AsyncMethodState
+ {
+ NotStarted = 0,// Async method not called yet
+ Started,// Async method called but not finished execution (usually set before any awaits)
+ Done// Async method has finished execution (all awaits have finished)
+ }
+}
diff --git a/src/DynamoUtilities/Properties/AssemblyInfo.cs b/src/DynamoUtilities/Properties/AssemblyInfo.cs
index 67a7633b408..208a7fe482b 100644
--- a/src/DynamoUtilities/Properties/AssemblyInfo.cs
+++ b/src/DynamoUtilities/Properties/AssemblyInfo.cs
@@ -25,4 +25,4 @@
[assembly: InternalsVisibleTo("DynamoCLI")]
[assembly: InternalsVisibleTo("NodeDocumentationMarkdownGenerator")]
[assembly: InternalsVisibleTo("DynamoUtilitiesTests")]
-
+[assembly: InternalsVisibleTo("Notifications")]
diff --git a/src/LibraryViewExtensionWebView2/LibraryViewController.cs b/src/LibraryViewExtensionWebView2/LibraryViewController.cs
index 6c4499fd368..749598a66c8 100644
--- a/src/LibraryViewExtensionWebView2/LibraryViewController.cs
+++ b/src/LibraryViewExtensionWebView2/LibraryViewController.cs
@@ -120,6 +120,22 @@ internal LibraryViewController(Window dynamoView, ICommandExecutive commandExecu
{
WebBrowserUserDataFolder = webBrowserUserDataFolder.FullName;
}
+
+ /// Create and add the library view to the WPF visual tree.
+ /// Also load the library.html and js files.
+ LibraryViewModel model = new LibraryViewModel();
+ LibraryView view = new LibraryView(model);
+
+ //Adding the LibraryView to the sidebar ensures that the webview2 component is visible.
+ var sidebarGrid = dynamoWindow.FindName("sidebarGrid") as Grid;
+ sidebarGrid.Children.Add(view);
+
+ browser = view.mainGrid.Children.OfType().FirstOrDefault();
+ browser.Loaded += Browser_Loaded;
+ browser.SizeChanged += Browser_SizeChanged;
+
+ LibraryViewController.SetupSearchModelEventsObserver(browser, dynamoViewModel.Model.SearchModel,
+ this, this.customization);
}
private void DynamoViewModel_PreferencesWindowChanged(object sender, EventArgs e)
@@ -302,30 +318,6 @@ private string ReplaceUrlWithBase64Image(string html, string minifiedURL, bool m
Tuple.Create("/resources/search-icon-clear.svg",true)
};
- ///
- /// Creates and adds the library view to the WPF visual tree.
- /// Also loads the library.html and js files.
- ///
- /// LibraryView control
- internal void AddLibraryView()
- {
- LibraryViewModel model = new LibraryViewModel();
- LibraryView view = new LibraryView(model);
-
- var sidebarGrid = dynamoWindow.FindName("sidebarGrid") as Grid;
- sidebarGrid.Children.Add(view);
-
- browser = view.mainGrid.Children.OfType().FirstOrDefault();
- browser.Loaded += Browser_Loaded;
- browser.SizeChanged += Browser_SizeChanged;
-
- this.browser = view.mainGrid.Children.OfType().FirstOrDefault();
- InitializeAsync();
-
- LibraryViewController.SetupSearchModelEventsObserver(browser, dynamoViewModel.Model.SearchModel,
- this, this.customization);
- }
-
async void InitializeAsync()
{
try
@@ -422,6 +414,8 @@ private void Browser_Loaded(object sender, RoutedEventArgs e)
{
string msg = "Browser Loaded";
LogToDynamoConsole(msg);
+
+ InitializeAsync();
}
// This enum is for matching the modifier keys between C# and javaScript
diff --git a/src/LibraryViewExtensionWebView2/ViewExtension.cs b/src/LibraryViewExtensionWebView2/ViewExtension.cs
index 999e02b596d..69d679dfd27 100644
--- a/src/LibraryViewExtensionWebView2/ViewExtension.cs
+++ b/src/LibraryViewExtensionWebView2/ViewExtension.cs
@@ -1,4 +1,4 @@
-using System.Windows;
+using System.Windows;
using Dynamo.Models;
using Dynamo.ViewModels;
using Dynamo.Wpf.Extensions;
@@ -35,7 +35,6 @@ public void Loaded(ViewLoadedParams viewLoadedParams)
{
viewParams = viewLoadedParams;
controller = new LibraryViewController(viewLoadedParams.DynamoWindow, viewLoadedParams.CommandExecutive, customization);
- controller.AddLibraryView();
(viewLoadedParams.DynamoWindow.DataContext as DynamoViewModel).PropertyChanged += handleDynamoViewPropertyChanges;
}
diff --git a/src/Notifications/NotificationCenterController.cs b/src/Notifications/NotificationCenterController.cs
index ec5d2740cd1..c66e7cd415e 100644
--- a/src/Notifications/NotificationCenterController.cs
+++ b/src/Notifications/NotificationCenterController.cs
@@ -12,6 +12,7 @@
using Dynamo.Controls;
using Dynamo.Logging;
using Dynamo.Notifications.View;
+using DynamoUtilities;
using Dynamo.ViewModels;
using Dynamo.Wpf.ViewModels.Core;
using Newtonsoft.Json;
@@ -48,7 +49,7 @@ public void UpdateNotificationWindowSize(int height)
}
}
- public class NotificationCenterController
+ public class NotificationCenterController : IDisposable
{
private readonly NotificationUI notificationUIPopup;
private readonly DynamoView dynamoView;
@@ -64,6 +65,8 @@ public class NotificationCenterController
private static readonly string NotificationCenterButtonName = "notificationsButton";
internal DirectoryInfo webBrowserUserDataFolder;
+ internal AsyncMethodState initState = AsyncMethodState.NotStarted;
+
private readonly DynamoLogger logger;
private string jsonStringFile;
private NotificationsModel notificationsModel;
@@ -82,7 +85,6 @@ internal NotificationCenterController(DynamoView view, DynamoLogger dynLogger)
dynamoView.SizeChanged += DynamoView_SizeChanged;
dynamoView.LocationChanged += DynamoView_LocationChanged;
notificationsButton.Click += NotificationsButton_Click;
- dynamoView.Closing += View_Closing;
dynamoView.PreviewMouseDown += DynamoView_PreviewMouseDown;
notificationUIPopup = new NotificationUI
@@ -97,41 +99,14 @@ internal NotificationCenterController(DynamoView view, DynamoLogger dynLogger)
// If user turns on the feature, they will need to restart Dynamo to see the count
// This ensures no network traffic when Notification center feature is turned off
- if (dynamoViewModel.PreferenceSettings.EnableNotificationCenter && !dynamoViewModel.Model.NoNetworkMode )
- {
- InitializeBrowserAsync();
+ if (dynamoViewModel.PreferenceSettings.EnableNotificationCenter && !dynamoViewModel.Model.NoNetworkMode )
+ {
+ notificationUIPopup.webView.Loaded += InitializeBrowserAsync;
RequestNotifications();
}
}
- private void View_Closing(object sender, System.ComponentModel.CancelEventArgs e)
- {
- dynamoView.Closing -= View_Closing;
- dynamoView.PreviewMouseDown -= DynamoView_PreviewMouseDown;
- SuspendCoreWebviewAsync();
- }
-
- async void SuspendCoreWebviewAsync()
- {
- if (notificationUIPopup == null) return;
-
- notificationUIPopup.IsOpen = false;
- notificationUIPopup.webView.Visibility = Visibility.Hidden;
-
- if (notificationUIPopup.webView.CoreWebView2 != null)
- {
- notificationUIPopup.webView.CoreWebView2.Stop();
- notificationUIPopup.webView.CoreWebView2InitializationCompleted -= WebView_CoreWebView2InitializationCompleted;
- notificationUIPopup.webView.CoreWebView2.NewWindowRequested -= WebView_NewWindowRequested;
-
- dynamoView.SizeChanged -= DynamoView_SizeChanged;
- dynamoView.LocationChanged -= DynamoView_LocationChanged;
- notificationsButton.Click -= NotificationsButton_Click;
- notificationUIPopup.webView.NavigationCompleted -= WebView_NavigationCompleted;
- }
- }
-
- private void InitializeBrowserAsync()
+ private async void InitializeBrowserAsync(object sender, RoutedEventArgs e)
{
if (webBrowserUserDataFolder != null)
{
@@ -142,7 +117,10 @@ private void InitializeBrowserAsync()
};
}
notificationUIPopup.webView.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted;
- notificationUIPopup.webView.EnsureCoreWebView2Async();
+
+ initState = AsyncMethodState.Started;
+ await notificationUIPopup.webView.EnsureCoreWebView2Async();
+ initState = AsyncMethodState.Done;
}
private void WebView_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
@@ -328,5 +306,49 @@ public void RefreshNotifications(string url="") {
InvokeJS(@"window.RequestNotifications('" + DynamoUtilities.PathHelper.GetServiceBackendAddress(this, "notificationAddress") + "');");
}
}
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (dynamoView != null)
+ {
+ dynamoView.SizeChanged -= DynamoView_SizeChanged;
+ dynamoView.LocationChanged -= DynamoView_LocationChanged;
+ dynamoView.PreviewMouseDown -= DynamoView_PreviewMouseDown;
+ }
+
+ if (notificationUIPopup != null)
+ {
+ notificationUIPopup.IsOpen = false;
+ notificationsButton.Click -= NotificationsButton_Click;
+
+ if (notificationUIPopup.webView != null)
+ {
+ notificationUIPopup.webView.Visibility = Visibility.Hidden;
+ notificationUIPopup.webView.Loaded -= InitializeBrowserAsync;
+ notificationUIPopup.webView.NavigationCompleted -= WebView_NavigationCompleted;
+ notificationUIPopup.webView.CoreWebView2InitializationCompleted -= WebView_CoreWebView2InitializationCompleted;
+
+ if (notificationUIPopup.webView.CoreWebView2 != null)
+ {
+ notificationUIPopup.webView.CoreWebView2.Stop();
+ notificationUIPopup.webView.CoreWebView2.NewWindowRequested -= WebView_NewWindowRequested;
+ }
+ notificationUIPopup.webView.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Dispose function for DocumentationBrowser
+ ///
+ public void Dispose()
+ {
+ if (initState == AsyncMethodState.Started)
+ {
+ logger?.Log("NotificationCenterController is being disposed but async initialization is still not done");
+ }
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
}
diff --git a/src/Notifications/NotificationsViewExtension.cs b/src/Notifications/NotificationsViewExtension.cs
index 2840e073f23..d3e19b5b0ce 100644
--- a/src/Notifications/NotificationsViewExtension.cs
+++ b/src/Notifications/NotificationsViewExtension.cs
@@ -18,7 +18,7 @@ public class NotificationsViewExtension : IViewExtension, INotifyPropertyChanged
private Action notificationHandler;
private ObservableCollection notifications;
private bool disposed;
- private NotificationCenterController notificationCenterController;
+ internal NotificationCenterController notificationCenterController;
///
/// Notifications data collection. PropertyChanged event is raised to help dealing WPF bind dispose.
///
@@ -67,6 +67,8 @@ public void Dispose()
{
BindingOperations.ClearAllBindings(notificationsMenuItem.CountLabel);
}
+ notificationCenterController?.Dispose();
+ notificationCenterController = null;
notificationsMenuItem = null;
disposed = true;
}
@@ -115,6 +117,12 @@ public void Loaded(ViewLoadedParams viewStartupParams)
private void LoadNotificationCenter()
{
var dynamoView = viewLoadedParams.DynamoWindow as DynamoView;
+
+ if (notificationCenterController != null)
+ {
+ notificationCenterController.Dispose();
+ notificationCenterController = null;
+ }
notificationCenterController = new NotificationCenterController(dynamoView, logger);
}
diff --git a/src/Notifications/Properties/AssemblyInfo.cs b/src/Notifications/Properties/AssemblyInfo.cs
index f5edea27fe7..32c77894c8c 100644
--- a/src/Notifications/Properties/AssemblyInfo.cs
+++ b/src/Notifications/Properties/AssemblyInfo.cs
@@ -1,4 +1,5 @@
-using System.Reflection;
+using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -9,3 +10,4 @@
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0acedbf9-81ab-45ee-b5e5-0d036bce0a31")]
+[assembly:InternalsVisibleTo("DynamoCoreWpfTests")]
diff --git a/test/DynamoCoreTests/UnitTestBase.cs b/test/DynamoCoreTests/UnitTestBase.cs
index 425ddc00bfc..3b846660b00 100644
--- a/test/DynamoCoreTests/UnitTestBase.cs
+++ b/test/DynamoCoreTests/UnitTestBase.cs
@@ -64,6 +64,7 @@ public static string TestDirectory
[SetUp]
public virtual void Setup()
{
+ System.Console.WriteLine("Start test: " + TestContext.CurrentContext.Test.Name);
SetupDirectories();
if (assemblyHelper == null)
@@ -97,6 +98,7 @@ public virtual void Cleanup()
{
AppDomain.CurrentDomain.AssemblyResolve -= assemblyHelper.ResolveAssembly;
}
+ System.Console.WriteLine("Finished test: " + TestContext.CurrentContext.Test.Name);
}
public string GetNewFileNameOnTempPath(string fileExtension = "dyn")
diff --git a/test/DynamoCoreWpfTests/DynamoTestUIBase.cs b/test/DynamoCoreWpfTests/DynamoTestUIBase.cs
index 2f7cd01bfb6..dab89d012a1 100644
--- a/test/DynamoCoreWpfTests/DynamoTestUIBase.cs
+++ b/test/DynamoCoreWpfTests/DynamoTestUIBase.cs
@@ -40,6 +40,7 @@ protected string ExecutingDirectory
[SetUp]
public virtual void Start()
{
+ System.Console.WriteLine("Start test: " + TestContext.CurrentContext.Test.Name);
var assemblyPath = Assembly.GetExecutingAssembly().Location;
preloader = new Preloader(Path.GetDirectoryName(assemblyPath));
preloader.Preload();
@@ -78,7 +79,7 @@ public virtual void Start()
View = new DynamoView(ViewModel);
View.Show();
- SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
+ SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
}
protected static void RaiseLoadedEvent(FrameworkElement element)
@@ -138,6 +139,7 @@ public void Exit()
{
Console.WriteLine(ex.StackTrace);
}
+ System.Console.WriteLine("Finished test: " + TestContext.CurrentContext.Test.Name);
}
protected virtual void GetLibrariesToPreload(List libraries)
diff --git a/test/DynamoCoreWpfTests/DynamoViewModelUnitTest.cs b/test/DynamoCoreWpfTests/DynamoViewModelUnitTest.cs
index fe782319b6a..8705685c2ef 100644
--- a/test/DynamoCoreWpfTests/DynamoViewModelUnitTest.cs
+++ b/test/DynamoCoreWpfTests/DynamoViewModelUnitTest.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -185,4 +185,4 @@ protected void RunCurrentModel() // Run currently loaded model.
Assert.DoesNotThrow(() => ViewModel.HomeSpace.Run());
}
}
-}
\ No newline at end of file
+}
diff --git a/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs b/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs
index b24af6612a0..563f0f50a65 100644
--- a/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs
+++ b/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs
@@ -1262,8 +1262,12 @@ public void PackageManagerDownloadsBeforeInstalling()
.Returns(MessageBoxResult.OK);
MessageBoxService.OverrideMessageBoxDuringTests(dlgMock.Object);
- //actually perform the download & install operations
- pmVmMock.Object.ExecutePackageDownload(id, pkgVer, "");
+ Task.Run(() => {
+ // This test runs on a single thread (using DispatcherSyncronizationContext)
+ // We need to run the async ExecutePackageDownload function in a separate thread so that the Thread.Sleep does not mess up the other awaiting tasks.
+ //actually perform the download & install operations
+ pmVmMock.Object.ExecutePackageDownload(id, pkgVer, "");
+ }).Wait();
//wait a bit.
Thread.Sleep(500);
diff --git a/test/DynamoCoreWpfTests/PythonNodeCustomizationTests.cs b/test/DynamoCoreWpfTests/PythonNodeCustomizationTests.cs
index 6a739b33db4..cc825e1e8da 100644
--- a/test/DynamoCoreWpfTests/PythonNodeCustomizationTests.cs
+++ b/test/DynamoCoreWpfTests/PythonNodeCustomizationTests.cs
@@ -12,6 +12,7 @@
using Dynamo.PythonServices;
using Dynamo.Tests;
using Dynamo.Utilities;
+using Dynamo.ViewModels;
using DynamoCoreWpfTests.Utility;
using ICSharpCode.AvalonEdit.Document;
using NUnit.Framework;
@@ -20,7 +21,7 @@
namespace DynamoCoreWpfTests
{
- [TestFixture, Category("Failure")]
+ [TestFixture]
public class PythonNodeCustomizationTests : DynamoTestUIBase
{
bool bTextEnteringEventRaised = false;
@@ -49,6 +50,19 @@ private static DependencyObject GetInfoBubble(DependencyObject parent)
return null;
}
+ private void WaitForDocumentationBrowserInitialization()
+ {
+ var docBrowserviewExtension = this.View.viewExtensionManager.ViewExtensions.OfType().FirstOrDefault();
+
+ // Wait for the DocumentationBrowserView webview2 control to finish initialization
+ DispatcherUtil.DoEventsLoop(() =>
+ {
+ return docBrowserviewExtension.BrowserView.initState == DynamoUtilities.AsyncMethodState.Done;
+ });
+
+ Assert.IsTrue(docBrowserviewExtension.BrowserView.initState == DynamoUtilities.AsyncMethodState.Done);
+ }
+
public override void Open(string path)
{
base.Open(path);
@@ -276,7 +290,7 @@ public void OnMoreInfoEventTest()
//Pressing the MoreInfo button, here the OnMoreInfoEvent is raised
moreInfoButton.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));
- DispatcherUtil.DoEvents();
+ WaitForDocumentationBrowserInitialization();
//Check that now we are showing a extension tab (DocumentationBrowser)
Assert.That(this.ViewModel.SideBarTabItems.Count, Is.EqualTo(1));
@@ -315,8 +329,7 @@ public void OpenPythonLearningMaterialValidationTest()
//Click the button and internally the OpenPythonLearningMaterial method is executed
learnMoreMenuItem.RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent));
-
- DispatcherUtil.DoEvents();
+ WaitForDocumentationBrowserInitialization();
var learnMoreTab = this.ViewModel.SideBarTabItems
.Where(x => x.Content.GetType().Equals(typeof(DocumentationBrowserView)))
@@ -349,8 +362,7 @@ public void OpenPythonLearningMaterial_PythonNodeFromStringValidationTest()
//Click the button and internally the OpenPythonLearningMaterial method is executed
learnMoreMenuItem.RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent));
-
- DispatcherUtil.DoEvents();
+ WaitForDocumentationBrowserInitialization();
var learnMoreTab = this.ViewModel.SideBarTabItems
.Where(x => x.Content.GetType().Equals(typeof(DocumentationBrowserView)))
diff --git a/test/DynamoCoreWpfTests/Utility/DispatcherUtil.cs b/test/DynamoCoreWpfTests/Utility/DispatcherUtil.cs
index 9f12de81776..b7e21cddb75 100644
--- a/test/DynamoCoreWpfTests/Utility/DispatcherUtil.cs
+++ b/test/DynamoCoreWpfTests/Utility/DispatcherUtil.cs
@@ -1,9 +1,11 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Permissions;
using System.Windows.Threading;
+using System.Threading;
+using NUnit.Framework;
namespace DynamoCoreWpfTests.Utility
{
@@ -22,11 +24,39 @@ public static void DoEvents()
}
///
- /// Helper method for DispatcherUtil
+ /// Force the Dispatcher to empty it's queue every 100 ms for a maximum 4 seconds or until
+ /// the check function returns true.
///
- ///
- ///
- private static object ExitFrame(object frame)
+ /// When check returns true, the even loop is stopped.
+ [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static void DoEventsLoop(Func check = null)
+ {
+ const int max_count = 40;
+
+ int count = 0;
+ while (true)
+ {
+ if (check != null && check())
+ {
+ return;
+ }
+ if (count >= max_count)
+ {
+ return;
+ }
+
+ DispatcherUtil.DoEvents();
+ Thread.Sleep(100);
+ count++;
+ }
+ }
+
+ ///
+ /// Helper method for DispatcherUtil
+ ///
+ ///
+ ///
+ private static object ExitFrame(object frame)
{
((DispatcherFrame)frame).Continue = false;
return null;
diff --git a/test/DynamoCoreWpfTests/ViewExtensions/DocumentationBrowserViewExtensionTests.cs b/test/DynamoCoreWpfTests/ViewExtensions/DocumentationBrowserViewExtensionTests.cs
index 6d3e73e72d8..e3abc8f4ee7 100644
--- a/test/DynamoCoreWpfTests/ViewExtensions/DocumentationBrowserViewExtensionTests.cs
+++ b/test/DynamoCoreWpfTests/ViewExtensions/DocumentationBrowserViewExtensionTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -7,7 +6,6 @@
using System.Reflection;
using System.Text;
using System.Threading;
-using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Dynamo;
@@ -20,6 +18,7 @@
using Dynamo.ViewModels;
using Dynamo.Wpf.Extensions;
using DynamoCoreWpfTests.Utility;
+using DynamoUtilities;
using NUnit.Framework;
namespace DynamoCoreWpfTests
@@ -80,6 +79,8 @@ public void ClickingMenuItemLaunchesSidebarWithIndexContent()
// simulate clicking the Show docs browser menu item
ShowDocsBrowser();
+ WaitForWebView2Initialization();
+
// confirm the extension loads a view into the sidebar
// and get the html content inside
var docsTab = GetDocsTabItem();
@@ -96,6 +97,9 @@ public void ShowingStartPageHidesBrowser()
{
// Arrange
ShowDocsBrowser();
+
+ WaitForWebView2Initialization();
+
var docsView = GetDocsTabItem().Content as DocumentationBrowserView;
this.ViewModel.NewHomeWorkspaceCommand.Execute(null);
var visibilityBeforeShowStartPageEvent = docsView.documentationBrowser.Visibility;
@@ -136,11 +140,10 @@ public void CanCreatePackageNodeDocumentationAndLoadImages()
);
var node = this.ViewModel.Model.CurrentWorkspace.Nodes.FirstOrDefault();
- node.Name = nodeRename; // Forces original name header to appear
- var nodeAnnotationEventArgs = new OpenNodeAnnotationEventArgs(node, this.ViewModel);
+ node.Name = nodeRename;// Forces original name header to appear
+
+ var htmlContent = RequestNodeDocs(node);
- docBrowserviewExtension.HandleRequestOpenDocumentationLink(nodeAnnotationEventArgs);
- var htmlContent = GetSidebarDocsBrowserContents();
//There are times in which the URL contain characters like backslash (%5C) then they need to be replaced by the normal slash "/"
htmlContent = htmlContent.Replace(@"%5C", "/");
@@ -162,6 +165,9 @@ public void ViewExtensionIgnoresExternalEvents()
// Act
var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
viewExtension.HandleRequestOpenDocumentationLink(externalEvent);
+ DispatcherUtil.DoEventsLoop();
+
+ Assert.AreEqual(AsyncMethodState.NotStarted, viewExtension.BrowserView.initState);
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
// Assert
@@ -180,9 +186,8 @@ public void CanHandleDocsEventWithValidLink()
{
// Act
var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
// Assert
Assert.IsFalse(docsEvent.IsRemoteResource);
@@ -220,9 +225,8 @@ public void Displays404PageOnMissingDocFile()
{
// Act
var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
// Assert
Assert.IsFalse(docsEvent.IsRemoteResource);
@@ -246,9 +250,9 @@ public void DisplaysHtmlEmbeddedInLoadedAssemblies()
// Act
var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
+
// Assert
Assert.IsFalse(docsEvent.IsRemoteResource);
@@ -273,9 +277,9 @@ public void Displays404PageWhenLinkPointsToAssemblyThatCannotBeFound()
// Act
var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
+
// Assert
Assert.IsFalse(docsEvent.IsRemoteResource);
@@ -306,9 +310,7 @@ public void DisplaysLocalizedContentWhenAvailable()
var uri = $"{assemblyName};{fileName}";
var docsEvent = new OpenDocumentationLinkEventArgs(new Uri(uri, UriKind.Relative));
- // Act
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
- var htmlContent = GetSidebarDocsBrowserContents();
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
// Assert
StringAssert.Contains("Division by zero - en-us
", htmlContent);
@@ -340,9 +342,7 @@ public void DisplayNeutralCultureContentWhenSpecificCultureContentIsNotAvailable
var uri = $"{assemblyName};{fileName}";
var docsEvent = new OpenDocumentationLinkEventArgs(new Uri(uri, UriKind.Relative));
- // Act
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
- var htmlContent = GetSidebarDocsBrowserContents();
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
// Assert
StringAssert.Contains("Division entre cero - es
", htmlContent);
@@ -374,9 +374,7 @@ public void DisplaySpecificCultureContentWhenNeutralCultureContentIsNotAvailable
var uri = $"{assemblyName};{fileName}";
var docsEvent = new OpenDocumentationLinkEventArgs(new Uri(uri, UriKind.Relative));
- // Act
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
- var htmlContent = GetSidebarDocsBrowserContents();
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
// Assert
StringAssert.Contains("Division by zero - en-us
", htmlContent);
@@ -409,9 +407,7 @@ public void DisplaysInvariantContentWhenNoCompatibleLocalizedContentIsAvailable(
var uri = $"{assemblyName};{fileName}";
var docsEvent = new OpenDocumentationLinkEventArgs(new Uri(uri, UriKind.Relative));
- // Act
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
- var htmlContent = GetSidebarDocsBrowserContents();
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
// Assert
StringAssert.Contains("Division by zero - invariant
", htmlContent);
@@ -437,9 +433,9 @@ public void RemovesScriptTagsFromLoadedHtml()
// Act
var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
+
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
// Assert
Assert.IsFalse(docsEvent.IsRemoteResource);
@@ -464,9 +460,8 @@ public void DPIScriptExists()
// Act
var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
+ var htmlContent = RequestDocLink(viewExtension, docsEvent);
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
// Assert
Assert.IsFalse(docsEvent.IsRemoteResource);
@@ -511,21 +506,21 @@ public void CanCreateNodeDocumenationHtmlFromNodeAnnotationEventArgsWithOOTBNode
var nodeRename = "New node name";
var expectedNodeDocumentationTitle = $"{nodeRename}
";
var expectedNodeDocumentationNamespace = $"{nodeName}
";
-
+
// Act
this.ViewModel.ExecuteCommand(
new DynamoModel.CreateNodeCommand(
Guid.NewGuid().ToString(), nodeName, 0, 0, false, false)
);
+ var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
+
var node = this.ViewModel.Model.CurrentWorkspace.Nodes.FirstOrDefault();
- node.Name = nodeRename; // Forces original name header to appear
- var nodeAnnotationEventArgs = new OpenNodeAnnotationEventArgs(node, this.ViewModel);
+ node.Name = nodeRename; // Forces original name header to appear
+
+ var htmlContent = RequestNodeDocs(node);
- var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- docBrowserviewExtension.HandleRequestOpenDocumentationLink(nodeAnnotationEventArgs);
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
// Assert
Assert.AreEqual(0, tabsBeforeExternalEventTrigger);
@@ -565,14 +560,16 @@ public void CanCreateNodeDocumenationHtmlFromNodeAnnotationEventArgsWithPackageN
Guid.NewGuid().ToString(), nodeName, 0, 0, false, false)
);
+ var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
+
var node = this.ViewModel.Model.CurrentWorkspace.Nodes.FirstOrDefault();
+
node.Name = nodeRename; // Forces original name header to appear
- var nodeAnnotationEventArgs = new OpenNodeAnnotationEventArgs(node, this.ViewModel);
- var tabsBeforeExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- docBrowserviewExtension.HandleRequestOpenDocumentationLink(nodeAnnotationEventArgs);
+ var htmlContent = RequestNodeDocs(node);
+
var tabsAfterExternalEventTrigger = this.ViewModel.SideBarTabItems.Count;
- var htmlContent = GetSidebarDocsBrowserContents();
+
htmlContent = htmlContent.Replace(@"%5C", "/");
// Assert
@@ -630,8 +627,8 @@ public void DocsCanBeLoadedForDSNonPackageNodesFrom_FallBackPath()
new DynamoModel.CreateNodeCommand(
Guid.NewGuid().ToString(), nodeName, 0, 0, false, false)
);
- node = this.ViewModel.Model.CurrentWorkspace.Nodes.FirstOrDefault();
- htmlContent = RequestNodeDocs(node);
+ node = this.ViewModel.Model.CurrentWorkspace.Nodes.FirstOrDefault();
+ htmlContent = RequestNodeDocs(node);
Assert.IsTrue(htmlContent.Contains("loopwhile sample docs"));
}
@@ -657,7 +654,7 @@ public void DocsAreLoadedFromHostPathBeforeCorePath()
var htmlContent = RequestNodeDocs(node);
Assert.IsTrue(htmlContent.Contains("list.rank sample docs from host path"));
- }
+ }
[Test]
public void DocsCanBeLoadedForCoreNodeModelNodesFrom_FallBackPath()
@@ -711,10 +708,22 @@ private string RequestNodeDocs(Dynamo.Graph.Nodes.NodeModel node)
var docBrowserviewExtension = this.View.viewExtensionManager.ViewExtensions.OfType().FirstOrDefault();
var nodeAnnotationEventArgs = new OpenNodeAnnotationEventArgs(node, this.ViewModel);
docBrowserviewExtension.HandleRequestOpenDocumentationLink(nodeAnnotationEventArgs);
+
+ WaitForWebView2Initialization(docBrowserviewExtension.BrowserView);
+
+ return GetSidebarDocsBrowserContents();
+ }
+
+ private string RequestDocLink(DocumentationBrowserViewExtension viewExtension, OpenDocumentationLinkEventArgs docsEvent)
+ {
+ viewExtension.HandleRequestOpenDocumentationLink(docsEvent);
+
+ WaitForWebView2Initialization(viewExtension.BrowserView);
+
return GetSidebarDocsBrowserContents();
}
- [Test,Category("Failure")]
+ [Test]
public void AddGraphInSpecificLocationToWorkspace()
{
//TODO see this issue:
@@ -752,7 +761,7 @@ public void AddGraphInSpecificLocationToWorkspace()
Assert.AreEqual(ViewModel.Model.CurrentWorkspace.Nodes.Count(), 1);
var node = ViewModel.Model.CurrentWorkspace.Nodes.FirstOrDefault();
-
+
RequestNodeDocs(node);
var docsView = GetDocsTabItem().Content as DocumentationBrowserView;
var docsViewModel = docsView.DataContext as DocumentationBrowserViewModel;
@@ -784,8 +793,6 @@ public void Validate_GetGraphLinkFromMDLocation()
//In this call the GetGraphLinkFromMDLocation() method is executed internally
RequestNodeDocs(node);
- // Show the DocumentationBrowser so we can get the DocumentationBrowserViewModel
- ShowDocsBrowser();
var docsView = GetDocsTabItem().Content as DocumentationBrowserView;
var docsViewModel = docsView.DataContext as DocumentationBrowserViewModel;
@@ -821,8 +828,6 @@ public void Validate_GetGraphLinkFromPackage()
//In this call the GetGraphLinkFromMDLocation() method is executed internally
RequestNodeDocs(node);
- // Show the DocumentationBrowser so we can get the DocumentationBrowserViewModel
- ShowDocsBrowser();
var docsView = GetDocsTabItem().Content as DocumentationBrowserView;
var docsViewModel = docsView.DataContext as DocumentationBrowserViewModel;
@@ -834,7 +839,7 @@ public void Validate_GetGraphLinkFromPackage()
Assert.IsTrue(!string.IsNullOrEmpty(graphPathValue));
//check that the path contains "packageWithDocumentation"
- Assert.That(graphPathValue.Contains(Path.Combine("PackageWithNodeDocumentation","doc",dynFileName)));
+ Assert.That(graphPathValue.Contains(Path.Combine("PackageWithNodeDocumentation", "doc", dynFileName)));
}
#region Helpers
@@ -865,10 +870,28 @@ private List