After #626354: Remove #process pattern from number field and #628188: Remove #process pattern from taxo autocomplete widget, last but definitely not least - option widgets.

Wow, that one neeeded a good cleanup.
- Workaround for the fact that #179932: Required radios/checkboxes are not validated just made option '0' not work in checkboxes anymore. We switch in a '_0' placeholder before turning the options to FAPI, and replace it back out. We can remove that if/when that issue gets sorted out, but for now this unbreaks optionwidgets.
- Comes with an extensive series of tests. Those widgets are incredibly touchy and painful to debug.
- Adds a few missing tools to drupal_web_test_case:
assertOptionSelected(), assertNoOptionSelected() : same as assertFieldChecked() / assertNoFieldChecked(), but for options within select boxes.
allows drupalPost() to unselect options in a multiple select box (previously impossible)

Still fighting to figure out a line seemingly needed only for tests (left as a @todo), but I'm posting this for review meanwhile.

Comments

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review

AWESOME.

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+    case 'options_buttons':
+      $type = $multiple ? 'checkboxes' : 'radios';
+      // If required and there is one single option, preselect it.
+      if ($element['#required'] && count($options) == 1) {
+        $default_value = array(key($options));
+      }

This should only happen for radios, not for checkboxes.

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+ * FAPI function to validate options element.

This should be:

Form element validation handler for options element.

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+function options_options($field, $instance, $zero_placeholder) {

ugh. ;)

Can't we name this options_allowed_values() or will Field API freak out then?

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+  // Add an empty choice for :
+  // - non required radios,
+  // - non required selects.

We can remove the trailing colon/white-space here. And, list items like these should not have punctuation.

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+    if (($instance['widget']['type'] == 'options_buttons' && ($field['cardinality'] == 1))
+      || ($instance['widget']['type'] == 'options_select')) {

Strange wrapping here?

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+ * Translate values as stored in the database to the format the widget needs.

"in the database"? That isn't necessarily true, right? So how about:

Transform stored values into the format the widget needs.

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+function _options_data2form($items, $options, $column, $zero_placeholder) {
...
+function _options_form2data($element) {

I wonder:

1) Why these are private?

2) What data means?

3) What the number does in there? ;)

So how about:

options_transform_storage_form()
options_transform_form_storage()

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+ * Translate submitted form values to the field storage format.

Transform submitted form values into field storage format.

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
   if (empty($values)) {
-    $values[] = NULL;
+    // @todo Apparently not needed IRL, but tests break without.
+    $values = array(NULL);
   }

erm. 8)

If the tests break without, then we can be sure that some code will break, too.

+++ modules/field/modules/options/options.module	17 Nov 2009 15:56:22 -0000
@@ -67,285 +47,174 @@ function options_field_widget_info() {
+  $result = options_transpose_array_rows_cols(array($element['#value_key'] => $values));

oh - we have a similarly named function already? Nice. :)

I guess this one flips rows to columns and vice-versa?

How about renaming to:

options_array_flip() ?

Or something else, so developers aren't confused by those function names like me currently :P

+++ modules/field/modules/options/options.test	17 Nov 2009 15:16:27 -0000
@@ -190,41 +210,197 @@ class OptionsWidgetsTestCase extends Dru
+    debug($e);
+    debug($values);
+    debug($expected_values);

uh oh

