Problem/Motivation

From #3143870-10: Calls to AssertLegacyTrait::assertUrl() have wrong number of arguments: WebAssert::addressEquals is currently totally skipping the querystring of an URL. This means that tests that check on the address URL expecting or having an actual querystring are passing no matter what. Not good.

Proposed resolution

Fix WebAssert::addressEquals and WebAssert::addressNotEquals so that if the expected URL contains a querystring, the actual URL is is checked for the querystring to be equal.

Remaining tasks

review

User interface changes

no

API changes

no

Data model changes

no

Release notes snippet

no

CommentFileSizeAuthor
#93 3164686-2-93.patch4.96 KBalexpott
#93 91-93-interdiff.txt2.84 KBalexpott
#91 3164686-2-91.patch4.89 KBalexpott
#91 90-91-interdiff.txt7.01 KBalexpott
#90 interdiff.3164686.89-90.txt907 byteslongwave
#90 3164686-90.patch5.65 KBlongwave
#89 interdiff.3164686.86-89.txt5.26 KBlongwave
#89 3164686-89.patch5.66 KBlongwave
#88 3164686-88.patch5.26 KBlongwave
#86 interdiff.3164686.82-86.txt1.58 KBlongwave
#86 3164686-86.patch2.29 KBlongwave
#82 interdiff.3164686.80-82.txt830 byteslongwave
#82 3164686-82.patch2.43 KBlongwave
#80 interdiff_73-80.txt14.84 KBmondrake
#80 3164686-80.patch2.36 KBmondrake
#73 3164686-73.patch15.18 KBmondrake
#24 3164686-24-revert.patch2.64 KBalexpott
#16 interdiff.3164686.11-16.txt545 byteslongwave
#16 3164686-16.patch9.86 KBlongwave
#11 interdiff.3164686.10-11.txt684 byteslongwave
#11 3164686-11.patch9.61 KBlongwave
#10 interdiff_8-10.txt753 bytesridhimaabrol24
#10 3164686-10.patch9.61 KBridhimaabrol24
#8 3164686-8.patch9.11 KBmondrake
#8 interdiff_6-8.txt1.26 KBmondrake
#6 interdiff_3-6.txt6.44 KBmondrake
#6 3164686-6.patch8.99 KBmondrake
#3 3164686-3.patch2.55 KBmondrake
#3 3164686-3-test-only.patch1.82 KBmondrake

Issue fork drupal-3164686

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mondrake created an issue. See original summary.

mondrake’s picture

Assigned: Unassigned » mondrake

on this.

mondrake’s picture

A test only patch to prove the bug, and a patch with the fix - not changing yet the assertUrl calls with wrong parameters as identified in #3143870: Calls to AssertLegacyTrait::assertUrl() have wrong number of arguments.

The last submitted patch, 3: 3164686-3-test-only.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 3: 3164686-3.patch, failed testing. View results

mondrake’s picture

Assigned: mondrake » Unassigned
Issue summary: View changes
Status: Needs work » Needs review
FileSize
8.99 KB
6.44 KB

Copying hunks from #3143870: Calls to AssertLegacyTrait::assertUrl() have wrong number of arguments where the failures were fixed already.

Status: Needs review » Needs work

The last submitted patch, 6: 3164686-6.patch, failed testing. View results

mondrake’s picture

Status: Needs review » Needs work

The last submitted patch, 8: 3164686-8.patch, failed testing. View results

ridhimaabrol24’s picture

Status: Needs work » Needs review
FileSize
9.61 KB
753 bytes

Fixing failed test

longwave’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
9.61 KB
684 bytes

Fixing a one character nit in a comment, but otherwise the patch looks good so marking RTBC.

Does this need a change record? Contrib/custom tests might be unexpectedly relying on the existing broken behaviour.

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record

Let's add a change record, but otherwise looks good.

mondrake’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs change record
larowlan’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs followup

This feels like a bug in Mink too - so can we open an issue in their tracker for them as a courtesy?

This is failing cspell because of the word `scriptalertxsssubjectscript` so we need to add that to the exception list I think

longwave’s picture

