From d1419983a8a0fd49c9e890a9d4fb20dfd5774d6f Mon Sep 17 00:00:00 2001 From: Maryna Balioura Date: Tue, 31 Oct 2023 00:18:53 +0100 Subject: [PATCH 1/6] export/html: rename field/contenteditable/index.jinja (ex new_field) --- .../form/{new_field => field/contenteditable}/index.jinja | 0 .../components/grammar_form/row_with_custom_field.jinja | 2 +- .../templates/components/grammar_form/row_with_relation.jinja | 2 +- .../components/grammar_form/row_with_reserved_field.jinja | 2 +- .../components/requirement_form/row_with_comment.jinja | 2 +- .../components/requirement_form/row_with_relation.jinja | 2 +- .../components/requirement_form/row_with_text_field.jinja | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename strictdoc/export/html/templates/components/form/{new_field => field/contenteditable}/index.jinja (100%) diff --git a/strictdoc/export/html/templates/components/form/new_field/index.jinja b/strictdoc/export/html/templates/components/form/field/contenteditable/index.jinja similarity index 100% rename from strictdoc/export/html/templates/components/form/new_field/index.jinja rename to strictdoc/export/html/templates/components/form/field/contenteditable/index.jinja diff --git a/strictdoc/export/html/templates/components/grammar_form/row_with_custom_field.jinja b/strictdoc/export/html/templates/components/grammar_form/row_with_custom_field.jinja index 6449cdd7b..92f9f2abb 100644 --- a/strictdoc/export/html/templates/components/grammar_form/row_with_custom_field.jinja +++ b/strictdoc/export/html/templates/components/grammar_form/row_with_custom_field.jinja @@ -45,7 +45,7 @@ mid = custom_field_row_context.field.field_mid, testid_postfix = "custom-field" %} - {%- include "components/form/new_field/index.jinja" %} + {%- include "components/form/field/contenteditable/index.jinja" %} {%- endwith -%} {% endblock row_content %} diff --git a/strictdoc/export/html/templates/components/grammar_form/row_with_relation.jinja b/strictdoc/export/html/templates/components/grammar_form/row_with_relation.jinja index a15a7b79d..04a0e9a93 100644 --- a/strictdoc/export/html/templates/components/grammar_form/row_with_relation.jinja +++ b/strictdoc/export/html/templates/components/grammar_form/row_with_relation.jinja @@ -62,7 +62,7 @@ mid = relation_row_context.relation.relation_mid, testid_postfix = "relation-role" %} - {%- include "components/form/new_field/index.jinja" %} + {%- include "components/form/field/contenteditable/index.jinja" %} {%- endwith -%} {% endif %} diff --git a/strictdoc/export/html/templates/components/grammar_form/row_with_reserved_field.jinja b/strictdoc/export/html/templates/components/grammar_form/row_with_reserved_field.jinja index 7e8c41ad5..a2f6d59b3 100644 --- a/strictdoc/export/html/templates/components/grammar_form/row_with_reserved_field.jinja +++ b/strictdoc/export/html/templates/components/grammar_form/row_with_reserved_field.jinja @@ -29,7 +29,7 @@ mid = reserved_field_row_context.field.field_mid, testid_postfix = "reserved_field" %} - {%- include "components/form/new_field/index.jinja" %} + {%- include "components/form/field/contenteditable/index.jinja" %} {%- endwith -%} {% endblock row_content %} diff --git a/strictdoc/export/html/templates/components/requirement_form/row_with_comment.jinja b/strictdoc/export/html/templates/components/requirement_form/row_with_comment.jinja index ba2912975..76b832a57 100644 --- a/strictdoc/export/html/templates/components/requirement_form/row_with_comment.jinja +++ b/strictdoc/export/html/templates/components/requirement_form/row_with_comment.jinja @@ -29,7 +29,7 @@ mid = comment_field_row_context.field.field_mid, testid_postfix = "requirement-field-COMMENT" %} - {%- include "components/form/new_field/index.jinja" %} + {%- include "components/form/field/contenteditable/index.jinja" %} {%- endwith -%} diff --git a/strictdoc/export/html/templates/components/requirement_form/row_with_text_field.jinja b/strictdoc/export/html/templates/components/requirement_form/row_with_text_field.jinja index ed94c41ed..56860d3e0 100644 --- a/strictdoc/export/html/templates/components/requirement_form/row_with_text_field.jinja +++ b/strictdoc/export/html/templates/components/requirement_form/row_with_text_field.jinja @@ -31,7 +31,7 @@ field_value = text_field_row_context.field.field_escaped_value, testid_postfix = "requirement-field-"~text_field_row_context.field.field_name %} - {%- include "components/form/new_field/index.jinja" %} + {%- include "components/form/field/contenteditable/index.jinja" %} {%- endwith -%} Date: Tue, 31 Oct 2023 02:00:24 +0100 Subject: [PATCH 2/6] export/html: add Reset icon --- strictdoc/export/html/templates/_res/svg_ico16_reset.jinja | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 strictdoc/export/html/templates/_res/svg_ico16_reset.jinja diff --git a/strictdoc/export/html/templates/_res/svg_ico16_reset.jinja b/strictdoc/export/html/templates/_res/svg_ico16_reset.jinja new file mode 100644 index 000000000..d8eee9f02 --- /dev/null +++ b/strictdoc/export/html/templates/_res/svg_ico16_reset.jinja @@ -0,0 +1,6 @@ + + + + + + From 786282b0e0430073a0256a0b6387a9e385cea0bb Mon Sep 17 00:00:00 2001 From: Maryna Balioura Date: Mon, 13 Nov 2023 23:07:54 +0100 Subject: [PATCH 3/6] export/html: add router "/reset_uid" and it's templates --- .../frame_row_with_uid_with_reset_field.jinja | 55 +++++++++++++++++++ ...stream_row_with_uid_with_reset_field.jinja | 33 +++++++++++ strictdoc/server/routers/main_router.py | 45 +++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 strictdoc/export/html/templates/components/requirement_form/frame_row_with_uid_with_reset_field.jinja create mode 100644 strictdoc/export/html/templates/components/requirement_form/stream_row_with_uid_with_reset_field.jinja diff --git a/strictdoc/export/html/templates/components/requirement_form/frame_row_with_uid_with_reset_field.jinja b/strictdoc/export/html/templates/components/requirement_form/frame_row_with_uid_with_reset_field.jinja new file mode 100644 index 000000000..647336975 --- /dev/null +++ b/strictdoc/export/html/templates/components/requirement_form/frame_row_with_uid_with_reset_field.jinja @@ -0,0 +1,55 @@ +{%- extends "components/form/row.jinja" %} + +{% assert text_field_row_context is defined, "row_with_text: row_context must be defined." %} + +{% assert text_field_row_context.errors is defined, "row_with_text: errors must be defined." %} +{% assert text_field_row_context.field is defined, "row_with_text: field must be defined." %} +{% assert text_field_row_context.field_type is defined, "row_with_text: field_type must be defined." %} +{% assert text_field_row_context.field_type in ("singleline", "multiline"), "row_with_text: field_type must be singleline or multiline." %} +{% assert text_field_row_context.reference_mid is defined, "row_with_text: reference_mid must be defined." %} + +{%- set row_context = text_field_row_context -%} + +{% block row_form_attributes %} +{# Explicitly no attributes. #} +{% endblock row_form_attributes %} + +{% block row_left %} + {# Explicitly nothing. #} +{% endblock row_left %} + +{% block row_content scoped %} + {%- set placeholder_name = text_field_row_context.field.field_name %} + + {%- with + mid = text_field_row_context.field.field_mid, + field_class_name = none, + field_input_name = text_field_row_context.field.get_input_field_name(), + field_label = text_field_row_context.field.field_name, + field_placeholder = "Enter "~placeholder_name~" here...", + field_type = text_field_row_context.field_type, + field_value = text_field_row_context.field.field_escaped_value, + testid_postfix = "requirement-field-"~text_field_row_context.field.field_name + %} + {%- include "components/form/field/contenteditable/index.jinja" %} + {%- endwith -%} + + +{% endblock row_content %} + +{% block row_right scoped %} + {% include "_res/svg_ico16_reset.jinja" %} +{% endblock row_right %} diff --git a/strictdoc/export/html/templates/components/requirement_form/stream_row_with_uid_with_reset_field.jinja b/strictdoc/export/html/templates/components/requirement_form/stream_row_with_uid_with_reset_field.jinja new file mode 100644 index 000000000..e966f8bbc --- /dev/null +++ b/strictdoc/export/html/templates/components/requirement_form/stream_row_with_uid_with_reset_field.jinja @@ -0,0 +1,33 @@ +{% assert uid_form_field is defined %} + +{% set text_field_row_context = namespace() %} +{% set row_context = text_field_row_context %} + +{# + When called in the template: + "strictdoc/export/html/templates/components/requirement_form/index.jinja", + + the following parameters are passed: + + {% set text_field_row_context.errors=form_object.get_errors(field_.field_name) %} + {% set text_field_row_context.field = field_ %} + {% set text_field_row_context.field_type = "singleline" %} + {% set text_field_row_context.reference_mid = form_object.requirement_mid %} + + There's no form_object here in Stream. + And we also create a field in the router "/reset_uid", + which we will pass here (instead of the field from the form_object, as in a normal form). + +#} +{# So we redefine the parameters like this: #} +{% set text_field_row_context.errors=[] %} +{% set text_field_row_context.field = uid_form_field %} +{% set text_field_row_context.field_type = "singleline" %} +{% set text_field_row_context.reference_mid = uid_form_field.field_mid %} + + + {# this template is turbo-frame and has a button to reset to the default value: #} + + diff --git a/strictdoc/server/routers/main_router.py b/strictdoc/server/routers/main_router.py index caafd644a..b3256fe13 100644 --- a/strictdoc/server/routers/main_router.py +++ b/strictdoc/server/routers/main_router.py @@ -998,6 +998,51 @@ def get_edit_requirement(requirement_id: str): }, ) + @router.get( + "/reset_uid", response_class=Response, + ) + def reset_uid(reference_mid: str): + reference_node = export_action.traceability_index.get_node_by_mid( + MID(reference_mid) + ) + + assert isinstance(reference_node, Requirement) # FIXME: it might as well be a section? + requirement: Requirement = reference_node + + document_tree_stats: DocumentTreeStats = ( + DocumentUIDAnalyzer.analyze_document_tree( + export_action.traceability_index + ) + ) + next_uid: str = document_tree_stats.get_next_requirement_uid( + reference_node.get_requirement_prefix() + ) + + uid_form_field: RequirementFormField = RequirementFormField( + field_mid=MID.create().get_string_value(), + field_name="UID", + field_type=RequirementFormFieldType.SINGLELINE, + field_unescaped_value=next_uid, + field_escaped_value=next_uid, + ) + template = env().get_template( + "components/" + "requirement_form/" + "stream_row_with_uid_with_reset_field.jinja" + ) + output = template.render( + next_uid=next_uid, + reference_mid=reference_mid, + uid_form_field=uid_form_field, + ) + return HTMLResponse( + content=output, + status_code=200, + headers={ + "Content-Type": "text/vnd.turbo-stream.html", + }, + ) + @router.post("/actions/document/update_requirement") async def document__update_requirement(request: Request): request_form_data: FormData = await request.form() From 97eec0f36bf9bac72a2d7789b7acf95bef224b10 Mon Sep 17 00:00:00 2001 From: Maryna Balioura Date: Tue, 14 Nov 2023 00:02:54 +0100 Subject: [PATCH 4/6] export/html: Use the 'UID reset button' on the requirement form (when the requirement exists and does not have a UID) --- .../templates/components/requirement_form/index.jinja | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/strictdoc/export/html/templates/components/requirement_form/index.jinja b/strictdoc/export/html/templates/components/requirement_form/index.jinja index a9663488a..95250fe91 100644 --- a/strictdoc/export/html/templates/components/requirement_form/index.jinja +++ b/strictdoc/export/html/templates/components/requirement_form/index.jinja @@ -30,7 +30,15 @@ {% set text_field_row_context.errors=form_object.get_errors(field_.field_name) %} {% set text_field_row_context.field = field_ %} {% set text_field_row_context.field_type = "singleline" %} - {% include "components/requirement_form/row_with_text_field.jinja" %} + {% set text_field_row_context.reference_mid = form_object.requirement_mid %} + {%- if field_.field_name == "UID" and field_.field_escaped_value == "" -%} + + {# this template is turbo-frame and has a button to reset to the default value: #} + {% include "components/requirement_form/frame_row_with_uid_with_reset_field.jinja" %} + + {%- else -%} + {% include "components/requirement_form/row_with_text_field.jinja" %} + {%- endif -%} {%- endfor -%} {%- endfor -%} From 6964e09525ba41499b8be374fe076a8d073bf6a2 Mon Sep 17 00:00:00 2001 From: Maryna Balioura Date: Tue, 14 Nov 2023 00:32:32 +0100 Subject: [PATCH 5/6] tests/end2end: add update_requirement_uid_reset_button/test_case.py; add the helpers needed for that --- .../helpers/components/node/requirement.py | 8 ++ .../screens/document/form_edit_requirement.py | 32 ++++++++ .../__init__.py | 0 .../expected_output/document.sdoc | 18 +++++ .../input/document.sdoc | 17 +++++ .../test_case.py | 73 +++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/__init__.py create mode 100644 tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/expected_output/document.sdoc create mode 100644 tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/input/document.sdoc create mode 100644 tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/test_case.py diff --git a/tests/end2end/helpers/components/node/requirement.py b/tests/end2end/helpers/components/node/requirement.py index cd408e53c..1eada2983 100644 --- a/tests/end2end/helpers/components/node/requirement.py +++ b/tests/end2end/helpers/components/node/requirement.py @@ -81,6 +81,14 @@ def assert_requirement_title( by=By.XPATH, ) + def assert_requirement_has_no_uid(self) -> None: + """Use it with full requirement. """ + self.test_case.assert_element_not_present( + f"{self.node_xpath}" + "//sdoc-requirement-field[@data-field-label='UID']", + by=By.XPATH, + ) + def assert_requirement_uid_contains( self, uid: str, diff --git a/tests/end2end/helpers/screens/document/form_edit_requirement.py b/tests/end2end/helpers/screens/document/form_edit_requirement.py index 8622bebfe..288da0021 100644 --- a/tests/end2end/helpers/screens/document/form_edit_requirement.py +++ b/tests/end2end/helpers/screens/document/form_edit_requirement.py @@ -115,3 +115,35 @@ def do_fill_in_field_statement(self, field_value: str) -> None: def do_fill_in_field_rationale(self, field_value: str) -> None: assert isinstance(field_value, str) super().do_fill_in("requirement-field-RATIONALE", field_value) + + # Reset UID button + + def assert_uid_field_contains(self, string: str) -> None: + self.test_case.assert_element_present( + "//*[@data-testid='form-field-requirement-field-UID']" + f"[contains(., '{string}')]", + by=By.XPATH, + ) + + def assert_uid_field_does_not_contain(self, string: str) -> None: + self.test_case.assert_element_not_present( + "//*[@data-testid='form-field-requirement-field-UID']" + f"[contains(., '{string}')]", + by=By.XPATH, + ) + + def assert_uid_field_has_reset_button(self) -> None: + self.test_case.assert_element_present( + "//*[@data-testid='reset-uid-field-action']", + by=By.XPATH, + ) + + def assert_uid_field_has_not_reset_button(self) -> None: + self.test_case.assert_element_not_present( + "//*[@data-testid='reset-uid-field-action']", + by=By.XPATH, + ) + + def do_reset_uid_field(self, field_name: str = "") -> None: + assert isinstance(field_name, str) + self.test_case.click_xpath("//*[@data-testid='reset-uid-field-action']") diff --git a/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/__init__.py b/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/expected_output/document.sdoc b/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/expected_output/document.sdoc new file mode 100644 index 000000000..d3b165c94 --- /dev/null +++ b/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/expected_output/document.sdoc @@ -0,0 +1,18 @@ +[DOCUMENT] +TITLE: Document 1 + +[FREETEXT] +Hello world! +[/FREETEXT] + +[REQUIREMENT] +UID: REQ-1 +STATEMENT: >>> +Requirement statement. +<<< + +[REQUIREMENT] +UID: REQ-2 +STATEMENT: >>> +Requirement statement. +<<< diff --git a/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/input/document.sdoc b/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/input/document.sdoc new file mode 100644 index 000000000..890664709 --- /dev/null +++ b/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/input/document.sdoc @@ -0,0 +1,17 @@ +[DOCUMENT] +TITLE: Document 1 + +[FREETEXT] +Hello world! +[/FREETEXT] + +[REQUIREMENT] +UID: REQ-1 +STATEMENT: >>> +Requirement statement. +<<< + +[REQUIREMENT] +STATEMENT: >>> +Requirement statement. +<<< diff --git a/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/test_case.py b/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/test_case.py new file mode 100644 index 000000000..29c68a76e --- /dev/null +++ b/tests/end2end/screens/document/update_requirement/update_requirement_uid_reset_button/test_case.py @@ -0,0 +1,73 @@ +from tests.end2end.e2e_case import E2ECase +from tests.end2end.end2end_test_setup import End2EndTestSetup +from tests.end2end.helpers.screens.document.form_edit_requirement import ( + Form_EditRequirement, +) +from tests.end2end.helpers.screens.project_index.screen_project_index import ( + Screen_ProjectIndex, +) +from tests.end2end.server import SDocTestServer + + +class Test(E2ECase): + def test(self): + test_setup = End2EndTestSetup(path_to_test_file=__file__) + + with SDocTestServer( + input_path=test_setup.path_to_sandbox + ) as test_server: + self.open(test_server.get_host_and_port()) + + screen_project_index = Screen_ProjectIndex(self) + + screen_project_index.assert_on_screen() + screen_project_index.assert_contains_document("Document 1") + + screen_document = screen_project_index.do_click_on_first_document() + + screen_document.assert_on_screen_document() + screen_document.assert_header_document_title("Document 1") + screen_document.assert_text("Hello world!") + + # Requirement 1 has UID. + # It shouldn't have a reset button. + requirement1 = screen_document.get_requirement(1) + requirement1.assert_requirement_uid_contains("REQ-1") + form_edit_requirement1: Form_EditRequirement = ( + requirement1.do_open_form_edit_requirement() + ) + form_edit_requirement1.assert_on_form() + form_edit_requirement1.assert_uid_field_contains("REQ-1") + form_edit_requirement1.assert_uid_field_has_not_reset_button() + form_edit_requirement1.do_form_cancel() + + # New requirement form shouldn't have a reset button, + # but must have the correct UID entered in the field. + node_menu = requirement1.do_open_node_menu() + form_add_requirement: Form_EditRequirement = ( + node_menu.do_node_add_requirement_above() + ) + form_add_requirement.assert_on_form() + form_add_requirement.assert_uid_field_has_not_reset_button() + form_add_requirement.assert_uid_field_contains("REQ-2") + form_add_requirement.do_form_cancel() + + # Requirement 2 has UID. + # It shouldn't have a reset button. + requirement2 = screen_document.get_requirement(2) + requirement2.assert_requirement_has_no_uid() + form_edit_requirement2: Form_EditRequirement = ( + requirement2.do_open_form_edit_requirement() + ) + form_edit_requirement2.assert_on_form() + form_edit_requirement2.assert_uid_field_has_reset_button() + form_edit_requirement2.assert_uid_field_does_not_contain("REQ-2") + # Use the reset button. + form_edit_requirement2.do_reset_uid_field() + form_edit_requirement2.assert_uid_field_contains("REQ-2") + form_edit_requirement2.do_form_submit() + + # Checking the success of the button. + requirement2.assert_requirement_uid_contains("REQ-2") + + assert test_setup.compare_sandbox_and_expected_output() From aa60c901faef2c802b9fa6d5a0a74c69694c0cc3 Mon Sep 17 00:00:00 2001 From: Maryna Balioura Date: Tue, 14 Nov 2023 00:34:24 +0100 Subject: [PATCH 6/6] main_router.py: fix lint issues --- strictdoc/server/routers/main_router.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/strictdoc/server/routers/main_router.py b/strictdoc/server/routers/main_router.py index b3256fe13..3e130eab8 100644 --- a/strictdoc/server/routers/main_router.py +++ b/strictdoc/server/routers/main_router.py @@ -999,15 +999,16 @@ def get_edit_requirement(requirement_id: str): ) @router.get( - "/reset_uid", response_class=Response, + "/reset_uid", + response_class=Response, ) def reset_uid(reference_mid: str): reference_node = export_action.traceability_index.get_node_by_mid( MID(reference_mid) ) - assert isinstance(reference_node, Requirement) # FIXME: it might as well be a section? - requirement: Requirement = reference_node + assert isinstance(reference_node, Requirement) + # FIXME: it might as well be a section? document_tree_stats: DocumentTreeStats = ( DocumentUIDAnalyzer.analyze_document_tree(