From ca6cbecb837c004cb950d5a286df656dec3b001f Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 20 Nov 2019 14:45:04 +1100 Subject: [PATCH] Add manual Components.Validator::validator method Support manual validation where needed, add unit tests. This also slightly changes the behaviour of non-required field, since an empty non-required field should be valid. --- .../components/components-validator.vala | 31 ++++-- .../components/components-validator-test.vala | 104 ++++++++++++++++++ test/meson.build | 1 + test/test-client.vala | 1 + 4 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 test/client/components/components-validator-test.vala diff --git a/src/client/components/components-validator.vala b/src/client/components/components-validator.vala index 3e0a7eb8..e5bf0bfd 100644 --- a/src/client/components/components-validator.vala +++ b/src/client/components/components-validator.vala @@ -48,7 +48,9 @@ public class Components.Validator : GLib.Object { /** The cause of a validity check being required. */ public enum Trigger { - /** The entry's contents changed */ + /** A manual validation was requested via {@link validate}. */ + MANUAL, + /** The entry's contents changed. */ CHANGED, /** The entry lost the keyboard focus. */ LOST_FOCUS, @@ -162,6 +164,17 @@ public class Components.Validator : GLib.Object { this.pulse_timer.reset(); } + /** + * Triggers a validation of the entry. + * + * In the case of an asynchronous validation implementations, + * result of the validation will be known sometime after this call + * has completed. + */ + public void validate() { + validate_entry(MANUAL); + } + /** * Called to validate the target entry's value. * @@ -187,7 +200,7 @@ public class Components.Validator : GLib.Object { * By default, this always returns {@link Validity.VALID}, making * it useful for required, but otherwise free-form fields only. */ - protected virtual Validity validate(string value, Trigger reason) { + protected virtual Validity do_validate(string value, Trigger reason) { return Validity.VALID; } @@ -255,12 +268,10 @@ public class Components.Validator : GLib.Object { string value = this.target.get_text(); Validity new_state = this.state; if (Geary.String.is_empty_or_whitespace(value)) { - new_state = this.is_required - ? Validity.EMPTY : Validity.INDETERMINATE; + new_state = this.is_required ? Validity.EMPTY : Validity.VALID; } else { - new_state = validate(value, reason); + new_state = do_validate(value, reason); } - update_state(new_state, reason); } @@ -384,8 +395,8 @@ public class Components.EmailValidator : Validator { } - protected override Validator.Validity validate(string value, - Validator.Trigger reason) { + protected override Validator.Validity do_validate(string value, + Validator.Trigger reason) { return Geary.RFC822.MailboxAddress.is_valid_address(value) ? Validator.Validity.VALID : Validator.Validity.INVALID; } @@ -435,8 +446,8 @@ public class Components.NetworkAddressValidator : Validator { } - public override Validator.Validity validate(string value, - Validator.Trigger reason) { + public override Validator.Validity do_validate(string value, + Validator.Trigger reason) { if (this.cancellable != null) { this.cancellable.cancel(); } diff --git a/test/client/components/components-validator-test.vala b/test/client/components/components-validator-test.vala new file mode 100644 index 00000000..aebb6f1c --- /dev/null +++ b/test/client/components/components-validator-test.vala @@ -0,0 +1,104 @@ +/* + * Copyright 2019 Michael Gratton + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + + +public class Components.ValidatorTest : TestCase { + + + private Gtk.Entry? entry = null; + + + public ValidatorTest() { + base("Components.ValidatorTest"); + add_test("manual_empty", manual_empty); + add_test("manual_valid", manual_valid); + add_test("manual_not_required", manual_not_required); + } + + public override void set_up() { + this.entry = new Gtk.Entry(); + } + + public override void tear_down() { + this.entry = null; + } + + public void manual_empty() throws GLib.Error { + Validator test_article = new Validator(this.entry); + + bool finished = false; + Validator.Trigger? reason = null; + Validator.Validity? prev_state = null; + test_article.state_changed.connect((r, p) => { + finished = true; + reason = r; + prev_state = p; + }); + + test_article.validate(); + + while (!finished) { + this.main_loop.iteration(true); + } + + assert_false(test_article.is_valid); + assert_true(test_article.state == EMPTY); + assert_true(reason == MANUAL); + assert_true(prev_state == INDETERMINATE); + } + + public void manual_valid() throws GLib.Error { + this.entry.text = "OHHAI"; + Validator test_article = new Validator(this.entry); + + bool finished = false; + Validator.Trigger? reason = null; + Validator.Validity? prev_state = null; + test_article.state_changed.connect((r, p) => { + finished = true; + reason = r; + prev_state = p; + }); + + test_article.validate(); + + while (!finished) { + this.main_loop.iteration(true); + } + + assert_true(test_article.is_valid); + assert_true(test_article.state == VALID); + assert_true(reason == MANUAL); + assert_true(prev_state == INDETERMINATE); + } + + public void manual_not_required() throws GLib.Error { + Validator test_article = new Validator(this.entry); + test_article.is_required = false; + + bool finished = false; + Validator.Trigger? reason = null; + Validator.Validity? prev_state = null; + test_article.state_changed.connect((r, p) => { + finished = true; + reason = r; + prev_state = p; + }); + + test_article.validate(); + + while (!finished) { + this.main_loop.iteration(true); + } + + assert_true(test_article.is_valid); + assert_true(test_article.state == VALID); + assert_true(reason == MANUAL); + assert_true(prev_state == INDETERMINATE); + } + +} diff --git a/test/meson.build b/test/meson.build index 1cced2f7..38a3aae2 100644 --- a/test/meson.build +++ b/test/meson.build @@ -84,6 +84,7 @@ geary_test_client_sources = [ 'client/application/application-configuration-test.vala', 'client/components/client-web-view-test.vala', 'client/components/client-web-view-test-case.vala', + 'client/components/components-validator-test.vala', 'client/composer/composer-web-view-test.vala', 'client/util/util-avatar-test.vala', 'client/util/util-cache-test.vala', diff --git a/test/test-client.vala b/test/test-client.vala index 6b828b0c..1016d2a5 100644 --- a/test/test-client.vala +++ b/test/test-client.vala @@ -53,6 +53,7 @@ int main(string[] args) { client.add_suite(new Application.ConfigurationTest().get_suite()); client.add_suite(new ClientWebViewTest().get_suite()); client.add_suite(new Composer.WebViewTest().get_suite()); + client.add_suite(new Components.ValidatorTest().get_suite()); client.add_suite(new Util.Avatar.Test().get_suite()); client.add_suite(new Util.Cache.Test().get_suite()); client.add_suite(new Util.Email.Test().get_suite());