Already discussed at length upstream in https://github.com/minkphp/Mink/pull/566 which, if I read it correctly, was closed without fix due to backward compatibility concerns.

https://github.com/minkphp/Mink/issues/656 is also open with no response.

longwave’s picture

Status: Needs work » Needs review
Issue tags: -Needs followup
FileSize
9.86 KB
545 bytes

Added cspell ignore comment.

mondrake’s picture

Status: Needs review » Reviewed & tested by the community

thank you. it looks like Drupal has been stricter directionally, coming from Simpletest, and that was overseen when implementing WebAssert.

larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Fixed on commit - cspell++, larowlan-- for not reading the output properly the first time

diff --git a/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php b/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php
index ce114f83eb..2a0e059098 100644
--- a/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php
@@ -49,7 +49,7 @@ public function testConfirmForm() {
     // Test cancelling the form with a complex destination.
     $this->drupalGet('form-test/confirm-form-array-path');
     $this->clickLink(t('ConfirmFormArrayPathTestForm::getCancelText().'));
-    // Verfiy that the form's complex cancel link was followed.
+    // Verify that the form's complex cancel link was followed.
     $this->assertUrl('form-test/confirm-form?destination=admin/config');
   }

Committed 1e15ed9 and pushed to 9.1.x. Thanks!

Because there's a risk that this will disrupt contrib testing, I think this is not eligible for backporting. Happy to have the discussion if folks think there is a case for it - if so please re-open with reasoning.

Published change record

  • larowlan committed 1e15ed9 on 9.1.x
    Issue #3164686 by mondrake, longwave, ridhimaabrol24: WebAssert::...
TR’s picture

Is there a way to ignore the query string? I have some tests that were broken by this - these tests were ignoring the query string because it is just a csrf token and I don't need or want to check that in every test. I have one test to ensure it's present, but in my functional tests I really want to be just testing the functionality, not the validity of the token every time.

larowlan’s picture

Is there a way to ignore the query string?

I've used this in the past

$this->assertStringContainsString('the/expected/bit', $this->getSession()->getCurrentUrl());
mondrake’s picture

... or use $this->assertSession()->addressMatches() that matches regexes, so you can have the URL path as a pattern and match against the full current URL.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

alexpott’s picture

Status: Closed (fixed) » Needs review
FileSize
2.64 KB

This change is not quite correct and rather than fixing things making test writing more difficult.

I think this change when in the wrong direction - \Behat\Mink\WebAssert::cleanUrl() removes query strings for good reason. Query strings are highly dynamic and tricky to assert on.

Patch attached partially reverts #16 so contrib testing is not broken.

Revert this means we're still not checking query strings correctly - so that does need to be fixed. Then I think we need a better plan with how to test query strings if present and how to cope with destination. The only reason that

    // Verify that the form's complex cancel link was followed.
    $this->assertSession()->addressEquals('form-test/confirm-form?destination=admin/config');

in the original patch is because \Drupal\form_test\ConfirmFormArrayPathTestForm::getCancelUrl() does not construct the destination correctly... it just hard codes 'destination' => 'admin/config', - which is wrong.

alexpott’s picture

Whatever solution we come up with we need to massively improve the CR for the deprecation triggered when assertUrl() is called with more than one argument...

    if (func_num_args() > 1) {
      @trigger_error('Calling AssertLegacyTrait::assertUrl() with more than one argument is deprecated in drupal:8.2.0 and the method is removed from drupal:10.0.0. Use $this->assertSession()->addressEquals() instead. See https://www.drupal.org/node/3129738', E_USER_DEPRECATED);
    }

https://www.drupal.org/node/3129738 is not at all helpful.

longwave’s picture

Status: Needs review » Reviewed & tested by the community

Let's get the revert in and then figure out what to do to move this forward.

Could we only apply the strict rule if the expected URL contains a question mark?

alexpott’s picture

@longwave that's a start - but the destination param with DrupalCI's sub-directory is PITA. See #2957953-99: Editing menus user-experience has regressed - it's too easy to have a test that passes locally and fails on CI - or vice versa. Other people have trod on this ground before... https://github.com/minkphp/Mink/pull/566