+++ modules/simpletest/drupal_web_test_case.php	17 Nov 2009 13:59:09 -0000
@@ -1610,20 +1610,28 @@ class DrupalWebTestCase extends DrupalTe
+            if (is_array($new_value) && empty($new_value)) {
+              // Multiple select box with no options selected: do not include
+              // any POST data for the element.
...
+                if (is_array($new_value)) {
...
+                elseif ($new_value == $option['value']) {

Can we move this inline comment above the condition, and also add similar ones to the other conditions?

I'm on crack. Are you, too?

moshe weitzman’s picture

Lovely. Thanks for the test coverage too. Subscribe.

yched’s picture

StatusFileSize
new50.11 KB

"If required and there is one single option, preselect it."
This should only happen for radios, not for checkboxes.

I'm not sure I agree, but I'm not sure I agree with this feature to begin with. The current code does this for both radios and checkboxes, I'd rather keep it as is in this patch and have this discussed in #569542: Button Option Field is Required, has One Option - Select that Option where this was introduced.

"options_options()"
ugh - Can't we name this options_allowed_values() ?

This is different. This gathers allowed values frm list_allowed_values() or taxonomy_allowed_values(), and does some extra 'option widgets'-specific massaging.
I went for options_get_options().

"_options_data2form() / _options_form2data()"
Why these are private? What data means? What the number does in there?
how about options_transform_storage_form() / options_transform_form_storage()

Well, those are helper functions for internal massaging, really. I'm not sure where we draw the line between public and private, but I don't want to make those part of an 'API'.
I went for _options_storage_to_form() / _options_form_to_storage().

"// @todo Apparently not needed IRL, but tests break without."
If the tests break without, then we can be sure that some code will break, too.

Yes, I left that here for now because I'd like to find out why exactly. Besides, I'm unable to phrase a code comment explaining why we do this at this point ;-)

"options_transpose_array_rows_cols()"
How about renaming to options_array_flip() ?

This does more than array_flip(): Flip rows and columns in a 2D array. That's transposition ;-)
I went for options_array_transpose().

DrupalWebTestCase::handleForm()
Can we move this inline comment above the condition, and also add similar ones to the other conditions?

Well, commenting if () {...} else {...} blocks is not easy above the 'if'...
I kept comments inside the blocks but tried to streamline this code a bit.

Fixed the other remarks.

+ there is still a forum test failure that beats me.

$this->assertRaw("<td class=\"topics\">\n          6                  </td>");

fails, while I *see* that code in the debug page HTML source.

Status: Needs review » Needs work

The last submitted patch failed testing.

yched’s picture

Heh, the forum test failure was only on my localhost, it seems. Strange. Would that be because of the newline in the asserted text (I'm on windows) ?
Anyway. I forgot about the 'List field' tests. Will fix those tomorrow.

yched’s picture

Status: Needs work » Needs review
StatusFileSize
new54.37 KB

Should fix the remaining failures.

sun’s picture

Lovely.

+++ modules/field/modules/options/options.module	18 Nov 2009 01:36:19 -0000
@@ -67,290 +47,178 @@ function options_field_widget_info() {
+ * Prepares for a field.
...
+function options_get_options($field, $instance, $zero_placeholder) {
@@ -380,24 +248,10 @@ function options_transpose_array_rows_co
- * Helper function for finding the allowed values list for a field.
- *
- * See if there is a module hook for the option values.
- * Otherwise, try list_allowed_values() for an options list.
...
-function options_options($field, $instance) {

Only remaining issue: This PHPDoc summary is a bit short ;) Also not sure whether the original description wasn't helpful, too.

This review is powered by Dreditor.

yched’s picture

StatusFileSize
new54.47 KB

Oops, that PHPdoc was indeed not finished...

- * See if there is a module hook for the option values.
- * Otherwise, try list_allowed_values() for an options list.

That comment was moved inside the function. That area is a WTF currently, we shouldn't hardcode anything on list_allowed_values(), and officially expose hook_options_allowed_values() but that's outside the scope of this patch. Added a @todo.

+ removed the // @todo Apparently not needed IRL, but tests break without. mentioned above, beats me for now.

sun’s picture

Status: Needs review » Reviewed & tested by the community

Great!

We can tackle any @todos in follow-up issues, if necessary.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

This change looks consistent with others that Dries has committed recently, and comes with buckets of nice tests. That _0 thing is too bad; hopefully we can get that sorted out in #179932: Required radios/checkboxes are not validated once and for all.

Committed to HEAD.

yched’s picture

Followup for the todo added in this patch: #639466: hook_options_list() + XSS filtering

bjaspan’s picture

Why were the widget tests changed from using drupal_get_form() and drupal_render() to using drupalGet(), or in other words from being pure API unit tests to being page-request system tests?

yched’s picture

@bjaspan: This allows full fledged tests, with form display + form submission - and form submission is a can of worms with option widgets.
It also makes the tests much simpler to write and maintain.
Besides, this tests 'test_entity' forms, that's what they're here for. That's also what text widget tests do.
The current tests include all the tests that were previously there.

bjaspan’s picture

It also makes the tests much slower, less targeted, and impossible to run on the command line (at least for me). But okay.

BTW, you've done a ton of work recently while I've been MIA. Thanks!

yched’s picture

much slower: not really. We *need* to test form submission. So having separate API-only tests for form display and Get / Post tests for form submission would be even slower while we need the latter anyway and it allows us to make *both* tests in one pass ;-)
So yes, slower, but only because we test more stuff.

less targeted: kinda true for the 'form display' tests, but the extra layer of 'test_entity' forms is quite thin. So sure, if this layer is broken, tests are broken, but, it's a test layer, not 'node' or 'comment', that's what it's here for.

impossible to run on the command line: oh ? that needs to be fixed, but I'm not sure what might cause that. Do text tests run OK ?
BTW, I think I remember you reported something like that months ago when we started to have form submission based tests, and found a reason...

yched’s picture

Glad to have you back, BTW ;-) (typical welcome sentence between CCK / Field API maintainers)

Status: Fixed » Closed (fixed)

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