Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Form API's integration with Javascript through #states is extremely complex. Now that we have JavascriptTestBase, we should test this.
Proposed resolution
Add #states tests for:
Triggers
- Checkbox
- Checkboxes
- Textfield (filled or not)
- Radios
- Select
- Multiple triggers (checkbox AND select)
Target #state changes
- Visible (tested with all triggers)
- Invisible
- Required
- Checkbox checked
- Checkbox unchecked
- Details expanded
We don't have all 36 possible combinations, but we've got a wide swath to start from. We can always expand in #2892872: [META] Add Javascript tests for all Form API's #states.
Spreadsheet of coverage from this issue is here:
https://docs.google.com/spreadsheets/d/1jaROESn25lz1L4C0v0vHgFYdtrR6n9TL...
Summary:
- Full coverage for
visible
state across all listed triggers. - Full coverage for all listed #state options using
checkbox
,textfield
, orradios
triggers.
We can get inspiration for tests from:
Remaining tasks
- Decide how much of the coverage matrix we need to fill out in this issue vs. follow-ups from the parent META.
- Re-RTBC.
- Commit.
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#145 | interdiff.txt | 2.25 KB | lauriii |
#133 | 2702233-133.patch | 25.28 KB | dww |
#117 | 2702233-117.patch | 14.73 KB | dww |
Comments
Comment #2
alexpottTest looks like it has found a bug :(
Comment #3
alexpottHmmm maybe this is not a bug because this is fixable with a default value... but it does suggest that there is interesting behaviour due to order different things happen when processing #states.
Comment #4
dawehnerNice!
Comment #5
dawehnerThat comment is a bit weird.
Comment #6
alexpottExpanded the test using the form from the Drupal 7 examples module (@xjm's idea). And fixed the comments @dawehner mentions in #5
Comment #7
larowlannot visible » visible
Did you try set value NULL or FALSE?
Comment #8
alexpott@larowlan thanks for the review:
Comment #9
dawehnerI'm wondering how we could organize the test to have more of a structured approach to all the different kind of cases of tests
Comment #10
xjmYeah, I wondered the same thing while reading the patch.
Probably wants an actual docblock. :)
Comment #11
jibranRelated #994360: #states cannot check/uncheck checkboxes elements
Comment #12
jibranCssSelector
.Can we move #9 to follow up it is not really this issues problem we can discuss it in dedicated issue?
Comment #13
jibranThis doesn't exist in core not even in D7. I don't know why we added this ref in #767738: Rewrite drupal_process_states() documentation, deal with action of 'required' for #states.
I don't think this change is out of scope here.
This is normal because browser are always slow to react. For behat we are used to of adding wait.
Comment #14
alexpottLooks like it is now.
Is this really necessary? I don't think you have to wait for this... if waiting is necessary then we need to wait for #edit-info-provide to be unchecked.
This seems like a big change and unrelated to the patch - should be a separate issue.
Comment #15
alexpottAlso AssertContentTrait adds a tonne of methods that just don;t work on BrowserTestBase ie assertText...
Comment #18
dawehnerWell, the interaction is not instant but async, but I cannot imagine that for states we ever do the communication with phantomjs fast enough, compared to phantomjs unchecking/checking a checkbox.
Comment #19
jibranThanks @alexpott for the review. From #14
In this patch
fillField
,uncheckField
andcheckField
because these show what we are doing on the page.WebAssert
instead of simple true false asserts because of better error handling.Comment #20
dawehnerDo you mind using short array syntax?
Can we avoid this indentation? Its just weird ...
So this is the thing, while this is a nice example from the example module, it makes it hard to follow the test. In case we would name things after what we test there, it would be possible to actually read the test without figuring the entire form out first. Just for example take 'Feedback testing', this comment doesn't help you at all of what we test, or right we test a textarea.
Comment #21
jibranThanks for awesome review @dawehner.
\Drupal\simpletest\WebAssert
but now this is perfectly readable imo. I don't know how much of this is out of scope here but I have updated\Drupal\Tests\toolbar\FunctionalJavascript\ToolbarIntegrationTest
as well and cleaned up BTB.In this patch
WebAssert::assertFieldVisible()
,WebAssert::assertFieldNotVisible()
,WebAssert::assertElementVisible()
andWebAssert::assertElementNotVisible()
to\Drupal\simpletest\WebAssert
JavascriptStatesForm
ToolbarIntegrationTest
JavascriptStatesTest
JavascriptTestBase
JavascriptTestBase::assertJsCondition()
to use session wait function instead of driver.BrowserTestBase
Comment #22
jibranFWIW I used
\Behat\MinkExtension\Context\MinkContext
as a reference for these changes.Comment #23
alexpott@jibran I think @dawehner didn't mean that. I think he meant the labels on the form. Things like 'High School', etc... don't tell us what we're testing.
Comment #25
dawehner@jibran
I'm sorry but I fear you didn't got my point. My point was that the test form is not really explicit about what its testing about. For example the feedback part of the form, is about testing 'textarea' states, so it should be named afterward
On top of that we should avoid putting too many custom stuff into your testing framework, but rather use what mink already provides for us, see https://github.com/Behat/Mink/blob/master/src/Behat/Mink/WebAssert.php
So yeah I think we should have an issue about using that. Also additional cleanup you were doing IMHO belongs into its own issue.
Comment #26
dawehnerComment #27
larowlanWhat I love about this test/these changes are the readability.
We are nearing the fluency of cucumber, I would argue a non-technical user could get a fair idea of what was being tested. And we're using domain language - there are very few css selectors left, which carry no meaning.
This is the first big javascript test in core, and also one of the biggest BTB new generation tests.
I think it is important to set the standard/bar high in terms of understanding.
I would bet that anyone new to testing would much prefer the format of the states test to your average simpletest.
Great work here @jibran
I think there is merit in assertField{Not}VisibleByName methods - would simplify the tests below (lots of 'named' calls). Ok for follow up
This reads much better now
<3 so much better
you could put
$this->assertSession()
in a local variable, and save the myriad of calls.Merit in a
assertFieldsetIsVisible
method? follow up okselectFieldOption
reads much nicer than findById->selectOptionWe should use a constant here
Comment #28
jibranCreated #2711963: Modernized ToolbarIntegrationTest for that.
Ok, I completely misunderstood that point :D
I disagree. If we ever want to fix #1567500: [meta] Pave the way to replace the testing framework with PHPUnit and possibly rewrite Simpletest then we have to bring BTB/JTB more inline with our simpletest kind of asserts. I think we should add wrapper functions in
\Drupal\simpletest\WebAssert
so that we can have feature parity with WTB. And as I said before in #22 these changes exit in MinkContext and DrupalContext so this is not totally new stuff for regular behat users.@larowlan thank for the review.
Lee also suggested we should move
\Drupal\simpletest\WebAssert
out of simpletest\Drupal\simpletest\BrowserTestBase
.In this patch
\Drupal\simpletest\WebAssert
.\Drupal\simpletest\WebAssert
.ToolbarIntegrationTest
.JavascriptTestBase
.BrowserTestBase
.Comment #29
jibranComment #30
Wim Leers+1
A significant reason why SimpleTest is painful to use, is because there's dozens and dozens and dozens of assert methods. Too many to chose from.
I think it'd be better to initially have all these new assert methods be in this particular test class. Once we see the need arise elsewhere, we can choose to put it in its own trait. And eventually we could chose to move it into Drupal's
WebAssert
itself.i.e., at some time in the future:
I think these could fit in a
FormWebAssert
— many tests don't have forms, and these are just distracting.These feel more universally useful and could potentially be moved into
WebAssert
at some point.But… I'm not even sure we really need these. It feels like overengineering at this time. Why can't these just use Mink's assertion methods directly?
Comment #31
jibranJust reroll after #2702281: Move Drupal\simpletest\RandomGeneratorTrait, Drupal\simpletest\WebAssert and Drupal\simpletest\SessionTestTrait into Drupal\Tests namespace.
Comment #32
DuaelFrI had started to work on this on another issue (didn't find this one, thanks @dawehner for closing as duplicate) : #2708823: Test form #states
I was thinking about testing all states on all field types because I remember that some of them behave unexpectedly (see #994360: #states cannot check/uncheck checkboxes elements).
Even if I didn't progress a lot, I found out that
checkboxes
,managed_file
,password_confirm
andradios
elements did not work as expected with some common states (optional/required, visible/invisible and enabled/disabled).That JS code is highly dependant of the html structure of the fields, so I really think we should test all combinations on all elements.
Comment #33
dawehnerWe should try to keep this running again :)
Comment #34
LendudeDiscussed moving this forward with @dawehner and @DuaelFr at DevDays.
We agreed that for this to move forward we need to address the feedback from @dawehner in #20/#23/#25 about the form elements needing to be more descriptive in what they are there to test for ('textarea_dependent_on_selector', along those lines), instead of having a nice real world example.
We also agreed that currently we don't want the extra assertion methods (as per @dawehner in #25 and @Wim Leers in #30), but the patch should look more along the lines of how it did in #19. We felt it's better to just get some javascript tests in and later decide if certain patterns are evident in the existing tests and only then add additional assertion methods, over adding methods that might not be used but we can't remove anymore because they might be used by contrib.
Easier to add things later then removing them and having to deal with BC stuff.
@DuaelFr said he was going to work on this, so feedback on these ramblings would be great and awesome work so far!
Comment #35
DuaelFrI'm working on this.
Comment #36
DuaelFrHere is my progress.
I'd need some help to figure out why the last assertions are failing. (See the @todo comment)
I'd also need to be comforted about the fact I didn't lost myself in translation too ;)
Comment #38
DuaelFrI discussed about this patch with @Lendude this morning (thanks!).
I'm going to rewrite it to make it more understandable.
New patch coming soon.
Comment #39
jibranAs we are seeing in #2735005: Convert all Simpletest web tests to BrowserTestBase (or UnitTestBase/KernelTestBase) we need a lot of new/old assert methods so I still think we should keep the new assert methods.
These all can now go into JsWebAssert or a AssertFieldTrait or AssertJSFieldTrait and then only this test will have to use the trait.
I don't think this is necessary at all If you can't make sense of the form then just enable the module and visit the page. Test will be more understandable if we'll add new assert methods.
IMO this patch is now going in a wrong direction. I still think #31 is the way forward but if no one agrees with me then I'll keep mum about it.
Comment #40
DuaelFrToo late. Here is my patch.
@Lendude and @dawehner agreed it was too early to add a bunch of new assertions as, for tests, we are more in a mood where we do things then, when we'll have enough tests, we'll think about refactoring. Providing many assertions right now means they are going to be there forever because we won't want to break contrib tests that should use them.
Comment #41
jibranThis form is understandable now but not intuitive imho.
This is exactly the stuff we used to do in WTB. What is the use of introducing BTB if we are not going to use it properly? i.e The way user interact with browser. This imo is just a WTB adapting JTB. I'm sorry but we are not setting a good example for adaption of BTB or JTB for rest of the core or contrib.
It means we are allowing ourselves to continue practicing old WTB testing habits.
Yes, that is exactly the intention here so that we'll adapt the browser testing, the way it suppose to be done, with interacting actual browser.
Comment #42
dawehner@jibran
Thank you for looking at the quite big changes done by @DuaelFr. I totally agree with you that we should discuss about adding a lot of assertions vs. not adding a lot of assertions. Let's do that here: #2755617: Add assertions for javascript testing
Well, if you just enable the test module and look at it, its equally clear, from just looking at it. On the other hand, when you actually would have to debug this test, you would see a difference:
vs.
@jibran Do you understand where we are coming from?
Comment #43
jibranIt is a very good idea to create #2755617: Add assertions for javascript testing so thank you for that.
It is just a personal preference. I can live with this change.
But can we please remove this? It can be a big test but if we want to split it in different methods then let's make multiple test methods.
Comment #44
dawehnerWell yeah its slower, and well, things share certainly a common setup. I don't care to be honest.
Comment #45
DuaelFrI made real test cases so core commiters can choose what they prefer between this patch and #40
Comment #46
dawehnerThis test is incredible well documented!
Comment #47
jibranWe want to check all the states.
Comment #48
jibranHere are some doc improvements and added helper methods in
JSWebAssert
andJavascriptStatesTest
Comment #49
dawehnerWhile I agree with you on the point on we should do it, there is no requirement to do it as part of one issue. You know, we can always iterate and enlarge the test coverage over time.
Comment #51
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedComment #52
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedReroll of #48, just a simple conflict on JSWebAssert.
Comment #53
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedComment #54
cilefen CreditAttribution: cilefen commentedComment #55
cilefen CreditAttribution: cilefen commentedOh
Comment #56
dagmarDidn't apply anymore. Added the interdiff of the updated part.
Comment #57
dagmarUps. wrong patch extension.
Comment #58
dagmarAdded new tests for Required and Optional elements.
Abstracted the logic to find a node in the page. It was repeated in four methods.
Comment #59
dagmarWrong patch.
Comment #62
dagmarIs this patch waiting to have all the test cases? @dawehner suggested in #49 we could commit this and iterate the missing tests cases in a follow up.
Comment #63
edurenye CreditAttribution: edurenye at ENDPHASYS Technologies commentedI think @dawehner and @dagmar are right, and more seeing that there are issues like #2700667: Notice: Undefined index: #type in Drupal\Core\Form\FormHelper::processStates() waiting for this to add their own cases.
Comment #64
idebr CreditAttribution: idebr at iO commented@jibran Are you okay with adding the base here and expanding it in related issue?
There are currently a number of issues regarding states that are blocked on the core gate for testing and could benefit greatly from the tests added in this issue:
#2700667: Notice: Undefined index: #type in Drupal\Core\Form\FormHelper::processStates()
#2226405: FAPI #states: dependent element added via AJAX initializes incorrectly if dependee has been initialized earlier
#1149078: States API doesn't work with multiple select fields
#2847425: #states not affecting visibility/requirement of managed_file
#736182: Match States API required fields theming with Forms API
#994360: #states cannot check/uncheck checkboxes elements
#2820586: #states not applied to link elements
Comment #65
jibranYeah, sure. Let's create a meta and track the #47 states and all the issues mentioned in #64.
Comment #66
tedbowCreate meta issue #2892872: [META] Add Javascript tests for all Form API's #states
Updated the title reflect the states covered.
Should these methods be made protected for move somewhere else and remain public?
otherwise this RTBC and would be good to commit.
Comment #69
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedReroll of #59. Manually fixed conflict on
core/modules/system/tests/modules/form_test/form_test.routing.yml
.Comment #71
jibranNeeds a reroll after #2995570: #states breaks when OR is used.
Can we replace these with
assertVisibleInViewport
andassertVisibleInViewport
now?Comment #72
YurkinPark CreditAttribution: YurkinPark at Skilld commentedrerolled
Comment #73
vacho CreditAttribution: vacho at Skilld commentedComment #74
dww#72 is NR (and the bot is green), but we've had a long string of patches without interdiffs. A careful review is needed to make sure we didn't loose anything during the 2.5+ years since the last major re-working (#48 or so by my reading of the issue).
Agreed it'd be fantastic to get this in soon. #states is still all kinds of broken around the edges. Having all these tests would definitely help.
Thanks,
-Derek
p.s. I wrote some #states JS tests for #2419131: [PP-1] #states attribute does not work on #type datetime. Doesn't impact or conflict with this patch at all, but marking related. My tests have to do a lot of this:
I'm not sure it would help me to use the
assertElementNotVisible()
added by this patch, b/c my tests read and work better to have the$date_field
hold the DOM node for both manipulation and asserting various things. So I'm kind of meh on that debate (which I now see got punted to #2755617: Add assertions for javascript testing but sort of died on the vine, as it were).Comment #76
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedRe #71 - I don't think checking the visibility with regards to the viewport is necessary in this scenario, all we care is that the elements react to the states configuration, whether or not they're inside the viewport is irrelevant.
Re #74- I've reviewed changes since #48 and I believe we haven’t lost any coverage, rather we gained some thanks to @dagmar :)
Re #66 - Not sure what you mean there, existing methods on that class are also public... could you please expand on that?
In this interdiff:
Comment #77
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedComment #78
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedImho we should look into adding this in so we can work on adding test coverage for the rest of the
#states
outstanding bugs.Anything else we should be doing here?
Comment #79
geaseComment #80
geaseAdded the test to the patch for #1091852-80: Display Bug when using #states (Forms API) with Ajax Request. Obviously, without that patch test should fail.
Comment #82
geaseRe-uploading the last successful patch, because I posted erroneously here the test for non-committed patch.
Comment #83
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedComment #85
heddnDrive-by quick review:
The comment here should say, "is required".
Comment #86
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation commentedRerolled, fixed conflict on common.inc due to https://www.drupal.org/node/3000069
Also addressed #85 while I was at it. Good catch @heddn by the way.
Comment #88
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedSetting
$defaultTheme
to stark in the test as per https://www.drupal.org/node/3083055Comment #90
dwwMostly looks really good, thanks! Some concerns / nits / requests for even-better-ness. ;)
This is cool, although I don't remember seeing this sort of thing in core much. +1 to doing it here, and trying to do it more often (where appropriate). ;)
I love how specific the other form elements are named. Can this one be:
'item_visible when_select_trigger_has_value2' instead of just '..._given_value'?
Ditto the #title.
s/has_given_value/has_value3/
?
'item_visible_when_select_trigger_has_value2_and_textfield_trigger_filled'
textfield_visible_when_select_trigger_has_value2_or_value3
?
...has_value2
Not clear why all these are using
/* foo */
instead of// foo
.If the idea is to visually break up this huge test case, it seems to make more sense to distinguish comments like these:
/* Test states of elements triggered by a checkbox element. /*
(At the start of each block of code + assertions).
Should we xpath() to the details itself and check that it's closed, instead of relying on the visibility of something inside of? Could be other reasons the inner thing is invisible, right?
Whacky. Thanks for the comment!
As above, can we also find the fieldset element itself and check its attributes?
/by a more/by more/
These local vars shouldn't be camelCase:
$select_trigger
$textfield_trigger
Seems good to clean up after ourselves, even though this is the final set of assertions, to be defensive for if/when we add more to this test.
Not clear why these lovely assertion methods live directly in this test. Why not add them to JSWebAssert.php like the other new helpers?
If they're going to remain in the test, they should probably be protected.
Oh, reading up in the comment thread, I see @tedbow said the same at #66. See also #2755617: Add assertions for javascript testing. Let's move all these to be public in JSWebAssert.php here and close 2755617 as a dup. Cool?
All the callers in this patch use 'named' as the value for this. Shouldn't we document that as an available option here?
"The element selector type (css|xpath|named)." or something?
I know core committers tend to not like 'array' as a @param type hint. We can almost always be more specific than that. E.g.
string[]
or whatever. I'm not 100% clear what kind of array you can use as a selector for this, so someone who knows better should say.A) Not sure what this is inheriting docs from. All I can find with grep is vendor/behat/mink/src/WebAssert.php:
I don't think that's legit to try to inherit docs from as a private method. I could be wrong.
B) Too bad we need to duplicate this entire function. But I guess that's the hell we're in since everything in phpunit is private and they don't make it too easy to extend like this. :(
A) 🤔 Everything else calls this "The selector". What's a "selector engine name"?
B) Don't we want a formal
@see
for this, and shouldn't it be fully qualified?s/if exists/if it exists/
More of the same from above. All I can find with grep is vendor/behat/mink/src/WebAssert.php:
Thanks!
-Derek
Comment #91
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedThank you so much @dww for the thorough review!
Re #90:
1. Done
2. Done
3. Done
4. Done
5. Done
6. Done
7. I believe the intention here is to document that first we assert the initial state before each trigger is triggered, which makes sense, but it's confusing to read indeed. I believe this can be done line by line instead so it reads more clearly, so I updated all these accordingly.
8. Good point, doing so in this patch.
9. Whacky indeed :)
10. Again good point, done.
11. Good catch, fixed.
12. Fixed.
13. I agree, and I hope we expand on his when we fix existing bugs.
14. I agree as well, done.
15. Good catch, fixed.
16. This can either be a string, or an array of strings since they are selectors. It was probably documented like that because thats how it is on \Behat\Mink\Element\ElementInterface::find()
17. Yeah... I added a docblock for it (not 100% sure about it).
18. I have changed it to be consistent with the rest of the patch: The element selector type (css|xpath|named).
19. Fixed.
20. I added a docblock for it (not 100% sure about it).
Comment #93
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedOops, mixed up the assertions on the fieldset and detail elements in the last patch. Fixing that now.
Comment #95
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedOf course since we changed the field names we must update the test.
Comment #97
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedAddressing the deprecation notices...
Comment #98
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedSelf review:
Re #91:
I messed this up, the assertion for the details element is not correct.
Re #93:
This is not the correct fix for the above, it should be checking the correct details element instead.
Comment #99
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedOK apparently I'm loosing my head from time to time... The details element is the correct one, and the patch on #91 was correct for this part. However the assertion isn't as I checked the markup changes of the detail element of the test form manually and the attribute "open" is just set without a value, thus why it wasn't working.
I haven't gotten the test to run locally yet for whatever reason:
Although I do have
chromedriver
running on that port, and I can actually see it in the browser window... /shrugAnyway hopefully this comes back green :)
Comment #100
dwwFantastic! Thanks so much for addressing all that.
I'm deeply sorry to report that I missed some stuff on the last review. :( A new day with fresh eyes leads to fresh concerns. ;)
Probably doesn't matter, but this isn't used anywhere else in the test/patch. Should probably be removed.
Another ubernit: core seems to use 'JavaScript' in comments.
Given "FunctionalJavascriptTests" namespace, I think in class and method names, "Javascript" is fine, so I wouldn't mess with changing any of the rest of this, just the class comment.
Reading more closely, it looks like we only need this when mucking with text fields, right? Maybe the comment should say so? Then folks won't think it's a problem that the other "Change back to the initial state..." blocks aren't doing this, too.
Any reason we can't use
assertElementVisible()
/assertElementNotVisible()
for these?Sorry, missed these, too.
s/dependant/dependent/
Not necessarily "on the current page", right? Depends on
$container
. TheassertField*
versions seem better, since they don't specify in the summary and clarify when documenting$container
.Again, I think the
assertField*
versions handle this better. We should say it's optional, and what happens when you leave it off.Should be 'is visible', right?
A) Seems worth documenting why we're duplicating this function from the parent class here, perhaps adding a @see link, too.
Something like:
B) I'd consider moving this function to the top of all the new methods we're adding to
JSWebAssert
, since nearly everything else depends on it.This doesn't really parse. You'd need more punctuation to keep this wording. But I'd simplify to just:
For
$selector_type
above, when the@param
doc hasfoo|bar|baz
, those are the literal values expected. In this case, what we're really expecting is a selector string that matches one of those selector types (id|name|label|value
). Not sure the most clean/concise way to say it, but I think we can improve these. How about:I think that's really clear that callers shouldn't try passing 'label' for this. ;) Thoughts?
I'd also move all these higher up in the file, to be right under all the
assertElement*()
methods.Comment #101
dwwp.s. re: #100.2:
"Form State" has very special and specific meaning in Drupal. ;) Maybe this would be better as:
Comment #102
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedThanks again for the review @dww
Re #100:
Re #101: good point, done.
Question: The new methods we're adding to
\Drupal\FunctionalJavascriptTests\JSWebAssert
would be useful to functional tests as well, and (as far as I can tell) these are not specific to JS functionality. Would it be possible to add them to\Drupal\Tests\WebAssert
instead?Comment #103
dwwWonderful, looking great!
Re:
I tried this locally. Turns out the visibility stuff is JS-dependent. Trying to use
assertElementVisible()
in aFunctional
test results in:However, the required/optional stuff seems to work great in a
Functional
test:Test:
Result:
Test:
Result:
Test:
Result:
So, if you wanted to split the required/optional stuff from the visibility stuff and move those parts to WebAssert, I wouldn't object. ;) But I think you have to leave the visibility-related assertions in JSWebAssert.
Thanks,
-Derek
Comment #104
dwwActually, looking more closely at these failure messages, I think I was wrong with #100.9. The failure message in #103 for trying to use
assertFieldRequired()
on something optional is confusing:Behat\Mink\Exception\ElementHtmlException: Element "field matching locator "projects[aaa_update_test]"" is required.
Looking at the upstream WebAssert, we see messages like this:
So I think we need to re-do the error messages in these new assertion methods to be more clear that there's a failure and why. See #3131946: [META] [policy] Remove PHPUnit assertion messages when possible, and standardize remaining messages for a larger discussion. ;)
'Element "%s" is invisible, but visible expected.'
Or:
'Element "%s" should be visible, but it is not.'
?
Inverse of whatever we decide above.
'Element "%s' should be required, but is not.'
?
'Element "%s' should be optional, but is required.'
?
(or something).
Thanks/sorry!
-Derek
p.s. Since it NW anyway, you might as well split the optional/required into WebAssert while you're at it. ;)
Comment #105
dwwSorry to do this at this point, but I've been looking more closely at core's existing JS plumbing. I'm now having doubts about some of the generic helper methods.
Looking at 8.9.x branch,
core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php
already has these (deprecated) methods:both point to:
So I'm not sure we want to re-add these like this at all. :(
Maybe we should refactor the tests to not need all this shared plumbing at all? :/
Sorry,
-Derek
Comment #106
dwwFor some inspiration of how to do this without any of the helpers, check out my patch at #3133532-5: JavaScript #states not working on 'duration' form elements. Seems pretty straight-forward:
Note that in this case, the 'duration' field isn't really a field ... it's a div wrapping a bunch of individual number elements. Which is why I needed this:
Anyway, hope that helps. I'm tempted to work on a version of this myself, but then I can no longer RTBC.
@Manuel Garcia you probably hate me by now. ;) But would you be willing to give this another push? Or would you rather I did it and we find someone else to review / RTBC it?
Thanks/sorry,
-Derek
Comment #107
dwwBit more Git archeology... apparently deprecating assertElementVisible() happened in #2711963: Modernized ToolbarIntegrationTest
Comment #108
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commented@dww thank you very much for spending the time to figure things out, it's very useful, so no, I don’t hate you lol!
Assigning to myself, working on this.
Comment #109
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedHere is the patch without adding anything to
\Drupal\FunctionalJavascriptTests\JSWebAssert
. Smaller patches++Comment #111
dwwYay, thanks!! Glad you're not upset. ;) Looking much better. Almost there!
Although the discussion is still going at #3131946: [META] [policy] Remove PHPUnit assertion messages when possible, and standardize remaining messages, we seem to be converging on agreement:
A) In the vast majority of cases, a custom assertion message is no longer needed in our tests. If any of these fail, the line # and default phpunit assertion failure message will be enough to understand what's wrong.
B) If we keep custom messages (e.g. when we're inside a loop and need them to identify which iteration we were on that failed), the suggested form is now:
{context} should {verb} {payload}
.The messages in here aren't consistent. Sometimes they use 'should', sometimes, they're blanket 'positive' statements, etc. However, I think we can follow A for this and all the other assert* lines in the test and simply remove the custom messages so we don't have to split hairs over phrasing them consistently. Only flagging this one in the review, but there are a bunch that can be removed to further simplify and shrink the patch.
Per #106, you don't have to call
find*()
twice on the same element. Once you find it, if you toggle the trigger state, you can immediately assert on the element again to see the new state.Instead of continuing to reuse
$element
for all the found elements, if we use unique + self-documenting names we don't have to keep re-finding them.E.g. the whole first block could become:
Can be just:
These two are duplicate, since the first is finding an element with the required attribute, and the 2nd confirms it's got a required attribute. ;) However, if the element variable had a unique name, both of these could become something like:
For consistency, maybe better to have:
This all becomes so clear that I don't think we need inline comments like "Test that the fieldset element is now visible."
Unique names for the different elements we care about would let us avoid a bunch of later calls to findField().
And here...
This is the test failure... you need
find()
notfindField()
here. But you don't need it at all, since you already found it above. ;)In this block, there's only one $element we care about, so you can just drop the 2nd and 3rd calls.
Otherwise, I'm running out of things to complain about. ;)
Thanks again for driving this home and putting up with all these reviews!
Cheers,
-Derek
Comment #112
dwwre: #111.3 and 9: If you wanted to be a little more explicit, and have a clearer failure if we have the wrong locator or an expected element is missing, we could adopt this form for each of the blocks of test code:
If I run that locally, and intentionally break something, instead of seeing:
Error: Call to a member function isVisible() on null
I get something arguably more useful:
Failed asserting that a NULL is not empty.
In both cases, we get a line number, so we can immediately find the assertion that failed.
Comment #113
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedRe #11, #12 All good feedback, thanks! I think I got all of that in this patch. Also cleaned up most of the comments since the test is much clearer due to the explicit variable and form element names.
I also took the liberty of using
$assert_session->elementExists()
instead of$page->find()
, since it will get you the element and assert that it is there in one call.Comment #114
dwwSo close!! ;)
+1 to
$assert_session->elementExists()
.This is still here. ;) In #102 you said you removed it, but apparently not. Sorry I missed it.
This can be protected.
Checking empty on the wrong variable. Should be:
assertNotEmpty($textfield_visible_value3);
Thanks again!
-Derek
Comment #115
dww#114 are such trivial fixes, here we go. Once the bot's happy, I still feel okay RTBC'ing here.
Comment #116
dwwI thought it'd be good to add some assertions on the textfields inside the details and fieldset. Good thing I did! ;) Found a bug in the form.
'textfield_in_fieldset' wasn't actually in the fieldset, since s/given_value/value2/ didn't happen for its parent item. ;)
Also added a few more clarifying comments.
Finally, I don't think we need to repeat all of "// Change back to the initial state to avoid issues running the next tests." each time. After the first one, I shortened the others to just: "// Change back to the initial state."
Comment #117
dwwSorry, 1 more. ;) If we're going to @see link to test plumbing in the docblock for processStates() seems like we should do this, too:
That seems more useful than
@see \Drupal\form_test\Form\JavascriptStatesForm
, although it doesn't seem to hurt to leave both.Comment #118
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedOh wow good catch on #116 @dww! Also, thanks for #115.
The changes since my last patch look good to me, the comments clean up make sense, and +1 for the extra
@see
.Comment #119
LendudeWow! Thanks so much for pushing this along! Remember spending a good chuck of time debating the setup for this at DevDays Milan, good times :)
This might not be necessary at the end of the test, but I think it is safe to assume we will need to add more coverage to this at some point, so no harm in preparing for that.
Comment #120
init90Added relevant tag
Comment #121
dwwRe: #119: Thanks for the review, and RTBC, @Lendude! Re: cleaning up the final case - agreed. See #90.13. ;)
Comment #122
jibranBig +1 to RTBC. This will be a huge step in the right direction. Let's update the IS with which states are being tested here to make it easier for the committers to understand the patch.
Comment #123
dwwThanks! Updated the summary and title. Hope that's sufficient, and we don't need the exact 5x5 table of which combos of trigger and #state are tested here. That seems more appropriate at the parent: #2892872: [META] Add Javascript tests for all Form API's #states
Cheers,
-Derek
Comment #124
dwwComment #125
dwwTo (hopefully) lay a more solid foundation here to expand this coverage in subsequent issues, and based on the experience of fleshing out the summary, here's an updated approach that splits up the huge
testJavascriptStates()
method into a series of protected helper methods:A) Easier to see what coverage we have for each type of triggering element.
B) No method is longer than ~30 lines, easier to understand everything at a glance.
C) Since each helper reloads the form, we don't have to do all the resetting state stuff... each protected method starts with a clean slate.
D) Easy to expand coverage for an existing trigger element without worrying about impacting other things.
E) Easy to add new coverage for another kind of triggering element.
F) Once split like this, none of the protected methods needed
$assert_session
more than once, so I removed that local variable entirely and just call$this->assertSession()
directly when needed.Interdiff is a bit larger than it needed to be, since I also moved the checkboxes helper to right below the checkbox one.
Do y'all agree this is an improvement? Anyone willing to re-RTBC?
Thanks!
-Derek
p.s. Leaving #117 visible in the summary files table, in case folks prefer that over this.
Comment #126
dwwNoting in the summary that this includes coverage for "Multiple triggers (checkbox AND select)".
Comment #127
dwwCurious for myself, I started a spreadsheet to track the coverage:
https://docs.google.com/spreadsheets/d/1jaROESn25lz1L4C0v0vHgFYdtrR6n9TL...
Adding link to that in the summary.
Not quite as much of a "wide swath" as I initially thought, although we do have at least something from each column and row. ;) Not sure if it's worth continuing to expand here or move to followups.
To further prep for expansion, here's a trivial reorganization of the new form elements added to
core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
to group form elements by what kind of triggering element(s) they respond to.Comment #128
dwwThis completes the test coverage row for checkbox triggers. Shows how easy it is to expand this given the current layout/structure of the form and test.
Updated remaining tasks:
Comment #129
dwwComplete coverage for the entire 'Visible' column.
Comment #130
dwwComplete coverage for the textfield trigger row.
Note: now that we're starting to have multiple details elements on the form, this is a safer way to find the enclosed textfield:
We still find the unique details via the
elementExists()
call. But then to find the sub-element, we search inside the returned element, not page-wide.Comment #131
dwwComment #132
dww(Almost) complete coverage for radios triggers. Some of the targets now respond to different values, so this also includes assertions that going from value2 to value3 will cause the correct state changes and nothing "leaks" to another target.
edit: Forgot to test expanded details behavior responding to a radios trigger. /shrug
Comment #133
dwwAdds expanded details tests for radios trigger.
Comment #134
dwwMore summary updates to clarify what's covered already.
Comment #135
jibranThis looks even better now. Thanks, for the additional coverage.
Comment #136
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commentedFantastic work on this @dww I've been reading all the changes since #117, and I agree on the approach on different protected methods, makes the file a lot easier to read. The extra coverage is needless to say a welcomed addition.
Personally I think it would be great if we can get this in, it already has a good amount of coverage, it sets the right example for adding more in the future, and it would enable us to start working on fixing bugs, so RTBC++
Comment #137
nod_RTBC +1
We'll probably need to make a matrix of the different features, and then add ajax things. But as a start it's very much welcome and will help with #1091852: Display Bug when using #states (Forms API) with Ajax Request, #997826: #states doesn't work correctly with type text_format, #1149078: States API doesn't work with multiple select fields & others. The sooner the better :)
Comment #138
dwwRe:
From the summary:
https://docs.google.com/spreadsheets/d/1jaROESn25lz1L4C0v0vHgFYdtrR6n9TL...
That link now allows edits. Please expand it there, or move it into the summary of #2892872: [META] Add Javascript tests for all Form API's #states.
Thanks!
-Derek
Comment #140
dww#139 was a random fail:
Requeued #133 on 8.9.x and back to RTBC.
Comment #141
daften CreditAttribution: daften commentedThis was marked back to 8.9 in #60, it should probably be fixed for 9.1 first now?
Comment #142
dww@daften - #133 still applies cleanly to all possible core branches this might be committed to (from 9.2.x to 8.9.x). I believe test-only changes are backported to more branches than other changes, and we're supposed to use the version to indicate the "lowest" branch that a given issue should go back to. Ultimately, the committers will decide / sort it out. But I believe the current settings are still accurate.
Cheers,
-Derek
Comment #143
lauriiiMoving to 9.2.x queue
Comment #145
lauriiiI had to make few minor changes to make our spellchecks to pass. Interdiff attached.
Committed e2c0ffb and pushed to 9.2.x. Thank you for working on expanding our test coverage!
Comment #146
DuaelFrWow! Thank you all for fixing this 5 years old issue!
Comment #147
dwwFrom Slack:
Therefore, tagging for release manager review and RTBC'ing as a test-only change for 8.9.x, 9.0.x and 9.1.x branches.
Thanks!
-Derek
Comment #148
alexpottComment #149
catchThis is a test only change (and it's not adding new test API or anything), so it doesn't really need release manager review at all to backport. Although since 8.9 is mostly only getting critical fixes now it could probably just go into 9.1.x
Comment #151
catchCherry-picked to 9.1.x, thanks!
Comment #152
nod_