longwave’s picture

So as discussed in Slack we likely need to special case the destination querystring to handle the base URL change, and perhaps also fix legacy assertUrl() to accept a second argument containing the querystring, which Simpletest allowed but was never converted fully to WebTestBase.

alexpott’s picture

I've confirmed that PathAutoUiTest is fixed by the revert.

catch’s picture

Agreed with reverting. Will commit once this comes back green.

  • catch committed 924fe92 on 9.2.x
    Issue #3164686 by mondrake, longwave, ridhimaabrol24, alexpott, larowlan...

  • catch committed 48b6510 on 9.1.x
    Issue #3164686 by mondrake, longwave, ridhimaabrol24, alexpott, larowlan...
catch’s picture

Status: Reviewed & tested by the community » Needs work

Committed/pushed to 9.2.x and cherry-picked to 9.1.x, thanks!

Moving back to needs work.

mondrake’s picture

Priority: Major » Critical

OK, but bumping to critical since:

  1. the partial revert reopens the door to possible false negatives, like those that got fixed with #19
  2. it's very possible that this does not get attention short term
  3. the CR announcing the stricter logic in #19 was not amended

Also - assertUrl is dead in core 9.1, since #3139419: Replace usages of AssertLegacyTrait::assertUrl, that is deprecated. We need to understand what to do in that case.

alexpott’s picture

I've unpublished the CR. We've lived with the false positives here for years. Make something critical to get attention feels wrong. But I'm not going to play issue status games. I feel this should be left at major like the original bug because it is just the original bug.

mondrake’s picture

Priority: Critical » Major

Fine.

mondrake’s picture

Here's an idea: add a WebAssert method urlQuerystringEquals that checks only the querystring equality, by loading expected and actual querystring parameters into arrays so that they can be compared by PHPUnit's assertEquals. That would make the order of the parameters in the string irrelevant. See MR.

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

The 7.3 test failure is connected to the composer.lock content hash, but that is not touched in the MR so it feels like an infra issue. For the 7.4 weirdness we have #3181910: Merge requests fail to run core PHPUnit tests on PHP 7.4.

mondrake’s picture

mondrake’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

anmolgoyal74 made their first commit to this issue’s fork.

anmolgoyal74’s picture

Status: Needs work » Needs review

Re-rolled.

mondrake’s picture

Status: Needs review » Needs work
mondrake’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
daffie’s picture

Status: Needs review » Needs work
mondrake’s picture

Status: Needs work » Needs review

Thanks for review @daffie; replies in MR.

daffie’s picture

Status: Needs review » Needs work

I found 3 more files that need changes:
- modules/system/tests/src/Functional/Routing/RouterTest.php
- modules/search/tests/src/Functional/SearchLanguageTest.php
- modules/block/tests/src/Functional/BlockTest.php

@mondrake: The changes you made look good.

mondrake’s picture

Status: Needs work » Needs review

Done #49

daffie’s picture

Status: Needs review » Needs work

One nitpick.

mondrake’s picture

Status: Needs work » Needs review
daffie’s picture

The patch is for me RTBC.
Only the IS and the CR need an update.

longwave’s picture

Added a few minor comments. Do we need to do anything special for the destination query string, as @alexpott alluded to earlier in this issue?

mondrake’s picture

Thanks, replied in MR

mondrake’s picture

Updated CR.

mondrake’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update

Updated IS

daffie’s picture

Issue summary: View changes
mondrake’s picture

rerolled

mondrake’s picture

Rerolled.

daffie’s picture

Status: Needs review » Needs work

The testbot is not happy.

Spokje’s picture

Looks like one of the TestBot's incarantions HDs is full, already encountered this in another issue and asked around in Slack (https://drupal.slack.com/archives/C51GNJG91/p1621922820016900), added this one also.

