|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements.
|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license.
|
3 | 3 |
|
4 |
| -using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; |
| 4 | +using System.Threading.Tasks; |
| 5 | +using Components.TestServer.RazorComponents; |
| 6 | +using Microsoft.AspNetCore.Components.E2ETest; |
5 | 7 | using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
|
| 8 | +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; |
6 | 9 | using Microsoft.AspNetCore.E2ETesting;
|
7 |
| -using TestServer; |
8 |
| -using Xunit.Abstractions; |
9 |
| -using Components.TestServer.RazorComponents; |
10 | 10 | using OpenQA.Selenium;
|
| 11 | +using OpenQA.Selenium.BiDi.Communication; |
11 | 12 | using OpenQA.Selenium.Support.Extensions;
|
| 13 | +using TestServer; |
| 14 | +using Xunit.Abstractions; |
| 15 | +using Xunit.Sdk; |
12 | 16 |
|
13 | 17 | namespace Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests;
|
14 | 18 |
|
@@ -662,6 +666,203 @@ public void CanUpdateHrefOnLinkTagWithIntegrity()
|
662 | 666 | Browser.Equal("rgba(0, 0, 255, 1)", () => originalH1Elem.GetCssValue("color"));
|
663 | 667 | }
|
664 | 668 |
|
| 669 | + [Theory] |
| 670 | + [InlineData(false, false, false)] |
| 671 | + [InlineData(false, true, false)] |
| 672 | + [InlineData(true, true, false)] |
| 673 | + [InlineData(true, false, false)] |
| 674 | + // [InlineData(false, false, true)] programmatic navigation doesn't work without enhanced navigation |
| 675 | + [InlineData(false, true, true)] |
| 676 | + [InlineData(true, true, true)] |
| 677 | + // [InlineData(true, false, true)] programmatic navigation doesn't work without enhanced navigation |
| 678 | + public void EnhancedNavigationScrollBehavesSameAsBrowserOnNavigation(bool enableStreaming, bool useEnhancedNavigation, bool programmaticNavigation) |
| 679 | + { |
| 680 | + // This test checks if the navigation to another path moves the scroll to the top of the page, |
| 681 | + // or to the beginning of a fragment, regardless of the previous scroll position |
| 682 | + string landingPageSuffix = enableStreaming ? "" : "-no-streaming"; |
| 683 | + string buttonKeyword = programmaticNavigation ? "-programmatic" : ""; |
| 684 | + Navigate($"{ServerPathBase}/nav/scroll-test{landingPageSuffix}"); |
| 685 | + EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, shouldSuppress: !useEnhancedNavigation, skipNavigation: true); |
| 686 | + |
| 687 | + // "landing" page: scroll maximally down and go to "next" page - we should land at the top of that page |
| 688 | + AssertWeAreOnLandingPage(); |
| 689 | + |
| 690 | + // staleness check is used to assert enhanced navigation is enabled/disabled, as requested |
| 691 | + var elementForStalenessCheckOnNextPage = Browser.Exists(By.TagName("html")); |
| 692 | + |
| 693 | + var button1Id = $"do{buttonKeyword}-navigation"; |
| 694 | + var button1Pos = Browser.GetElementPositionWithRetry(button1Id); |
| 695 | + Browser.SetScrollY(button1Pos); |
| 696 | + Browser.Exists(By.Id(button1Id)).Click(); |
| 697 | + |
| 698 | + // "next" page: check if we landed at 0, then navigate to "landing" |
| 699 | + AssertWeAreOnNextPage(); |
| 700 | + WaitStreamingRendersFullPage(enableStreaming); |
| 701 | + string fragmentId = "some-content"; |
| 702 | + Browser.WaitForElementToBeVisible(By.Id(fragmentId)); |
| 703 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); |
| 704 | + Assert.Equal(0, Browser.GetScrollY()); |
| 705 | + var elementForStalenessCheckOnLandingPage = Browser.Exists(By.TagName("html")); |
| 706 | + var fragmentScrollPosition = Browser.GetElementPositionWithRetry(fragmentId); |
| 707 | + Browser.Exists(By.Id(button1Id)).Click(); |
| 708 | + |
| 709 | + // "landing" page: navigate to a fragment on another page - we should land at the beginning of the fragment |
| 710 | + AssertWeAreOnLandingPage(); |
| 711 | + WaitStreamingRendersFullPage(enableStreaming); |
| 712 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnLandingPage); |
| 713 | + |
| 714 | + var button2Id = $"do{buttonKeyword}-navigation-with-fragment"; |
| 715 | + Browser.Exists(By.Id(button2Id)).Click(); |
| 716 | + AssertWeAreOnNextPage(); |
| 717 | + WaitStreamingRendersFullPage(enableStreaming); |
| 718 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); |
| 719 | + var expectedFragmentScrollPosition = fragmentScrollPosition - 1; |
| 720 | + Assert.Equal(expectedFragmentScrollPosition, Browser.GetScrollY()); |
| 721 | + } |
| 722 | + |
| 723 | + [Theory] |
| 724 | + [InlineData(false, false, false)] |
| 725 | + [InlineData(false, true, false)] |
| 726 | + [InlineData(true, true, false)] |
| 727 | + [InlineData(true, false, false)] |
| 728 | + // [InlineData(false, false, true)] programmatic navigation doesn't work without enhanced navigation |
| 729 | + [InlineData(false, true, true)] |
| 730 | + [InlineData(true, true, true)] |
| 731 | + // [InlineData(true, false, true)] programmatic navigation doesn't work without enhanced navigation |
| 732 | + public void EnhancedNavigationScrollBehavesSameAsBrowserOnBackwardsForwardsAction(bool enableStreaming, bool useEnhancedNavigation, bool programmaticNavigation) |
| 733 | + { |
| 734 | + // This test checks if the scroll position is preserved after backwards/forwards action |
| 735 | + string landingPageSuffix = enableStreaming ? "" : "-no-streaming"; |
| 736 | + string buttonKeyword = programmaticNavigation ? "-programmatic" : ""; |
| 737 | + Navigate($"{ServerPathBase}/nav/scroll-test{landingPageSuffix}"); |
| 738 | + EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, shouldSuppress: !useEnhancedNavigation, skipNavigation: true); |
| 739 | + |
| 740 | + // "landing" page: scroll to pos1, navigate away |
| 741 | + AssertWeAreOnLandingPage(); |
| 742 | + WaitStreamingRendersFullPage(enableStreaming); |
| 743 | + |
| 744 | + // staleness check is used to assert enhanced navigation is enabled/disabled, as requested |
| 745 | + var elementForStalenessCheckOnNextPage = Browser.Exists(By.TagName("html")); |
| 746 | + |
| 747 | + var buttonId = $"do{buttonKeyword}-navigation"; |
| 748 | + Browser.WaitForElementToBeVisible(By.Id(buttonId)); |
| 749 | + var landingPagePos1 = Browser.GetElementPositionWithRetry(buttonId) - 100; |
| 750 | + Browser.SetScrollY(landingPagePos1); |
| 751 | + Browser.Exists(By.Id(buttonId)).Click(); |
| 752 | + |
| 753 | + // "next" page: scroll to pos1, navigate away |
| 754 | + AssertWeAreOnNextPage(); |
| 755 | + WaitStreamingRendersFullPage(enableStreaming); |
| 756 | + Browser.WaitForElementToBeVisible(By.Id(buttonId)); |
| 757 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); |
| 758 | + var elementForStalenessCheckOnLandingPage = Browser.Exists(By.TagName("html")); |
| 759 | + var nextPagePos1 = Browser.GetElementPositionWithRetry(buttonId) - 100; |
| 760 | + // make sure we are expecting different scroll positions on the 1st and the 2nd page |
| 761 | + Assert.NotEqual(landingPagePos1, nextPagePos1); |
| 762 | + Browser.SetScrollY(nextPagePos1); |
| 763 | + Browser.Exists(By.Id(buttonId)).Click(); |
| 764 | + |
| 765 | + // "landing" page: scroll to pos2, go backwards |
| 766 | + AssertWeAreOnLandingPage(); |
| 767 | + WaitStreamingRendersFullPage(enableStreaming); |
| 768 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnLandingPage); |
| 769 | + var landingPagePos2 = 500; |
| 770 | + Browser.SetScrollY(landingPagePos2); |
| 771 | + Browser.Navigate().Back(); |
| 772 | + |
| 773 | + // "next" page: check if we landed on pos1, move the scroll to pos2, go backwards |
| 774 | + AssertWeAreOnNextPage(); |
| 775 | + WaitStreamingRendersFullPage(enableStreaming); |
| 776 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); |
| 777 | + AssertScrollPositionCorrect(useEnhancedNavigation, nextPagePos1); |
| 778 | + |
| 779 | + var nextPagePos2 = 600; |
| 780 | + Browser.SetScrollY(nextPagePos2); |
| 781 | + Browser.Navigate().Back(); |
| 782 | + |
| 783 | + // "landing" page: check if we landed on pos1, move the scroll to pos3, go forwards |
| 784 | + AssertWeAreOnLandingPage(); |
| 785 | + WaitStreamingRendersFullPage(enableStreaming); |
| 786 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnLandingPage); |
| 787 | + AssertScrollPositionCorrect(useEnhancedNavigation, landingPagePos1); |
| 788 | + var landingPagePos3 = 700; |
| 789 | + Browser.SetScrollY(landingPagePos3); |
| 790 | + Browser.Navigate().Forward(); |
| 791 | + |
| 792 | + // "next" page: check if we landed on pos1, go forwards |
| 793 | + AssertWeAreOnNextPage(); |
| 794 | + WaitStreamingRendersFullPage(enableStreaming); |
| 795 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); |
| 796 | + AssertScrollPositionCorrect(useEnhancedNavigation, nextPagePos2); |
| 797 | + Browser.Navigate().Forward(); |
| 798 | + |
| 799 | + // "scroll" page: check if we landed on pos2 |
| 800 | + AssertWeAreOnLandingPage(); |
| 801 | + WaitStreamingRendersFullPage(enableStreaming); |
| 802 | + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnLandingPage); |
| 803 | + AssertScrollPositionCorrect(useEnhancedNavigation, landingPagePos2); |
| 804 | + } |
| 805 | + |
| 806 | + private void AssertScrollPositionCorrect(bool useEnhancedNavigation, long previousScrollPosition) |
| 807 | + { |
| 808 | + // from some reason, scroll position sometimes differs by 1 pixel between enhanced and browser's navigation |
| 809 | + // browser's navigation is not precisely going backwards/forwards to the previous state |
| 810 | + var currentScrollPosition = Browser.GetScrollY(); |
| 811 | + string messagePart = useEnhancedNavigation ? $"{previousScrollPosition}" : $"{previousScrollPosition} or {previousScrollPosition - 1}"; |
| 812 | + bool isPreciselyWhereItWasLeft = currentScrollPosition == previousScrollPosition; |
| 813 | + bool isPixelLowerThanItWasLeft = currentScrollPosition == (previousScrollPosition - 1); |
| 814 | + bool success = useEnhancedNavigation |
| 815 | + ? isPreciselyWhereItWasLeft |
| 816 | + : (isPreciselyWhereItWasLeft || isPixelLowerThanItWasLeft); |
| 817 | + Assert.True(success, $"The expected scroll position was {messagePart}, but it was found at {currentScrollPosition}."); |
| 818 | + } |
| 819 | + |
| 820 | + private void AssertEnhancedNavigation(bool useEnhancedNavigation, IWebElement elementForStalenessCheck, int retryCount = 3, int delayBetweenRetriesMs = 100) |
| 821 | + { |
| 822 | + bool enhancedNavigationDetected = false; |
| 823 | + for (int i = 0; i < retryCount; i++) |
| 824 | + { |
| 825 | + try |
| 826 | + { |
| 827 | + enhancedNavigationDetected = !IsElementStale(elementForStalenessCheck); |
| 828 | + Assert.Equal(useEnhancedNavigation, enhancedNavigationDetected); |
| 829 | + return; |
| 830 | + } |
| 831 | + catch (XunitException) |
| 832 | + { |
| 833 | + // Maybe the check was done too early to change the DOM ref, retry |
| 834 | + } |
| 835 | + |
| 836 | + Thread.Sleep(delayBetweenRetriesMs); |
| 837 | + } |
| 838 | + string expectedNavigation = useEnhancedNavigation ? "enhanced navigation" : "browser navigation"; |
| 839 | + string isStale = enhancedNavigationDetected ? "is not stale" : "is stale"; |
| 840 | + var isNavigationSupressed = (string)((IJavaScriptExecutor)Browser).ExecuteScript("return sessionStorage.getItem('suppress-enhanced-navigation');"); |
| 841 | + throw new Exception($"Expected to use {expectedNavigation} because 'suppress-enhanced-navigation' is set to {isNavigationSupressed} but the element from previous path {isStale}"); |
| 842 | + } |
| 843 | + |
| 844 | + private void AssertWeAreOnLandingPage() |
| 845 | + { |
| 846 | + string infoName = "test-info-1"; |
| 847 | + Browser.WaitForElementToBeVisible(By.Id(infoName), timeoutInSeconds: 20); |
| 848 | + Browser.Equal("Scroll tests landing page", () => Browser.Exists(By.Id(infoName)).Text); |
| 849 | + } |
| 850 | + |
| 851 | + private void AssertWeAreOnNextPage() |
| 852 | + { |
| 853 | + string infoName = "test-info-2"; |
| 854 | + Browser.WaitForElementToBeVisible(By.Id(infoName), timeoutInSeconds: 20); |
| 855 | + Browser.Equal("Scroll tests next page", () => Browser.Exists(By.Id(infoName)).Text); |
| 856 | + } |
| 857 | + |
| 858 | + private void WaitStreamingRendersFullPage(bool enableStreaming) |
| 859 | + { |
| 860 | + if (enableStreaming) |
| 861 | + { |
| 862 | + Browser.WaitForElementToBeVisible(By.Id("some-content")); |
| 863 | + } |
| 864 | + } |
| 865 | + |
665 | 866 | private void AssertEnhancedUpdateCountEquals(long count)
|
666 | 867 | => Browser.Equal(count, () => ((IJavaScriptExecutor)Browser).ExecuteScript("return window.enhancedPageUpdateCount;"));
|
667 | 868 |
|
|
0 commit comments