From 111a8419f93cb9afec22c0a759ec2dd10327280b Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:49:42 -0700 Subject: [PATCH 1/3] Simplify bootstrap form tests --- tests/test_app/forms/components.py | 4 +- tests/test_app/forms/forms.py | 7 +++ tests/test_app/tests/test_components.py | 73 +++---------------------- 3 files changed, 17 insertions(+), 67 deletions(-) diff --git a/tests/test_app/forms/components.py b/tests/test_app/forms/components.py index 26ab8704..112b7811 100644 --- a/tests/test_app/forms/components.py +++ b/tests/test_app/forms/components.py @@ -2,7 +2,7 @@ from reactpy_django.components import django_form -from .forms import BasicForm, DatabaseBackedForm, EventForm +from .forms import BasicForm, BootstrapForm, DatabaseBackedForm, EventForm @component @@ -13,7 +13,7 @@ def basic_form(): @component def bootstrap_form(): return django_form( - BasicForm, + BootstrapForm, extra_props={"style": {"maxWidth": "600px", "margin": "auto"}}, form_template="bootstrap_form_template.html", ) diff --git a/tests/test_app/forms/forms.py b/tests/test_app/forms/forms.py index 6b8dedd6..6fe79dc2 100644 --- a/tests/test_app/forms/forms.py +++ b/tests/test_app/forms/forms.py @@ -48,3 +48,10 @@ class Meta: class EventForm(forms.Form): char_field = forms.CharField(label="chars") + + +class BootstrapForm(forms.Form): + # Render a handful of Django field types + boolean_field = forms.BooleanField(label="boolean") + char_field = forms.CharField(label="chars") + choice_field = forms.ChoiceField(label="choice", choices=[("1", "One"), ("2", "Two")]) diff --git a/tests/test_app/tests/test_components.py b/tests/test_app/tests/test_components.py index e04a66f6..4978c236 100644 --- a/tests/test_app/tests/test_components.py +++ b/tests/test_app/tests/test_components.py @@ -776,10 +776,10 @@ def test_form_basic(self): model_choice_field_values[2], ]) + # Submit and wait for one of the error messages to disappear (indicating that the form has been re-rendered) + invalid_feedback = self.page.locator(".errorlist").all()[0] self.page.wait_for_selector("input[type=submit]").click(delay=CLICK_DELAY) - - # Wait for one of the error messages to disappear (indicating that the form has been re-rendered) - expect(self.page.locator(".errorlist").all()[0]).not_to_be_attached() + expect(invalid_feedback).not_to_be_attached() # Make sure no errors remain assert len(self.page.query_selector_all(".errorlist")) == 0 @@ -801,82 +801,25 @@ def test_form_bootstrap(self): self.page.wait_for_selector("#id_boolean_field") self.page.wait_for_selector("#id_char_field") self.page.wait_for_selector("#id_choice_field") - self.page.wait_for_selector("#id_date_field") - self.page.wait_for_selector("#id_date_time_field") - self.page.wait_for_selector("#id_decimal_field") - self.page.wait_for_selector("#id_duration_field") - self.page.wait_for_selector("#id_email_field") - self.page.wait_for_selector("#id_file_path_field") - self.page.wait_for_selector("#id_float_field") - self.page.wait_for_selector("#id_generic_ip_address_field") - self.page.wait_for_selector("#id_integer_field") - self.page.wait_for_selector("#id_float_field") - self.page.wait_for_selector("#id_json_field") - self.page.wait_for_selector("#id_multiple_choice_field") - self.page.wait_for_selector("#id_null_boolean_field") - self.page.wait_for_selector("#id_regex_field") - self.page.wait_for_selector("#id_slug_field") - self.page.wait_for_selector("#id_time_field") - self.page.wait_for_selector("#id_typed_choice_field") - self.page.wait_for_selector("#id_typed_multiple_choice_field") - self.page.wait_for_selector("#id_url_field") - self.page.wait_for_selector("#id_uuid_field") - self.page.wait_for_selector("#id_combo_field") - self.page.wait_for_selector("#id_password_field") - self.page.wait_for_selector("#id_model_choice_field") - self.page.wait_for_selector("#id_model_multiple_choice_field") sleep(1) self.page.wait_for_selector("button[type=submit]").click(delay=CLICK_DELAY) self.page.wait_for_selector(".invalid-feedback") - # Submitting an empty form should result in 22 error elements. + # Submitting an empty form should result in 2 error elements. # The number of errors may change if/when new test form elements are created. - assert len(self.page.query_selector_all(".invalid-feedback")) == 22 + assert len(self.page.query_selector_all(".invalid-feedback")) == 2 # Fill out the form self.page.wait_for_selector("#id_boolean_field").click(delay=CLICK_DELAY) expect(self.page.locator("#id_boolean_field")).to_be_checked() - self.page.locator("#id_char_field").type("test", delay=CLICK_DELAY) self.page.locator("#id_choice_field").select_option("2") - self.page.locator("#id_date_field").type("2021-01-01", delay=CLICK_DELAY) - self.page.locator("#id_date_time_field").type("2021-01-01 01:01:00", delay=CLICK_DELAY) - self.page.locator("#id_decimal_field").type("0.123", delay=CLICK_DELAY) - self.page.locator("#id_duration_field").type("1", delay=CLICK_DELAY) - self.page.locator("#id_email_field").type("test@example.com", delay=CLICK_DELAY) - file_path_field_options = self.page.query_selector_all("#id_file_path_field option") - file_path_field_values: list[str] = [option.get_attribute("value") for option in file_path_field_options] - self.page.locator("#id_file_path_field").select_option(file_path_field_values[1]) - self.page.locator("#id_float_field").type("1.2345", delay=CLICK_DELAY) - self.page.locator("#id_generic_ip_address_field").type("127.0.0.1", delay=CLICK_DELAY) - self.page.locator("#id_integer_field").type("123", delay=CLICK_DELAY) - self.page.locator("#id_json_field").clear() - self.page.locator("#id_json_field").type('{"key": "value"}', delay=CLICK_DELAY) - self.page.locator("#id_multiple_choice_field").select_option(["2", "3"]) - self.page.locator("#id_null_boolean_field").select_option("false") - self.page.locator("#id_regex_field").type("12", delay=CLICK_DELAY) - self.page.locator("#id_slug_field").type("my-slug-text", delay=CLICK_DELAY) - self.page.locator("#id_time_field").type("01:01:00", delay=CLICK_DELAY) - self.page.locator("#id_typed_choice_field").select_option("2") - self.page.locator("#id_typed_multiple_choice_field").select_option(["1", "2"]) - self.page.locator("#id_url_field").type("http://example.com", delay=CLICK_DELAY) - self.page.locator("#id_uuid_field").type("550e8400-e29b-41d4-a716-446655440000", delay=CLICK_DELAY) - self.page.locator("#id_combo_field").type("test@example.com", delay=CLICK_DELAY) - self.page.locator("#id_password_field").type("password", delay=CLICK_DELAY) - - model_choice_field_options = self.page.query_selector_all("#id_model_multiple_choice_field option") - model_choice_field_values: list[str] = [option.get_attribute("value") for option in model_choice_field_options] - self.page.locator("#id_model_choice_field").select_option(model_choice_field_values[0]) - self.page.locator("#id_model_multiple_choice_field").select_option([ - model_choice_field_values[1], - model_choice_field_values[2], - ]) + # Submit and wait for one of the error messages to disappear (indicating that the form has been re-rendered) + invalid_feedback = self.page.locator(".invalid-feedback").all()[0] self.page.wait_for_selector("button[type=submit]").click(delay=CLICK_DELAY) - - # Wait for one of the error messages to disappear (indicating that the form has been re-rendered) - expect(self.page.locator(".invalid-feedback").all()[0]).not_to_be_attached() + expect(invalid_feedback).not_to_be_attached() # Make sure no errors remain assert len(self.page.query_selector_all(".invalid-feedback")) == 0 From 5993ffc762ce562f6f632577ba975a5e054d417e Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:51:40 -0700 Subject: [PATCH 2/3] Better playwright logging --- tests/test_app/tests/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/test_app/tests/utils.py b/tests/test_app/tests/utils.py index f1fb4bd9..dc3cb9dd 100644 --- a/tests/test_app/tests/utils.py +++ b/tests/test_app/tests/utils.py @@ -117,7 +117,16 @@ def start_playwright_client(cls): cls.browser = cls.playwright.chromium.launch(headless=bool(headless)) cls.page = cls.browser.new_page() cls.page.set_default_timeout(10000) - cls.page.on("console", lambda msg: _logger.error("error: %s", msg.text) if msg.type == "error" else None) + cls.page.on("console", cls.playwright_logging) + + @staticmethod + def playwright_logging(msg): + if msg.type == "error": + _logger.error(msg.text) + elif msg.type == "warning": + _logger.warning(msg.text) + elif msg.type == "info": + _logger.info(msg.text) @classmethod def shutdown_playwright_client(cls): From 911907e455508efc082374f803fbd8938068d9bb Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:59:36 -0700 Subject: [PATCH 3/3] bump codeql version --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 17080b52..63a412e0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,7 +46,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -72,6 +72,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}"