FWIW: On my incarnation simply ordering a retest made my issue go away (probably because I've got a different TestBot than before).
Ordered a re-test here as well.

mondrake’s picture

Assigned: Unassigned » mondrake

No it's something more evil, like a contributor not testing before posting a patch. Horrible!

On this

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
Spokje’s picture

No it's something more evil, like a contributor not testing before posting a patch. Horrible!

Oh noes! That never ever happens! 😈

daffie’s picture

Status: Needs review » Needs work

All the changes look good to me.
As far as I can see, we got all instances that need the new method.
There is a CR for the new method.
For me it is RTBC.

The only reason I am not setting the status to RTBC, is because the patch from the 9.2 MR does not apply to 9.3.

mondrake’s picture

Status: Needs work » Needs review

Rebased onto 9.3.x

mondrake’s picture

Assigned: Unassigned » mondrake
Status: Needs review » Needs work

I messed up the MR branch, cleaning up

mondrake’s picture

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

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
FileSize
15.18 KB

Oh my, I can't get MR working for me. Reverting to g'old patch.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

Oh my, I can't get MR working for me. Reverting to g'old patch.

And who was the person who said that working with MR's was so much better then working with those patches? :grinning:

@mondrake I do not know what you did with the MR. I have tried compare the MR, that was for me RTBC, to the new patch. And I could not do it.

I had saved the MR as a patchfile on my local machine and all the changes are the same. Therefore this is RTBC for me.

mondrake’s picture

And who was the person who said that working with MR's was so much better then working with those patches? :grinning:

Haha, touchè.

I was trying to re-target the MR to 9.3.x. The problem was that in 9.2.x some stuff was removed to make it for the release, but not in 9.3.x. Having rebased the issue branch earlier onto 9.2.x, when changing the MR target to 9.3.x, stuff was not added back to the branch. IDK what is the right thing to do here.

Then, I had a couple of earlier branches for this issue in my local repo, and could not get a new clean branch to work on. At that point, I just cut short and made the patch :)

Thanks for review @daffie!

alexpott’s picture

I wonder if we should push on the upstream project again. I think if we include a query string in addressEquals then it should check it. It's far too easy to write an assertion and think you've got it right - as shown by our code base.

I think if we want to implement a workaround in core we should do something like only asserting on query strings if the assertion contains a query string. So if you do $this->assertSession()->addressEquals('admin/structure/block/list/classy?block-placement=' . Html::getClass($block['id'])); it will test the query string but if you do $this->assertSession()->addressEquals('admin/structure/block/list/classy'); it won't. That way you preserve BC but you break when the unexpected thing is happening.

