-
Notifications
You must be signed in to change notification settings - Fork 42
Drop blank options when using Bulk Edit Options #3345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
b8370b7
c578816
9c598b9
ecd34d3
911bb43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,11 @@ public function setUp(): void { | |
| $this->user_id = $this->factory->user->create( array( 'role' => 'administrator' ) ); | ||
| wp_set_current_user( $this->user_id ); | ||
| FrmAppHelper::maybe_add_permissions(); | ||
| // maybe_add_permissions() grants the cap via a separate WP_User | ||
| // instance, which doesn't refresh the cached current user - add it | ||
| // directly so current_user_can() (used by import_options()'s own | ||
| // is_admin()/current_user_can() gate) sees it. | ||
| wp_get_current_user()->add_cap( 'frm_edit_forms' ); | ||
|
|
||
| $form = $this->factory->form->create_and_get(); | ||
| $this->assertNotEmpty( $form ); | ||
|
|
@@ -98,6 +103,108 @@ public function test_duplicating_text_field() { | |
| self::check_in_section_variable( $field, 0 ); | ||
| } | ||
|
|
||
| /** | ||
| * @covers FrmFieldsController::import_options | ||
| */ | ||
| public function test_import_options_drops_blanks_without_disturbing_other_key() { | ||
| $field = $this->factory->field->create_and_get( | ||
| array( | ||
| 'form_id' => $this->form_id, | ||
| 'type' => 'checkbox', | ||
| 'field_options' => array( 'other' => '1' ), | ||
| 'options' => array( | ||
| array( | ||
| 'label' => 'Existing', | ||
| 'value' => 'existing-value', | ||
| ), | ||
| 'other_2' => 'Other', | ||
| ), | ||
| ) | ||
| ); | ||
|
|
||
| $_POST = array( | ||
| 'action' => 'frm_import_options', | ||
| 'nonce' => wp_create_nonce( 'frm_ajax' ), | ||
| 'field_id' => $field->id, | ||
| 'opts' => "One\n\nTwo", | ||
| 'separate' => 'false', | ||
| ); | ||
|
|
||
| $response = $this->trigger_action( 'frm_import_options' ); | ||
|
|
||
| // The merged-back "other_2" option renders via a Pro-only view | ||
| // (FrmProAppHelper's other-option.php), a no-op with Pro inactive | ||
| // here, so it never appears as its own row - but if blank-line | ||
| // filtering reindexed it into a plain integer key, it would leak | ||
| // through as a normal option labeled "Other" instead. Asserting | ||
| // its exact absence from a normal render is what proves the | ||
| // "other_" key survived the filtering intact. | ||
| preg_match_all( '/\[label\]" value="([^"]*)"/', $response, $matches ); | ||
| // First match is always the hidden "New Option" template row | ||
| // (FrmFieldsHelper::hidden_field_option()), not a real option. | ||
| $this->assertSame( array( 'One', 'Two' ), array_slice( $matches[1], 1 ) ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| /** | ||
| * @covers FrmFieldsController::import_options | ||
| */ | ||
| public function test_import_options_separate_value_drops_blank_value_but_keeps_blank_label() { | ||
| $field = $this->factory->field->create_and_get( | ||
| array( | ||
| 'form_id' => $this->form_id, | ||
| 'type' => 'radio', | ||
| 'field_options' => array( 'other' => '1' ), | ||
| 'options' => array( | ||
| array( | ||
| 'label' => 'Existing', | ||
| 'value' => 'existing-value', | ||
| ), | ||
| 'other_2' => 'Other', | ||
| ), | ||
| ) | ||
| ); | ||
|
|
||
| $_POST = array( | ||
| 'action' => 'frm_import_options', | ||
| 'nonce' => wp_create_nonce( 'frm_ajax' ), | ||
| 'field_id' => $field->id, | ||
| // "Blank|" (blank value) drops; "|no-label" (blank label, real | ||
| // value) survives - it's a legitimate option, not a collision. | ||
| 'opts' => "One|1\nBlank|\n|no-label\nTwo|2", | ||
| 'separate' => 'true', | ||
| ); | ||
|
|
||
| $response = $this->trigger_action( 'frm_import_options' ); | ||
|
|
||
| preg_match_all( '/\[label\]" value="([^"]*)"/', $response, $matches ); | ||
| $this->assertSame( array( 'One', '', 'Two' ), array_slice( $matches[1], 1 ) ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| /** | ||
| * @covers FrmFieldsController::import_options | ||
| */ | ||
| public function test_import_options_keeps_leading_blank_for_select() { | ||
| $field = $this->factory->field->create_and_get( | ||
| array( | ||
| 'form_id' => $this->form_id, | ||
| 'type' => 'select', | ||
| ) | ||
| ); | ||
|
|
||
| $_POST = array( | ||
| 'action' => 'frm_import_options', | ||
| 'nonce' => wp_create_nonce( 'frm_ajax' ), | ||
| 'field_id' => $field->id, | ||
| 'opts' => "\nOne\nTwo", | ||
| 'separate' => 'false', | ||
| ); | ||
|
|
||
| $response = $this->trigger_action( 'frm_import_options' ); | ||
|
|
||
| preg_match_all( '/\[label\]" value="([^"]*)"/', $response, $matches ); | ||
| $this->assertSame( array( '', 'One', 'Two' ), array_slice( $matches[1], 1 ) ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| /** | ||
| * Get a field object by key. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,122 @@ private function prepare_placeholder( $field ) { | |
| return $this->run_private_method( array( 'FrmFieldsController', 'prepare_placeholder' ), array( $field ) ); | ||
| } | ||
|
|
||
| /** | ||
| * @covers FrmFieldsController::parse_bulk_edit_opts | ||
| */ | ||
| public function test_parse_bulk_edit_opts_drops_blank_lines() { | ||
| // A blank line (or one that is only whitespace) must be dropped, not | ||
| // kept as an option with an empty string value - an empty value | ||
| // collides with an unset field value in FrmAppHelper::check_selected() | ||
| // and renders as selected by default (formidable-pro#3385). | ||
| $opts = $this->parse_bulk_edit_opts( "One\n\nTwo\n \nThree", 'radio' ); | ||
|
|
||
| $this->assertSame( array( 'One', 'Two', 'Three' ), $opts ); | ||
| } | ||
|
Comment on lines
+42
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking: both tests exercise the helpers in isolation, so
One test through The PR description says the three helper tests were confirmed red against the old code — that's the right discipline and worth keeping for this one too.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added integration tests driving the real frm_import_options AJAX action end to end (test_FrmFieldsAjax.php): blank-line dropping, the select leading-blank case, the label|value split ordering, and the other_ key surviving the reindex/merge. |
||
|
|
||
| public function test_parse_bulk_edit_opts_keeps_zero_value() { | ||
| // '0' is falsy but a valid option value - only truly blank lines drop. | ||
| $opts = $this->parse_bulk_edit_opts( "0\nOne", 'checkbox' ); | ||
|
|
||
| $this->assertSame( array( '0', 'One' ), $opts ); | ||
| } | ||
|
|
||
| public function test_parse_bulk_edit_opts_keeps_leading_blank_for_select() { | ||
| // A blank first line is a legitimate manual placeholder option on a | ||
| // select field (dropdown-field.php's own placeholder/skip handling), | ||
| // unlike radio/checkbox where a blank option is always a bug. | ||
| $opts = $this->parse_bulk_edit_opts( "\nOne\n\nTwo", 'select' ); | ||
|
|
||
| $this->assertSame( array( '', 'One', 'Two' ), $opts ); | ||
| } | ||
|
|
||
| public function test_parse_bulk_edit_opts_drops_leading_blank_for_radio() { | ||
| $opts = $this->parse_bulk_edit_opts( "\nOne\nTwo", 'radio' ); | ||
| $this->assertSame( array( 'One', 'Two' ), $opts ); | ||
| } | ||
|
|
||
| /** | ||
| * @param string $opts | ||
| * @param string $field_type | ||
| */ | ||
| private function parse_bulk_edit_opts( $opts, $field_type ) { | ||
| return $this->run_private_method( array( 'FrmFieldsController', 'parse_bulk_edit_opts' ), array( $opts, $field_type ) ); | ||
| } | ||
|
|
||
| /** | ||
| * @covers FrmFieldsController::remove_blank_separated_values | ||
| */ | ||
| public function test_remove_blank_separated_values_drops_blank_value() { | ||
| // A "label|" line with nothing after the separator produces a | ||
| // blank value half, the same collision as a blank textarea line | ||
| // (formidable-pro#3385), just reached via separate-value mode. | ||
| $opts = $this->remove_blank_separated_values( | ||
| array( | ||
| array( | ||
| 'label' => 'One', | ||
| 'value' => '1', | ||
| ), | ||
| array( | ||
| 'label' => 'Blank', | ||
| 'value' => '', | ||
| ), | ||
| 'Two', | ||
| ) | ||
| ); | ||
|
|
||
| $this->assertSame( | ||
| array( | ||
| array( | ||
| 'label' => 'One', | ||
| 'value' => '1', | ||
| ), | ||
| 'Two', | ||
| ), | ||
| $opts | ||
| ); | ||
| } | ||
|
|
||
| public function test_remove_blank_separated_values_keeps_blank_label_with_real_value() { | ||
| // A "|value" line with nothing before the separator has a blank | ||
| // label but a real value - no collision with an unset field value | ||
| // (FrmAppHelper::check_selected() only ever compares the value | ||
| // half), and dropdown-field.php renders a blank label as a real, | ||
| // selectable option, so this is left alone. | ||
| $opts = $this->remove_blank_separated_values( | ||
| array( | ||
| array( | ||
| 'label' => 'One', | ||
| 'value' => '1', | ||
| ), | ||
| array( | ||
| 'label' => '', | ||
| 'value' => 'no-label', | ||
| ), | ||
| ) | ||
| ); | ||
|
|
||
| $this->assertSame( | ||
| array( | ||
| array( | ||
| 'label' => 'One', | ||
| 'value' => '1', | ||
| ), | ||
| array( | ||
| 'label' => '', | ||
| 'value' => 'no-label', | ||
| ), | ||
| ), | ||
| $opts | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @param array $opts | ||
| */ | ||
| private function remove_blank_separated_values( $opts ) { | ||
| return $this->run_private_method( array( 'FrmFieldsController', 'remove_blank_separated_values' ), array( $opts ) ); | ||
| } | ||
|
|
||
| /** | ||
| * @covers FrmFieldsController::pull_custom_error_body_from_custom_html | ||
| */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking: the predicate only inspects the value half, so the mirror-image malformed line survives.
|valuesplits toarray( 'label' => '', 'value' => 'value' ).'' !== $opt['value']is true, so it's kept, and the option renders with an empty label against a real value. That isn't thecheck_selected()collision — the value is non-blank, so nothing pre-selects — but it's the same shape of malformed input arriving through the same split, and the docblock above describes the helper as handling that split generally.Deciding to keep it is fine; it just isn't stated anywhere. A line in the docblock saying only the value half is checked, and why, would stop the next reader assuming both halves are covered.
Also:
explode( '|', $opt )at :362 keeps only$vals[0]and$vals[1], soa|b|csilently discardsc. Pre-existing, not yours, and not worth widening this PR for — noting it because it's in the block you're now filtering.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kept, per your note - and now documented in the docblock why: check_selected() only ever compares the value half, and dropdown-field.php renders a blank-label option deliberately, so a value line survives on purpose rather than by omission. Added a test locking in that a blank-label/real-value line survives.