The AJAX Commands introduced in #544418: Integrate CTools AJAX framework with Drupal to extend (and replace) existing ahah framework went in without tests, as have almost all of our AJAX changes.
This patch is an attempt to start doing at least some testing on our AJAX facilities. It:
- Provides AJAXTestCase and AJAXPost(), a standard way to call 'system/ajax' from within a test, including placing the ajax_triggering_element in $_POST.
- Provides a mock module which provides forms for testing AJAX issues.
- Provides new tests for these AJAX commands
- after
- alert
- append
- before
- changed (partial, pending #623310: AJAX 'changed' command asterisk argument doesn't work for full implementation)
css(pending #623320: AJAX 'css' command unimplemented in misc/ajax.js for implementation)- data
- html
- prepend
- remove
- restripe
We are and will be struggling with true functional AJAX testing because we have no Javascript testing capability. #237566: Automated JavaScript unit testing framework is the hoped-for path forward on that front.
But in the meantime, this technique can at least assure that the ajax callbacks are there, implemented, and result in the JSON we expect.
Comment | File | Size | Author |
---|---|---|---|
#40 | drupal.drupalgetajax.40.patch | 840 bytes | sun |
#37 | ajax_tests-633156-37.patch | 10.62 KB | yched |
#34 | ajax_tests-633156-34.patch | 10.99 KB | effulgentsia |
#29 | ajax_tests.patch | 6.94 KB | yched |
#25 | ajax_tests.patch | 7.2 KB | yched |
Comments
Comment #4
rfayOK. I'm baffled. Applied the patch to HEAD again from scratch and I have no trouble parsing this module or running the tests. I've never seen 'Invalid PHP syntax' before as a failure.
Comment #5
drifter CreditAttribution: drifter commentedI can confirm that the patch applies cleanly, and the AJAX tests run fine: 59 passes, 0 fails, and 0 exceptions. Not sure about the PHP syntax error.
Comment #6
yched CreditAttribution: yched commentedIt would be cool to make the AJAXPost() method available in DrupalWebTestCase. Field tests, and IIRC, poll tests, both use a hacked-in method to test their own AHAH behaviors currently.
Comment #7
rfay@yched, if that's the best way, I'll do it. I was assuming that that test in poll.test could just change to an AJAXTestCase.
Comment #8
yched CreditAttribution: yched commentedWell, if the function is self contained and generic enough, it does belong to DrupalWebTestCase. Forcing test classes to subclass AJAXTestCase just to gain access to that function is fairly restrictive, we don't have multiple inheritance in PHP.
Comment #9
rfay@yched, I think it should work fine in DrupalWebTestCase. I'll move it there. I'd also appreciate you taking a close look at the code in that, since I adapted it from DrupalPost, to see if there's anything that should be better.
Comment #10
rfayHere's a reroll with AJAXPost() added to DrupalWebTestCase() per #8. Wonder what the test bot will think about this? That PHP Syntax thing is strange.
Comment #11
rfayIf any of you who have looked at this approve of it and are willing to mark it RTBC, I think we can get it committed and then we can use this structure for the many other AJAX patches that need tests, some of which are pending right now.
Comment #12
effulgentsia CreditAttribution: effulgentsia commentedI think this is great. My only nit is:
You removed uploaded file support in copying this from drupalPost(). PHPDoc should be updated accordingly.
If someone wants to RTBC this (after the above nit is fixed), I'm in favor. Otherwise, I'd like to give it a little more time for other reviewers to see it, before I RTBC it.
I'm on crack. Are you, too?
Comment #13
yched CreditAttribution: yched commentedI'm not familiar enough with the guts of drupalPost() to have an opinion on the implementation :-(, but I'd suggest renaming AJAXPost() to drupalPostAJAX() for consistency (not sure about the capitalization, too).
+ I see AJAXTestCase has a drupalGetAJAX(). Should this be moved to DrupalWebTestCase to mirror drupalGet() ? Or is this unrelated ?
Comment #14
rfayOK, here's a reroll with those two comments. Thanks, @effulgentsia.
@yched, I did make that change here. And the drupalGetAJAX() predates this work so I didn't want to fiddle with it. I'm not sure where it's used. but it uses a GET, which seems odd to me.
The reason I'm being hasty with this was due to a conversation with webchick. Since there are several AJAX patches out there that she's going to require tests for, it was important not to get the basic testing framework tied up with any of them. So this is a low risk (to core) patch which can be fixed up over time. She indicated this as the best way to get a starter in the tests and that she'd be willing to have a low threshold to commit.
Comment #15
effulgentsia CreditAttribution: effulgentsia commentedCapitilization suggested and implemented in #14 is correct. AJAX is an acronym, and this is PHP's convention. Same as
DOMDocument::loadHTML()
Please move it as yched suggests. It makes sense to keep it with drupalPostAJAX(); moving it to the base class won't hurt regressions; and having both of these functions available to more test cases will help, especially as we write more AJAX related tests for field.module.
Ok. Once the above is in, I'll happily mark it RTBC.
Comment #16
rfayHere's the reroll with drupalGetAJAX() moved into DrupalWebTestCase.
Comment #17
effulgentsia CreditAttribution: effulgentsia commentedawesome.
Comment #18
webchickHere are some comments from an initial read-through. I'm being extra anal about documentation here because the AJAX framework is one of those areas that is voodoo mystery sauce to most PHP developers, and they really need to be able to understand what's going on here if tests in here start breaking when they're patching some other part of the system.
The biggest thing I have a problem with is drupalPost() being copy/pasted in two places. I'd rather us either extend drupalPost to let it test AJAX callbacks as well, or see what else we can do to minimize the amount of 100% duplicated code.
It'd be nice to get some unification of these comments, since these functions are basically opposite, no?
Also, could we include a bit of info on why I might need to call these functions from my test?
Maybe note that this is passed into drupalGet().
Contents of the GET? That doesn't make sense. :)
Please put this between @code and @endcode. (see drupal_execute() for an example)
Um. Wow. Is all of this hand-holding necessary? Can't we just give an error if they specified values that didn't make sense?
...oh, I see. You've basically copy/pasted the contents of drupalPost() here.
Would it not make more sense to extend the parameters of drupalPost() to take whatever extra stuff is needed to do what you're trying to do here? Having that much tweaky-ass code duplicated in two places gives me the heebie-jeebies.
(etc.)
Please don't use /** PHPdoc comments */ inline. Use // Inline instead. It's really annoying if you're trying to quickly comment out a test function to figure out where errors are coming from.
I'd use
// Tests the 'xxx' command.
for consistency with the way these are documented in the test module.No need to indent this. Should be directly below @file.
Needs PHPDoc.
(minor) We use sentence capitalization, Not Proper Case.
Remove these; it's enough to document that this is a form.
Separate lines.
(etc.)
It seems odd that you're single-quoting the div id, when normally we use double-quotes for that. I notice you don't do this on "test checkbox", so I assume these are oddities and should be fixed.
These should go in t(), I think.
These need PHPDoc.
There is no such thing as "CTools-style AJAX commands." This is testing code in core. I would remove this.
(minor) Remove extra line between comment and form element definition.
Please spell these out as explicit @todos so we don't lose track of them.
The others don't have three dots, so please don't put three dots here.
(etc.)
These all need a line of PHPDoc.
This review is powered by Dreditor.
Comment #19
rfayWHOA, BRUTALIZED!
@webchick, I feel quite strongly that "forking" drupalPost() was the right thing to do and I stand by it. I definitely understand your point of view, and would even support it a bit if this were core function code and we were trying to minimize the LOC for that reason. But it's test code, likely to be maintained by different types of people for different and very specific reasons. Both functions work in quite dramatically different contexts and would be enormously difficult to maintain if rolled into one thing. The code would turn to spaghetti, and anybody maintaining it would have to completely understand the AJAX stuff, which as we know, many people don't. Yes, the code was forked. No, it's not duplicated. The signature is quite different (which would have to be handled with lots of 'ifs'. Much does not have to be handled by drupalPostAJAX() (which would require lots of 'ifs'. In short, I think it would be a bad idea to put them together. But you knew that already :-)
RE: Unification of comments on drupalGetAJAX and drupalPostAJAX: No, they're not basically opposite. drupalGetAJAX is not a part of this effort, but pre-existed. IMO it should probably not have the name "drupal" in it as it's actually just a helper function for a single test case. Its name was making it seem like it was related to the POST function, but it's actually not. There is no actual use for GET in any of the standard AJAX stuff. This is for a test case only. I changed its name and moved it back into the test case itself where I think it belongs.
What the code is doing at the point you mention is it's trying to make sure that every item in $edit actually exists in the form. This approach is original in drupalPost() and I certainly would not think to fiddle with it there.
I put the few non t() items in the form test function inside t() but I'm confused why this is important in a mock module. If the value actually mattered (which it doesn't in the items you flagged) I would think it would be better NOT to translate, and would result in a more reliable test and mock module. These would never be translated, right?
With the important exception of the drupalPost/drupalPostAJAX discussion, I believe this patch addresses all of your concerns. Thanks for the careful review.
Comment #20
rfayThis one actually passed on all environments (the view details link shows) but never got reported back here. I'll attach it again, I guess , to provoke the bot to actually report.
Comment #21
effulgentsia CreditAttribution: effulgentsia commented@rfay: I hope I'm not out of line here, but I agree with webchick that duplicate drupalPost() code will be harder to maintain than a single one that's AJAX compatible. Also, even though the drupalGetAJAX() function is tiny and not yet used much, I do think it belongs in the web test case, for symmetry if for no other reason, and I suspect once it's there, we'll find uses for it. This patch includes those changes. Obviously, feel free to post disagreements. If you don't have disagreements, RTBC it, so it gets in front of webchick for her to verify that her concerns have been addressed.
Comment #22
rfayThanks for the work, effulgentsia. There will never be a way for you to be "out of line" posting a patch to any issue I'm pushing. Your input is of the highest quality.
I imagine I will have to concede if effulgentsia and webchick both agree on something :-)
I won't have time to look at this until tomorrow, I don't think.
Thanks!
Comment #23
effulgentsia CreditAttribution: effulgentsia commented@webchick: Since rfay concedes on my changes, and since he indicated a desire on your part for this portion of an AJAX testing framework to land in HEAD quickly, I'm RTBC'ing it for your review. The implementation is straightforward and sound and low risk to HEAD. But your thorough review on documentation and grokability is most welcome.
This patch is same as #21 except for these nits:
s/Retrieves/Retrieve/
s/functions/function/
I'm on crack. Are you, too?
Comment #24
webchickAwesome work. There are a few more things I could nitpick here (like drupalGet needs PHPdoc for its params) but this is definitely a solid base and getting this in ASAP will help enable other testing.
Committed to HEAD.
Comment #25
yched CreditAttribution: yched commentedFollowup: convert field and poll tests that currently use their own hacked helper.
Posting this here, because this required enriching drupalPostAJAX() a little.
Comment #26
rfay@yched, please reroll your patch against current HEAD, as #23 got committed yesterday. It moved drupalPostAJAX into drupalPost.
Comment #27
webchickSetting needs review.
Comment #29
yched CreditAttribution: yched commented@rfay: patch *is* rolled against HEAD ;-)
That one should fix the warnings. drupalPostAJAX() cannot assume the structure of the json result array, so it cannot populate $this->content itself. Each consumer has to extract the HTML data from where it expects it.
Maybe drupalPostAJAX() could loop over the $result and put the first $result[n]['data'] it finds in $this->content ? dunno if that's too much assumption already.
Comment #30
effulgentsia CreditAttribution: effulgentsia commentedExcellent cleanup. I have a request, but if it can't happen due to needing to be strict about API freeze, then I'm fine with the patch as-is.
Is it too late to add a drupal_json_decode() function to common.inc, and have these lines call it? If we have a drupal_json_encode() function as part of core because PHP's json_encode() is lame, then it seems like we should include the decode function right along with it.
This review is powered by Dreditor.
Comment #31
yched CreditAttribution: yched commentedre #30: would make sense I guess.
Let's push this to RTBC to have a core committer feedback.
Comment #32
effulgentsia CreditAttribution: effulgentsia commentedAgreed.
webchick/Dries: Please see #30. If you agree that it makes sense to add a drupal_json_decode() function to common.inc right below drupal_json_encode() despite it being an API change to core, please either set this back to "needs work", or commit this patch and add a comment that the follow-up cleanup would be okay with you. As far as API changes go, adding a new function that starts with "drupal_" seems about as low risk as can be, and would have the benefit that if someone needs to change the implementation of drupal_json_encode(), then they see the matching function right there below it, instead of having the decoding buried somewhere within a test case class.
Comment #33
webchickHm. I think this would be acceptable. It doesn't stop anyone from using the old way and those lines of code are damn nasty as-is.
Comment #34
effulgentsia CreditAttribution: effulgentsia commentedGreat! Here it is. I also added tests, so that if someone changes the implementation of the encode function without also changing the decode function or vice versa, bot will catch it.
@yched: please RTBC it if you like it.
Comment #35
yched CreditAttribution: yched commentedSweet :-)
Comment #36
yched CreditAttribution: yched commentedI asked webchick to wait after #638356: Reorganize field_test.module lands, though. I'll do the reroll ;-)
Comment #37
yched CreditAttribution: yched commentedReroll after #638356: Reorganize field_test.module.
Comment #38
webchickLooks good! Committed to HEAD.
Comment #40
sunWithout being protected, assertion messages will be logged with drupalGetAJAX() as source.
Comment #41
yched CreditAttribution: yched commentedWasn't aware of that. Does drupalPostAJAX need the same fix ?
Comment #42
Damien Tournoud CreditAttribution: Damien Tournoud commentedhm? There is no such thing. But yes, it is a good idea to mark those as protected.
Comment #43
effulgentsia CreditAttribution: effulgentsia commenteddrupalPostAJAX() already is protected in HEAD, so #40 is good to go.
@Damien: There is no such what? Do you mean drupalGetAJAX() is a bad function name for what the function does? rfay had pointed the same thing out and suggested renaming it to drupalGetAndJSONDecode(). The advantage to drupalGetAJAX() is when looked at as a group, drupalGet(), drupalGetAJAX(), drupalPost(), and drupalPostAJAX(), provide a symmetrical set of functionality. But if you have a recommendation, please submit one.
No need to hold off the fix in #40. If we do end up renaming the function at some point, it's no harder to rename a protected function than a public one.
Comment #44
webchickCommitted to HEAD. Thanks!