+++ b/core/tests/Drupal/Tests/WebAssert.php
@@ -767,6 +787,28 @@ public function addressNotEquals($page) {
+    if (func_num_args() > 1) {
+      throw new \ArgumentCountError('Called ' . __METHOD__ . ' with more than one argument');
+    }

Do we need this given it is a new method?

longwave’s picture

  1. +++ b/core/modules/forum/tests/src/Functional/ForumIndexTest.php
    @@ -54,7 +54,8 @@ public function testForumIndexStatus() {
    +    $this->assertSession()->urlQuerystringEquals("?forum_id=$tid");
    

    Nit: Querystring -> QueryString

  2. +++ b/core/tests/Drupal/FunctionalTests/WebAssertTest.php
    @@ -54,6 +56,37 @@ public function testResponseHeaderDoesNotExist() {
    +    $this->assertSession()->urlQueryStringEquals('');
    

    Why does this pass? What is it testing?

mondrake’s picture

Status: Reviewed & tested by the community » Needs review

I think if we want to implement a workaround in core we should do something like only asserting on query strings if the assertion contains a query string.

That's a good idea. Do we want to keep the separate, new, method anyways, or are we merging that into addressEquals? Do we need a method to check a querystring independently from the URL address?

alexpott’s picture

I don't think we need more methods - I think once we've implemented it for us we should try again upstream - because if you include a query then I think it should either error or check it... and checking it seems better.

mondrake’s picture

Something like this?

Status: Needs review » Needs work

The last submitted patch, 80: 3164686-80.patch, failed testing. View results

longwave’s picture

Status: Needs work » Needs review
FileSize
2.43 KB
830 bytes

addressEquals() only takes strings - not sure how this worked before, as you can't cast a Url to string?

longwave’s picture

Also, should we be strict with the query strings here, or not? ie. if we expect ?a=1 should ?a=1&b=2 be considered a pass, because that would be OK from most web applications' point of view?

mondrake’s picture

#83 good point, we should probably check only equality for the query parameter keys that are in the expectation, disregarding extra keys that occur in the actual.

Also, need to figure out what to do for addressNotEquals.

Status: Needs review » Needs work

The last submitted patch, 82: 3164686-82.patch, failed testing. View results

longwave’s picture

Status: Needs work » Needs review
FileSize
2.29 KB
1.58 KB

Whoops, didn't see our overridden cleanUrl() - I think we should just use that.

alexpott’s picture

Status: Needs review » Needs work
+++ b/core/tests/Drupal/Tests/WebAssert.php
@@ -754,6 +771,18 @@ public function addressEquals($page) {
+    $page = $this->cleanUrl($page);
+    if (strpos($page, '?') !== FALSE) {

$this->cleanUrl($page) is gonna remove the query from $page so the if will never evaluate to true... we need to write a test that is expected to fail :)

longwave’s picture

Argh yes. Let's try again, splitting cleanUrl() in two and adding some tests.

longwave’s picture

FileSize
5.66 KB
5.26 KB
longwave’s picture

alexpott’s picture

+++ b/core/tests/Drupal/Tests/WebAssert.php
@@ -59,7 +74,23 @@ protected function cleanUrl($url) {
+    parse_str($query, $parameters);

This is tricky. This futzes with stuff.

I'd approach this a bit differently. I think we should maintain the same Behat\Mink exception and do something very similar as the parent but in the case that $page includes a query then it should be checked too. Something like the patch attached. Both could do with quite a bit of documentation. Another concern is that you can't assert that a page has no query parameters (like HEAD) but I'm not too sure that in practise that matters. Also this fix is pretty much what I think the upstream fix should look like.

Status: Needs review » Needs work

The last submitted patch, 91: 3164686-2-91.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
FileSize
2.84 KB
4.96 KB

Fixing the test fails...

mondrake’s picture

+++ b/core/tests/Drupal/FunctionalTests/WebAssertTest.php
@@ -96,4 +98,39 @@ public function testElementTextEquals(): void {
+    $this->assertSession()->addressEquals('test-page?a=b&c=d');
...
+    $this->assertSession()->addressNotEquals('test-page?c=d&a=b');

IMHO these two URLs are equivalent (=equal); the order of the keys is not relevant.

alexpott’s picture

@mondrake I think we should reduce the futzing with the comparison not add. However, if we do want to do that we could consider using \GuzzleHttp\Psr7\UriNormalizer as this has all the tools to do url comparison... however reading the docs there:

    /**
     * Sort query parameters with their values in alphabetical order.
     *
     * However, the order of parameters in a URI may be significant (this is not defined by the standard).
     * So this normalization is not safe and may change the semantics of the URI.
     *
     * Example: ?lang=en&article=fred → ?article=fred&lang=en
     *
     * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the
     * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly.
     */
    const SORT_QUERY_PARAMETERS = 128;

I think "IMHO these two URLs are equivalent (=equal); the order of the keys is not relevant." is an assumption that is 99% true but I'd bet there is a contrib / custom module out there where this is not correct.

I also think that evaluating 'test-page?a=b&c=d' to be equal to 'test-page?c=d&a=b' is not in the spirit of the principle of least surprise (also the current behaviour of addressEquals and addressNotEquals also goes against this)

mondrake’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

With rationale in #95, this is RTBC. The CR is now irrelevant. Updated IS.

mondrake’s picture

longwave’s picture

I had the same concern as #94 but #95 makes sense and we can always revisit this later to make it less strict if it turns out we need that for some reason.

Another concern is that you can't assert that a page has no query parameters

This is what this assertion was meant to do in the test?

    $this->assertSession()->addressEquals('test-page?');

Anyway, RTBC +1

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 93: 3164686-2-93.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Reviewed & tested by the community

Random test fail.

  • catch committed 1329479 on 9.3.x
    Issue #3164686 by mondrake, longwave, alexpott, ridhimaabrol24,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Let's try this again :)

Committed/pushed to 9.3.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.