From 264b8e8f3f9d64b394bef510f1d4e7cba9dfe94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Sat, 17 Aug 2024 23:58:46 +0200 Subject: [PATCH 1/4] Preserve other hisory.state properties when storing the viewmodel on page unload --- .../Framework/Resources/Scripts/dotvvm-base.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts b/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts index 6abad6881b..1d42ba76c2 100644 --- a/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts +++ b/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts @@ -69,6 +69,10 @@ export function getStateManager(): StateManager { return getCoreS let initialViewModelWrapper: any; +function isBackForwardNavigation() { + return (performance.getEntriesByType?.("navigation").at(-1) as PerformanceNavigationTiming)?.type == "back_forward"; +} + export function initCore(culture: string): void { if (currentCoreState) { throw new Error("DotVVM is already loaded"); @@ -124,8 +128,10 @@ const getViewModelStorageElement = () => document.getElementById("__dot_viewmodel_root") function persistViewModel() { - const viewModel = getState() - const persistedViewModel = { ...initialViewModelWrapper, viewModel }; - - getViewModelStorageElement().value = JSON.stringify(persistedViewModel); + history.replaceState({ + ...history.state, + viewModel: { ...initialViewModelWrapper, viewModel: getState() } + }, "") + // avoid storing the viewmodel hidden field, as Firefox would also reuse it on page reloads + getViewModelStorageElement()?.remove() } From 5f9f48a4ecb2a3a059b0893bee3e829424d7416c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Sat, 7 Sep 2024 09:53:49 +0200 Subject: [PATCH 2/4] JS: restore last viewmodel from history API --- src/Framework/Framework/Resources/Scripts/dotvvm-base.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts b/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts index 1d42ba76c2..1d1d1a5eba 100644 --- a/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts +++ b/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts @@ -79,7 +79,9 @@ export function initCore(culture: string): void { } // load the viewmodel - const thisViewModel = initialViewModelWrapper = JSON.parse(getViewModelStorageElement().value); + const thisViewModel = initialViewModelWrapper = + (isBackForwardNavigation() ? history.state?.viewModel : null) ?? + JSON.parse(getViewModelStorageElement().value); resourceLoader.registerResources(thisViewModel.renderedResources) From 05b955471f4aa174d9a929434f9522244a59a0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Sat, 7 Sep 2024 10:02:06 +0200 Subject: [PATCH 3/4] Add UI test for back navigaton VM restore --- .../Tests/Tests/Complex/TaskListTests.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/Samples/Tests/Tests/Complex/TaskListTests.cs b/src/Samples/Tests/Tests/Complex/TaskListTests.cs index 09fd44c01d..2e1776e050 100644 --- a/src/Samples/Tests/Tests/Complex/TaskListTests.cs +++ b/src/Samples/Tests/Tests/Complex/TaskListTests.cs @@ -62,5 +62,46 @@ public void Complex_TaskList_ServerRenderedTaskList() "Last task is not marked as completed."); }); } + + [Fact] + public void Complex_TaskList_TaskListAsyncCommands_ViewModelRestore() + { + // view model should be restored after back/forward navigation, but not on refresh + RunInAllBrowsers(browser => + { + browser.NavigateToUrl("/"); + browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_TaskList_TaskListAsyncCommands); + + browser.SendKeys("input[type=text]", "test1"); + browser.Click("input[type=button]"); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + + browser.NavigateBack(); + browser.WaitUntilDotvvmInited(); + browser.NavigateForward(); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + + browser.Refresh(); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); + + browser.SendKeys("input[type=text]", "test2"); + browser.Click("input[type=button]"); + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + + browser.NavigateToUrl("/"); + browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_TaskList_TaskListAsyncCommands); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); + + browser.NavigateBack(); + browser.WaitUntilDotvvmInited(); + browser.NavigateBack(); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + }); + } } } From 780c9d1ab72978304056c4b4e6e3db5c61997d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg=20=28RIGANTI=20s=2Er=2Eo=2E=29?= Date: Sat, 7 Sep 2024 16:42:40 +0200 Subject: [PATCH 4/4] Fixed failing UI test --- src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs b/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs index 79c468a479..7a47b04a0c 100644 --- a/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs +++ b/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs @@ -84,7 +84,7 @@ public void Feature_FormControlsEnabled_FormControlsEnabled() private void TestLinkButton(IBrowserWrapper browser, string id, bool shouldBeEnabled, ref int currentPresses) { - browser.First($"#{id}").Click(); + browser.First($"#{id}").ScrollTo().Wait(500).Click(); if (shouldBeEnabled) { currentPresses++;