Problem/Motivation

With the advent of smarter UIs that can reload only a portion of the page without a full refresh, it becomes necessary to assert whether this is actually happening or not.

Proposed resolution

Add a test trait with relevant assertions methods.

Remaining tasks

User interface changes

API changes

Data model changes

Comments

tim.plunkett created an issue. See original summary.

tim.plunkett’s picture

Status: Active » Needs review
StatusFileSize
new1.46 KB

Something like this?
Not super happy with the statefulness of this approach, open to suggestions.

dawehner’s picture

Nice functionality and really nice solution!

We could add this asssertion to our WebAssert class if we store the state non on the class but rather locally. Do you think the DX would be worse as a result of that?

tim.plunkett’s picture

the original version of this was more like this:

diff --git a/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php b/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php
index 8aa46c7..3dffff8 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php
@@ -139,6 +139,29 @@ protected function createScreenshot($filename, $set_background_color = TRUE) {
   }
 
   /**
+   * Marks the page to assist determining if the page has been reloaded.
+   */
+  protected function markCurrentPage() {
+    $string = $this->randomMachineName();
+    $this->getSession()->executeScript('document.body.appendChild(document.createTextNode("' . $string . '"));');
+    return $string;
+  }
+
+  /**
+   * Asserts that the page has not been reloaded.
+   */
+  protected function assertPageNotReloaded($string) {
+    $this->assertSession()->pageTextContains($string);
+  }
+
+  /**
+   * Asserts that the page has been reloaded.
+   */
+  protected function assertPageReloaded($string) {
+    $this->assertSession()->pageTextNotContains($string);
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function assertSession($name = NULL) {

Is that more what you meant, but on webassert? not sure

dawehner’s picture

Yeah ... what is your thought?

tim.plunkett’s picture

If we go that way, it doesn't really make sense to have assertPageReloaded(), I'd just as soon call pageTextNotContains() directly.
And maybe a better name for markCurrentPage() is needed.

tim.plunkett’s picture

Also, @tedbow should be credited for this issue, as it was his suggestion in #2764931: Contextual links don't work with 'use-ajax' links

tedbow’s picture

I like the current way rather than returning the string. I feel like it would be easier to make a mistake. In the current way we could even throw an exception if $pageReloadMarker was never set and 1 of the other 2 functions is called.

Also the way it is not we are actually free to change the way it actually works to mark the page later.

droplet’s picture

You could jQuery.once that element. if `jQuery.once === 1`, that's no changes.

** It should be equal to ONE. Two or more and ZERO, the test env is polluted.

dawehner’s picture

Wouldn't this require jquery once on every page? I think having no extra dependency would be nice.

tim.plunkett’s picture

StatusFileSize
new2.93 KB
new3.59 KB

Good idea.

droplet’s picture

@dawehner

For Contrib way yes, otherwise it always loaded with any ajax in D8 Core.

From the big picture, it's the best I think.

DOM Manipulation has side effect and jQuery.once is a standard thing in Drupal, every script could exclude it globally :)

About the current patch, I suggest adding an ID to the new DOM and let other scripts could exclude it from/trigger a re-render. (these kinds of scripts: https://www.drupal.org/node/2645250)

(it's also best to use a standard prefix. Probably it should add .hidden to minimize any side effect too)

dawehner’s picture

+++ b/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php
@@ -139,6 +146,34 @@ protected function createScreenshot($filename, $set_background_color = TRUE) {
+    $this->getSession()->executeScript('document.body.appendChild(document.createTextNode("' . $this->pageReloadMarker . '"));');

Could we store actually this machine name somewhere on the window ... object maybe?

GrandmaGlassesRopeMan’s picture

@dawehner - Nice point. I think if we can avoid DOM manipulation all together for this test we'll be better off; maybe window.Drupal.pageReloadMarker ?

tim.plunkett’s picture

StatusFileSize
new2.86 KB
new3.98 KB

This simplifies the patch in a really great way, and removes the possibility of false positives due to stale state.
Yay statelessness!

Went with window.Drupal.testPageReloadMarker

GrandmaGlassesRopeMan’s picture

+++ b/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php
@@ -139,6 +139,55 @@ protected function createScreenshot($filename, $set_background_color = TRUE) {
+      (function() {
+        window.Drupal = window.Drupal || {};
+        return window.Drupal.testPageReloadMarker;
+      })();

Minor note, evaluateScript requires multiline scripts to be wrapped in an IIFE, let's maybe document that?

droplet’s picture

can we replace EOS to JS? Some editors has JS highlighting :)

tim.plunkett’s picture

StatusFileSize
new2.86 KB
new1.12 KB

Opened https://github.com/minkphp/Mink/pull/742 to address #16, we shouldn't need to document that.

I switched to JS and PHPStorm immediately picked it up. That's cool, thanks!

dawehner’s picture

This is a much nicer solution now!!!

+++ b/core/tests/Drupal/FunctionalJavascriptTests/Tests/JavascriptTestBaseTest.php
@@ -0,0 +1,36 @@
+  /**
+   * @covers ::markCurrentPage
+   * @covers ::assertPageNotReloaded
+   */
+  public function testAssertPageNotReloaded() {
+    $this->drupalGet('');
+    $this->markCurrentPage();
+    $this->assertPageNotReloaded();
+  }
+

I think for an actual test coverage it would be nice to have some interactive behaviour going on in the meantime

tedbow’s picture

re #19 I am not sure we should add interactive behavior to the testAssertPageNotReloaded() we are testing the assertPageNotReloaded not whether any particular activity does or doesn't reload the page.

Maybe as follow up we could look at adding this to current tests that testing our ajax system but I think it is pretty untested with functional javascript tests.

tim.plunkett’s picture

The current use cases we have for this are around testing code that returns an AJAX response when JS is enabled, and performs a redirect when not. Usually from a modal. But it's not only intended for that, and I'm hesitant to tie this test to any particular use case.

If you really strongly, we can couple this test to something with modals.

dawehner’s picture

Maybe executing some HTTP request using jQuery.Get to the frontpage or so. It just feels like doing nothing also doesn't really cover it properly.

tim.plunkett’s picture

Status: Needs review » Needs work
dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new4.4 KB
new2.18 KB

I was the pain in the ass, so I should also add the test coverage ... I'm not sure these tests pass, I don't have time right now :(

Status: Needs review » Needs work

The last submitted patch, 24: 2909782-24.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dawehner’s picture

Status: Needs work » Needs review

I forgot to add some files ...
This time the tests though also pass.

dawehner’s picture

StatusFileSize
new5.81 KB
new3.75 KB

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tim.plunkett’s picture

StatusFileSize
new5.8 KB
new5.29 KB

Fixed trailing whitespace and moved everything from JTB to WDTB.

tedbow’s picture

Status: Needs review » Needs work
  1. +++ b/core/tests/Drupal/FunctionalJavascriptTests/Tests/WebDriverTestBaseTest.php
    @@ -0,0 +1,45 @@
    +  public function testAssertPageReloaded() {
    ...
    +  public function testAssertPageNotReloaded() {
    

    For both of these test methods should have test that assertion actually fails if the page is/isn't reloaded.

  2. +++ b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php
    @@ -169,6 +169,55 @@ protected function createScreenshot($filename, $set_background_color = TRUE) {
    +  /**
    +   * Marks the page to assist determining if the page has been reloaded.
    +   */
    +  protected function markCurrentPage() {
    

    I think this should have @see to getCurrentPageMarker()
    also a @see in the other direction.

  3. So the pattern for using for these test methods would be
    1. You call markCurrentPage() to mark the page
    2. You either call assertPageNotReloaded() or assertPageReloaded()
    3. in either case getCurrentPageMarker() get called.

    Would it be logical error or a problem in your test if you call markCurrentPage() 2x without calling one of the asserts above?
    We could maybe avoid some bad tests but actually checking getCurrentPageMarker() inside to see if it is already marked and throw an exception if it is.
    Maybe markCurrentPage() could have parameter $clear_mark which would default to FALSE but if TRUE would not throw the exception(and not check internally)

    I just don't know of another case where we have these to interelated test methods and might want help with this because easy to make logic errors in tests and not know what you are actually testing.

tedbow’s picture

I played around with this some more.

  1. +++ b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php
    @@ -169,6 +169,55 @@ protected function createScreenshot($filename, $set_background_color = TRUE) {
    +    $this->assertNotEmpty($this->getCurrentPageMarker());
    

    Both of the new assertions don't have a custom message so get an error message like
    "Failed asserting that a string is empty."

    This is not very helpful. Could we change these to custom messages about the reload state.

  2. +++ b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php
    @@ -169,6 +169,55 @@ protected function createScreenshot($filename, $set_background_color = TRUE) {
    +  protected function assertPageReloaded() {
    +    $this->assertEmpty($this->getCurrentPageMarker());
    

    If at test calls assertPageReloaded() without calling markCurrentPage() first the assert actually just passes. And we don't have a @see for markCurrentPage() so it seems reasonable this would happen. Even with the @see

    We could just add the @see but I think it would be better to make sure that does happen.
    We could add WebDriverTestBase::$pageMarkedCalled which would default to FALSE and be set to TRUE at the end of markCurrentPage(). Then in getCurrentPageMarker() which both the new assertions use we could throw an exception or $this->fail() with a message about calling markCurrentPage() if $pageMarkedCalled is still FALSE.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new5.79 KB

This added to the same spot as #3000762: Add assertNoDuplicateIds to a functional test trait so needed a rebase. Still NW for #31/#32, but marking NR first for a test run

tim.plunkett’s picture

Status: Needs review » Needs work

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

tim.plunkett’s picture

Status: Needs work » Closed (outdated)