|
4 | 4 | from time import sleep
|
5 | 5 |
|
6 | 6 | import pytest
|
7 |
| -from playwright.sync_api import TimeoutError |
| 7 | +from playwright.sync_api import TimeoutError, expect |
8 | 8 |
|
9 | 9 | from reactpy_django.models import ComponentSession
|
10 | 10 | from reactpy_django.utils import strtobool
|
11 | 11 |
|
12 |
| -from .utils import GITHUB_ACTIONS, PlaywrightTestCase |
| 12 | +from .utils import GITHUB_ACTIONS, PlaywrightTestCase, navigate_to_page |
13 | 13 |
|
14 | 14 | CLICK_DELAY = 250 if strtobool(GITHUB_ACTIONS) else 25 # Delay in miliseconds.
|
15 | 15 |
|
@@ -579,3 +579,223 @@ def test_offline_components(self):
|
579 | 579 | self._server_process.join()
|
580 | 580 | self.page.wait_for_selector("div:not([hidden]) > #offline")
|
581 | 581 | assert self.page.query_selector("div[hidden] > #online") is not None
|
| 582 | + |
| 583 | + |
| 584 | +class FormTests(PlaywrightTestCase): |
| 585 | + def test_basic_form(self): |
| 586 | + navigate_to_page(self, "/form/") |
| 587 | + |
| 588 | + try: |
| 589 | + from test_app.models import TodoItem |
| 590 | + |
| 591 | + os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" |
| 592 | + |
| 593 | + if TodoItem.objects.count() == 0: |
| 594 | + TodoItem(done=False, text="First").save() |
| 595 | + TodoItem(done=True, text="Second").save() |
| 596 | + TodoItem(done=False, text="Third").save() |
| 597 | + finally: |
| 598 | + os.environ.pop("DJANGO_ALLOW_ASYNC_UNSAFE") |
| 599 | + |
| 600 | + self.page.wait_for_selector("form") |
| 601 | + self.page.wait_for_selector("#id_boolean_field") |
| 602 | + self.page.wait_for_selector("#id_char_field") |
| 603 | + self.page.wait_for_selector("#id_choice_field") |
| 604 | + self.page.wait_for_selector("#id_date_field") |
| 605 | + self.page.wait_for_selector("#id_date_time_field") |
| 606 | + self.page.wait_for_selector("#id_decimal_field") |
| 607 | + self.page.wait_for_selector("#id_duration_field") |
| 608 | + self.page.wait_for_selector("#id_email_field") |
| 609 | + self.page.wait_for_selector("#id_file_path_field") |
| 610 | + self.page.wait_for_selector("#id_float_field") |
| 611 | + self.page.wait_for_selector("#id_generic_ip_address_field") |
| 612 | + self.page.wait_for_selector("#id_integer_field") |
| 613 | + self.page.wait_for_selector("#id_float_field") |
| 614 | + self.page.wait_for_selector("#id_json_field") |
| 615 | + self.page.wait_for_selector("#id_multiple_choice_field") |
| 616 | + self.page.wait_for_selector("#id_null_boolean_field") |
| 617 | + self.page.wait_for_selector("#id_regex_field") |
| 618 | + self.page.wait_for_selector("#id_slug_field") |
| 619 | + self.page.wait_for_selector("#id_time_field") |
| 620 | + self.page.wait_for_selector("#id_typed_choice_field") |
| 621 | + self.page.wait_for_selector("#id_typed_multiple_choice_field") |
| 622 | + self.page.wait_for_selector("#id_url_field") |
| 623 | + self.page.wait_for_selector("#id_uuid_field") |
| 624 | + self.page.wait_for_selector("#id_combo_field") |
| 625 | + self.page.wait_for_selector("#id_password_field") |
| 626 | + self.page.wait_for_selector("#id_model_choice_field") |
| 627 | + self.page.wait_for_selector("#id_model_multiple_choice_field") |
| 628 | + |
| 629 | + sleep(1) |
| 630 | + self.page.wait_for_selector("input[type=submit]").click(delay=CLICK_DELAY) |
| 631 | + self.page.wait_for_selector(".errorlist") |
| 632 | + |
| 633 | + # Submitting an empty form should result in 22 error elements. |
| 634 | + # The number of errors may change if/when new test form elements are created. |
| 635 | + assert len(self.page.query_selector_all(".errorlist")) == 22 |
| 636 | + |
| 637 | + # Fill out the form |
| 638 | + self.page.wait_for_selector("#id_boolean_field").click(delay=CLICK_DELAY) |
| 639 | + expect(self.page.locator("#id_boolean_field")).to_be_checked() |
| 640 | + |
| 641 | + self.page.locator("#id_char_field").type("test", delay=CLICK_DELAY) |
| 642 | + self.page.locator("#id_choice_field").select_option("2") |
| 643 | + self.page.locator("#id_date_field").type("2021-01-01", delay=CLICK_DELAY) |
| 644 | + self.page.locator("#id_date_time_field").type("2021-01-01 01:01:00", delay=CLICK_DELAY) |
| 645 | + self.page.locator("#id_decimal_field").type("0.123", delay=CLICK_DELAY) |
| 646 | + self.page.locator("#id_duration_field").type("1", delay=CLICK_DELAY) |
| 647 | + self. page. locator( "#id_email_field"). type( "[email protected]", delay=CLICK_DELAY) |
| 648 | + self.page.locator("#id_file_path_field").select_option("manage.py") |
| 649 | + self.page.locator("#id_float_field").type("1.2345", delay=CLICK_DELAY) |
| 650 | + self.page.locator("#id_generic_ip_address_field").type("127.0.0.1", delay=CLICK_DELAY) |
| 651 | + self.page.locator("#id_integer_field").type("123", delay=CLICK_DELAY) |
| 652 | + self.page.locator("#id_json_field").clear() |
| 653 | + self.page.locator("#id_json_field").type('{"key": "value"}', delay=CLICK_DELAY) |
| 654 | + self.page.locator("#id_multiple_choice_field").select_option(["2", "3"]) |
| 655 | + self.page.locator("#id_null_boolean_field").select_option("false") |
| 656 | + self.page.locator("#id_regex_field").type("12", delay=CLICK_DELAY) |
| 657 | + self.page.locator("#id_slug_field").type("my-slug-text", delay=CLICK_DELAY) |
| 658 | + self.page.locator("#id_time_field").type("01:01:00", delay=CLICK_DELAY) |
| 659 | + self.page.locator("#id_typed_choice_field").select_option("2") |
| 660 | + self.page.locator("#id_typed_multiple_choice_field").select_option(["1", "2"]) |
| 661 | + self.page.locator("#id_url_field").type("http://example.com", delay=CLICK_DELAY) |
| 662 | + self.page.locator("#id_uuid_field").type("550e8400-e29b-41d4-a716-446655440000", delay=CLICK_DELAY) |
| 663 | + self. page. locator( "#id_combo_field"). type( "[email protected]", delay=CLICK_DELAY) |
| 664 | + self.page.locator("#id_password_field").type("password", delay=CLICK_DELAY) |
| 665 | + |
| 666 | + model_choice_field_options = self.page.query_selector_all("#id_model_multiple_choice_field option") |
| 667 | + model_choice_field_values: list[str] = [option.get_attribute("value") for option in model_choice_field_options] |
| 668 | + self.page.locator("#id_model_choice_field").select_option(model_choice_field_values[0]) |
| 669 | + self.page.locator("#id_model_multiple_choice_field").select_option([ |
| 670 | + model_choice_field_values[1], |
| 671 | + model_choice_field_values[2], |
| 672 | + ]) |
| 673 | + |
| 674 | + self.page.wait_for_selector("input[type=submit]").click(delay=CLICK_DELAY) |
| 675 | + |
| 676 | + # Wait for one of the error messages to disappear (indicating that the form has been re-rendered) |
| 677 | + expect(self.page.locator(".errorlist").all()[0]).not_to_be_attached() |
| 678 | + # Make sure no errors remain |
| 679 | + assert len(self.page.query_selector_all(".errorlist")) == 0 |
| 680 | + |
| 681 | + def test_bootstrap_form(self): |
| 682 | + navigate_to_page(self, "/form/bootstrap/") |
| 683 | + |
| 684 | + try: |
| 685 | + from test_app.models import TodoItem |
| 686 | + |
| 687 | + os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" |
| 688 | + |
| 689 | + if TodoItem.objects.count() == 0: |
| 690 | + TodoItem(done=False, text="First").save() |
| 691 | + TodoItem(done=True, text="Second").save() |
| 692 | + TodoItem(done=False, text="Third").save() |
| 693 | + finally: |
| 694 | + os.environ.pop("DJANGO_ALLOW_ASYNC_UNSAFE") |
| 695 | + |
| 696 | + self.page.wait_for_selector("form") |
| 697 | + self.page.wait_for_selector("#id_boolean_field") |
| 698 | + self.page.wait_for_selector("#id_char_field") |
| 699 | + self.page.wait_for_selector("#id_choice_field") |
| 700 | + self.page.wait_for_selector("#id_date_field") |
| 701 | + self.page.wait_for_selector("#id_date_time_field") |
| 702 | + self.page.wait_for_selector("#id_decimal_field") |
| 703 | + self.page.wait_for_selector("#id_duration_field") |
| 704 | + self.page.wait_for_selector("#id_email_field") |
| 705 | + self.page.wait_for_selector("#id_file_path_field") |
| 706 | + self.page.wait_for_selector("#id_float_field") |
| 707 | + self.page.wait_for_selector("#id_generic_ip_address_field") |
| 708 | + self.page.wait_for_selector("#id_integer_field") |
| 709 | + self.page.wait_for_selector("#id_float_field") |
| 710 | + self.page.wait_for_selector("#id_json_field") |
| 711 | + self.page.wait_for_selector("#id_multiple_choice_field") |
| 712 | + self.page.wait_for_selector("#id_null_boolean_field") |
| 713 | + self.page.wait_for_selector("#id_regex_field") |
| 714 | + self.page.wait_for_selector("#id_slug_field") |
| 715 | + self.page.wait_for_selector("#id_time_field") |
| 716 | + self.page.wait_for_selector("#id_typed_choice_field") |
| 717 | + self.page.wait_for_selector("#id_typed_multiple_choice_field") |
| 718 | + self.page.wait_for_selector("#id_url_field") |
| 719 | + self.page.wait_for_selector("#id_uuid_field") |
| 720 | + self.page.wait_for_selector("#id_combo_field") |
| 721 | + self.page.wait_for_selector("#id_password_field") |
| 722 | + self.page.wait_for_selector("#id_model_choice_field") |
| 723 | + self.page.wait_for_selector("#id_model_multiple_choice_field") |
| 724 | + |
| 725 | + sleep(1) |
| 726 | + self.page.wait_for_selector("button[type=submit]").click(delay=CLICK_DELAY) |
| 727 | + self.page.wait_for_selector(".invalid-feedback") |
| 728 | + |
| 729 | + # Submitting an empty form should result in 22 error elements. |
| 730 | + # The number of errors may change if/when new test form elements are created. |
| 731 | + assert len(self.page.query_selector_all(".invalid-feedback")) == 22 |
| 732 | + |
| 733 | + # Fill out the form |
| 734 | + self.page.wait_for_selector("#id_boolean_field").click(delay=CLICK_DELAY) |
| 735 | + expect(self.page.locator("#id_boolean_field")).to_be_checked() |
| 736 | + |
| 737 | + self.page.locator("#id_char_field").type("test", delay=CLICK_DELAY) |
| 738 | + self.page.locator("#id_choice_field").select_option("2") |
| 739 | + self.page.locator("#id_date_field").type("2021-01-01", delay=CLICK_DELAY) |
| 740 | + self.page.locator("#id_date_time_field").type("2021-01-01 01:01:00", delay=CLICK_DELAY) |
| 741 | + self.page.locator("#id_decimal_field").type("0.123", delay=CLICK_DELAY) |
| 742 | + self.page.locator("#id_duration_field").type("1", delay=CLICK_DELAY) |
| 743 | + self. page. locator( "#id_email_field"). type( "[email protected]", delay=CLICK_DELAY) |
| 744 | + self.page.locator("#id_file_path_field").select_option("manage.py") |
| 745 | + self.page.locator("#id_float_field").type("1.2345", delay=CLICK_DELAY) |
| 746 | + self.page.locator("#id_generic_ip_address_field").type("127.0.0.1", delay=CLICK_DELAY) |
| 747 | + self.page.locator("#id_integer_field").type("123", delay=CLICK_DELAY) |
| 748 | + self.page.locator("#id_json_field").clear() |
| 749 | + self.page.locator("#id_json_field").type('{"key": "value"}', delay=CLICK_DELAY) |
| 750 | + self.page.locator("#id_multiple_choice_field").select_option(["2", "3"]) |
| 751 | + self.page.locator("#id_null_boolean_field").select_option("false") |
| 752 | + self.page.locator("#id_regex_field").type("12", delay=CLICK_DELAY) |
| 753 | + self.page.locator("#id_slug_field").type("my-slug-text", delay=CLICK_DELAY) |
| 754 | + self.page.locator("#id_time_field").type("01:01:00", delay=CLICK_DELAY) |
| 755 | + self.page.locator("#id_typed_choice_field").select_option("2") |
| 756 | + self.page.locator("#id_typed_multiple_choice_field").select_option(["1", "2"]) |
| 757 | + self.page.locator("#id_url_field").type("http://example.com", delay=CLICK_DELAY) |
| 758 | + self.page.locator("#id_uuid_field").type("550e8400-e29b-41d4-a716-446655440000", delay=CLICK_DELAY) |
| 759 | + self. page. locator( "#id_combo_field"). type( "[email protected]", delay=CLICK_DELAY) |
| 760 | + self.page.locator("#id_password_field").type("password", delay=CLICK_DELAY) |
| 761 | + |
| 762 | + model_choice_field_options = self.page.query_selector_all("#id_model_multiple_choice_field option") |
| 763 | + model_choice_field_values: list[str] = [option.get_attribute("value") for option in model_choice_field_options] |
| 764 | + self.page.locator("#id_model_choice_field").select_option(model_choice_field_values[0]) |
| 765 | + self.page.locator("#id_model_multiple_choice_field").select_option([ |
| 766 | + model_choice_field_values[1], |
| 767 | + model_choice_field_values[2], |
| 768 | + ]) |
| 769 | + |
| 770 | + self.page.wait_for_selector("button[type=submit]").click(delay=CLICK_DELAY) |
| 771 | + |
| 772 | + # Wait for one of the error messages to disappear (indicating that the form has been re-rendered) |
| 773 | + expect(self.page.locator(".invalid-feedback").all()[0]).not_to_be_attached() |
| 774 | + # Make sure no errors remain |
| 775 | + assert len(self.page.query_selector_all(".invalid-feedback")) == 0 |
| 776 | + |
| 777 | + def test_model_form(self): |
| 778 | + navigate_to_page(self, "/form/model/") |
| 779 | + |
| 780 | + self.page.wait_for_selector("form") |
| 781 | + |
| 782 | + sleep(1) |
| 783 | + self.page.wait_for_selector("input[type=submit]").click(delay=CLICK_DELAY) |
| 784 | + self.page.wait_for_selector(".errorlist") |
| 785 | + |
| 786 | + # Submitting an empty form should result in 1 error element. |
| 787 | + assert len(self.page.query_selector_all(".errorlist")) == 1 |
| 788 | + |
| 789 | + # Fill out the form |
| 790 | + self.page.locator("#id_text").type("test", delay=CLICK_DELAY) |
| 791 | + |
| 792 | + self.page.wait_for_selector("input[type=submit]").click(delay=CLICK_DELAY) |
| 793 | + |
| 794 | + # Wait for the error message to disappear (indicating that the form has been re-rendered) |
| 795 | + expect(self.page.locator(".errorlist").all()[0]).not_to_be_attached() |
| 796 | + |
| 797 | + # Make sure no errors remain |
| 798 | + assert len(self.page.query_selector_all(".errorlist")) == 0 |
| 799 | + |
| 800 | + # Make sure text field is empty |
| 801 | + assert self.page.locator("#id_text").get_attribute("value") == "" |
0 commit comments