diff --git a/strictdoc/core/traceability_index.py b/strictdoc/core/traceability_index.py
index c5aa43661..4ec2f8591 100644
--- a/strictdoc/core/traceability_index.py
+++ b/strictdoc/core/traceability_index.py
@@ -354,9 +354,10 @@ def get_node_by_uid_weak2(self, uid: str):
def get_linkable_node_by_uid(
self, uid
- ) -> Union[SDocNode, SDocSection, Anchor]:
+ ) -> Union[SDocDocument, SDocNode, SDocSection, Anchor]:
return assert_cast(
- self.get_node_by_uid(uid), (SDocNode, SDocSection, Anchor)
+ self.get_node_by_uid(uid),
+ (SDocDocument, SDocNode, SDocSection, Anchor),
)
def get_node_by_uid_weak(
@@ -383,16 +384,16 @@ def get_node_by_uid_weak(
def get_linkable_node_by_uid_weak(
self, uid
- ) -> Union[SDocNode, SDocSection, Anchor, None]:
+ ) -> Union[SDocDocument, SDocNode, SDocSection, Anchor, None]:
return assert_optional_cast(
self.graph_database.get_link_value_weak(
link_type=GraphLinkType.UID_TO_NODE, lhs_node=uid
),
- (SDocNode, SDocSection, Anchor),
+ (SDocDocument, SDocNode, SDocSection, Anchor),
)
def get_incoming_links(
- self, node: Union[SDocNode, SDocSection, Anchor]
+ self, node: Union[SDocDocument, SDocNode, SDocSection, Anchor]
) -> Optional[List[InlineLink]]:
incoming_links = self.graph_database.get_link_values_weak(
link_type=GraphLinkType.NODE_TO_INCOMING_LINKS,
@@ -448,6 +449,20 @@ def create_traceability_info(
source_file_rel_path, traceability_info, traceability_index
)
+ def create_document(self, document: SDocDocument) -> None:
+ assert isinstance(document, SDocDocument)
+ if document.reserved_uid is not None:
+ self.graph_database.create_link(
+ link_type=GraphLinkType.UID_TO_NODE,
+ lhs_node=document.reserved_uid,
+ rhs_node=document,
+ )
+ self.graph_database.create_link(
+ link_type=GraphLinkType.MID_TO_NODE,
+ lhs_node=document.reserved_mid,
+ rhs_node=document,
+ )
+
def create_section(self, section: SDocSection) -> None:
assert isinstance(section, SDocSection)
if section.reserved_uid is not None:
@@ -469,12 +484,14 @@ def create_inline_link(self, new_link: InlineLink):
if self.graph_database.has_link(
link_type=GraphLinkType.UID_TO_NODE, lhs_node=new_link.link
):
- node_or_anchor: Union[SDocNode, SDocSection, Anchor] = assert_cast(
+ node_or_anchor: Union[
+ SDocDocument, SDocNode, SDocSection, Anchor
+ ] = assert_cast(
self.graph_database.get_link_value(
link_type=GraphLinkType.UID_TO_NODE,
lhs_node=new_link.link,
),
- (SDocNode, SDocSection, Anchor),
+ (SDocDocument, SDocNode, SDocSection, Anchor),
)
self.graph_database.create_link(
link_type=GraphLinkType.NODE_TO_INCOMING_LINKS,
@@ -767,6 +784,21 @@ def update_disconnect_two_documents_if_no_links_left(
rhs_node=document.reserved_mid,
)
+ def delete_document(self, document: SDocDocument) -> None:
+ assert isinstance(document, SDocDocument), document
+
+ self.graph_database.delete_link(
+ link_type=GraphLinkType.MID_TO_NODE,
+ lhs_node=document.reserved_mid,
+ rhs_node=document,
+ )
+ if document.reserved_uid is not None:
+ self.graph_database.delete_link(
+ link_type=GraphLinkType.UID_TO_NODE,
+ lhs_node=document.reserved_uid,
+ rhs_node=document,
+ )
+
def delete_section(self, section: SDocSection) -> None:
assert isinstance(section, SDocSection), section
diff --git a/strictdoc/core/traceability_index_builder.py b/strictdoc/core/traceability_index_builder.py
index f5d04cb77..73994ae26 100644
--- a/strictdoc/core/traceability_index_builder.py
+++ b/strictdoc/core/traceability_index_builder.py
@@ -232,7 +232,9 @@ def create_from_document_tree(
),
(
GraphLinkType.UID_TO_NODE,
- OneToOneDictionary(str, (SDocNode, SDocSection, Anchor)),
+ OneToOneDictionary(
+ str, (SDocDocument, SDocNode, SDocSection, Anchor)
+ ),
),
(
GraphLinkType.UID_TO_REQUIREMENT_CONNECTIONS,
@@ -352,7 +354,12 @@ def create_from_document_tree(
lhs_node=document.reserved_mid,
rhs_node=document,
)
- # FIXME: Register Document with UID_TO_NODE
+ if document.uid:
+ graph_database.create_link(
+ link_type=GraphLinkType.UID_TO_NODE,
+ lhs_node=document.uid,
+ rhs_node=document,
+ )
document_tags: Dict[str, int] = {}
graph_database.create_link(
diff --git a/strictdoc/core/transforms/update_document_config.py b/strictdoc/core/transforms/update_document_config.py
index 59e293d4d..aae06e842 100644
--- a/strictdoc/core/transforms/update_document_config.py
+++ b/strictdoc/core/transforms/update_document_config.py
@@ -1,8 +1,9 @@
# mypy: disable-error-code="arg-type,no-untyped-call,no-untyped-def"
from collections import defaultdict
-from typing import Dict, List
+from typing import Dict, List, Optional
from strictdoc.backend.sdoc.models.document import SDocDocument
+from strictdoc.backend.sdoc.models.inline_link import InlineLink
from strictdoc.core.traceability_index import (
TraceabilityIndex,
)
@@ -36,11 +37,6 @@ def perform(self):
# Update the document.
document.title = form_object.document_title
- document.config.uid = (
- form_object.document_uid
- if len(form_object.document_uid) > 0
- else None
- )
document.config.version = (
form_object.document_version
if len(form_object.document_version) > 0
@@ -52,6 +48,16 @@ def perform(self):
else None
)
+ self.traceability_index.delete_document(document)
+
+ document.config.uid = (
+ form_object.document_uid
+ if len(form_object.document_uid) > 0
+ else None
+ )
+
+ self.traceability_index.create_document(document)
+
def validate(
self,
form_object: DocumentConfigFormObject,
@@ -59,9 +65,31 @@ def validate(
):
errors: Dict[str, List[str]] = defaultdict(list)
assert isinstance(document, SDocDocument)
+
if len(form_object.document_title) == 0:
errors["TITLE"].append("Document title must not be empty.")
+ # Ensure that UID doesn't have any incoming links if it is going to be
+ # renamed or removed
+ existing_uid = document.reserved_uid
+ new_uid = form_object.document_uid
+ if existing_uid is not None:
+ if new_uid is None or existing_uid != new_uid:
+ existing_incoming_links: Optional[List[InlineLink]] = (
+ self.traceability_index.get_incoming_links(document)
+ )
+ if (
+ existing_incoming_links is not None
+ and len(existing_incoming_links) > 0
+ ):
+ errors["UID"].append(
+ (
+ "Renaming a node UID when the node has "
+ "incoming links is not supported yet. "
+ "Please delete all incoming links first."
+ ),
+ )
+
if len(errors):
raise MultipleValidationError(
"Document form has not passed validation.", errors=errors
diff --git a/strictdoc/export/html/form_objects/requirement_form_object.py b/strictdoc/export/html/form_objects/requirement_form_object.py
index 9be666084..10d2c122e 100644
--- a/strictdoc/export/html/form_objects/requirement_form_object.py
+++ b/strictdoc/export/html/form_objects/requirement_form_object.py
@@ -193,7 +193,7 @@ def __init__(
context_document_mid: str,
fields: List[RequirementFormField],
reference_fields: List[RequirementReferenceFormField],
- exiting_requirement_uid: Optional[str],
+ existing_requirement_uid: Optional[str],
grammar: DocumentGrammar,
# FIXME: Better name
relation_types: List[str],
@@ -213,7 +213,7 @@ def __init__(
self.reference_fields: List[RequirementReferenceFormField] = (
reference_fields
)
- self.exiting_requirement_uid: Optional[str] = exiting_requirement_uid
+ self.existing_requirement_uid: Optional[str] = existing_requirement_uid
self.grammar: DocumentGrammar = grammar
self.relation_types: List[str] = relation_types
self.basic_free_text: bool = basic_free_text
@@ -225,7 +225,7 @@ def create_from_request(
requirement_mid: str,
request_form_data: FormData,
document: SDocDocument,
- exiting_requirement_uid: Optional[str],
+ existing_requirement_uid: Optional[str],
basic_free_text: bool,
) -> "RequirementFormObject":
request_form_data_as_list = [
@@ -326,7 +326,7 @@ def create_from_request(
context_document_mid=context_document_mid,
fields=form_fields,
reference_fields=form_ref_fields,
- exiting_requirement_uid=exiting_requirement_uid,
+ existing_requirement_uid=existing_requirement_uid,
grammar=grammar,
relation_types=element.get_relation_types(),
basic_free_text=basic_free_text,
@@ -384,7 +384,7 @@ def create_new(
context_document_mid=context_document_mid,
fields=form_fields,
reference_fields=[],
- exiting_requirement_uid=None,
+ existing_requirement_uid=None,
grammar=grammar,
relation_types=element.get_relation_types(),
basic_free_text=False,
@@ -474,7 +474,7 @@ def create_from_requirement(
context_document_mid=context_document_mid,
fields=form_fields,
reference_fields=form_refs_fields,
- exiting_requirement_uid=requirement.reserved_uid,
+ existing_requirement_uid=requirement.reserved_uid,
grammar=grammar,
relation_types=grammar_element_relations,
basic_free_text=requirement.basic_free_text,
@@ -617,7 +617,7 @@ def validate(
new_node_uid_or_none = new_node_uid
if new_node_uid_or_none is not None and (
- self.is_new or self.exiting_requirement_uid != new_node_uid_or_none
+ self.is_new or self.existing_requirement_uid != new_node_uid_or_none
):
existing_node_with_this_uid = (
traceability_index.get_node_by_uid_weak(new_node_uid_or_none)
@@ -636,10 +636,10 @@ def validate(
Ensure that UID doesn't have any incoming links if it is going to be
renamed or removed.
"""
- if self.exiting_requirement_uid is not None:
+ if self.existing_requirement_uid is not None:
if (
new_node_uid_or_none is None
- or self.exiting_requirement_uid != new_node_uid_or_none
+ or self.existing_requirement_uid != new_node_uid_or_none
):
existing_node: SDocNode = traceability_index.get_node_by_mid(
MID(self.requirement_mid)
@@ -725,8 +725,8 @@ def validate(
)
if (
- self.exiting_requirement_uid is not None
- and self.exiting_requirement_uid != requirement_uid
+ self.existing_requirement_uid is not None
+ and self.existing_requirement_uid != requirement_uid
):
if len(self.reference_fields) > 0:
self.add_error(
@@ -738,7 +738,7 @@ def validate(
)
requirement_connections: SDocNodeConnections = (
traceability_index.get_node_connections(
- self.exiting_requirement_uid
+ self.existing_requirement_uid
)
)
if len(requirement_connections.children):
diff --git a/strictdoc/server/routers/main_router.py b/strictdoc/server/routers/main_router.py
index 0fc917cd6..8effa00ca 100644
--- a/strictdoc/server/routers/main_router.py
+++ b/strictdoc/server/routers/main_router.py
@@ -870,7 +870,7 @@ async def create_requirement(request: Request):
requirement_mid=requirement_mid,
request_form_data=request_form_data,
document=document,
- exiting_requirement_uid=None,
+ existing_requirement_uid=None,
basic_free_text=basic_free_text,
)
)
@@ -1090,7 +1090,7 @@ async def document__update_requirement(request: Request):
requirement_mid=requirement_mid,
request_form_data=request_form_data,
document=document,
- exiting_requirement_uid=requirement.reserved_uid,
+ existing_requirement_uid=requirement.reserved_uid,
basic_free_text=requirement.basic_free_text,
)
)
@@ -1763,7 +1763,7 @@ def document__add_comment(
context_document_mid=context_document_mid,
fields=[],
reference_fields=[],
- exiting_requirement_uid=None,
+ existing_requirement_uid=None,
grammar=grammar,
relation_types=[],
basic_free_text=False,
@@ -1815,7 +1815,7 @@ def document__add_relation(
context_document_mid=context_document_mid,
fields=[],
reference_fields=[],
- exiting_requirement_uid=None,
+ existing_requirement_uid=None,
grammar=grammar,
relation_types=grammar_element_relations,
basic_free_text=False,
diff --git a/tests/end2end/helpers/screens/document/screen_document.py b/tests/end2end/helpers/screens/document/screen_document.py
index 442d9b148..a509bab55 100644
--- a/tests/end2end/helpers/screens/document/screen_document.py
+++ b/tests/end2end/helpers/screens/document/screen_document.py
@@ -98,3 +98,9 @@ def do_drag_first_toc_node_to_the_second(self) -> None:
raise TimeoutError(
"StrictDoc custom timeout: Moving element in the TOC"
)
+
+ def do_click_on_tree_document(self, doc_order: int = 1) -> None:
+ self.test_case.assert_element_not_present("//sdoc-modal", by=By.XPATH)
+ self.test_case.click_xpath(
+ f'(//*[@data-testid="tree-document-link"])[{doc_order}]'
+ )
diff --git a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/expected_output/document1.sdoc b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/expected_output/document1.sdoc
new file mode 100644
index 000000000..066064096
--- /dev/null
+++ b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/expected_output/document1.sdoc
@@ -0,0 +1,9 @@
+[DOCUMENT]
+TITLE: Document 1
+
+[REQUIREMENT]
+UID: REQ-1
+TITLE: Requirement 1
+STATEMENT: >>>
+Modified. [LINK: DOC-2]
+<<<
diff --git a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/expected_output/document2.sdoc b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/expected_output/document2.sdoc
new file mode 100644
index 000000000..fdeecdd02
--- /dev/null
+++ b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/expected_output/document2.sdoc
@@ -0,0 +1,3 @@
+[DOCUMENT]
+TITLE: Document 2
+UID: DOC-2
diff --git a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/input/document1.sdoc b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/input/document1.sdoc
new file mode 100644
index 000000000..089c95f00
--- /dev/null
+++ b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/input/document1.sdoc
@@ -0,0 +1,9 @@
+[DOCUMENT]
+TITLE: Document 1
+
+[REQUIREMENT]
+UID: REQ-1
+TITLE: Requirement 1
+STATEMENT: >>>
+Nothing here yet.
+<<<
diff --git a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/test_case.py b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/test_case.py
new file mode 100644
index 000000000..1d938897e
--- /dev/null
+++ b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_create/create_document_with_uid_and_node_with_LINK/test_case.py
@@ -0,0 +1,66 @@
+from tests.end2end.e2e_case import E2ECase
+from tests.end2end.end2end_test_setup import End2EndTestSetup
+from tests.end2end.helpers.components.node.document_root import Form_EditConfig
+from tests.end2end.helpers.screens.document.form_edit_requirement import (
+ Form_EditRequirement,
+)
+from tests.end2end.helpers.screens.project_index.form_add_document import (
+ Form_AddDocument,
+)
+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__)
+
+ # Run server.
+ 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")
+
+ # Create Document 2 and change UID to DOC-2
+ form_add_document: Form_AddDocument = (
+ screen_project_index.do_open_modal_form_add_document()
+ )
+ form_add_document.do_fill_in_title("Document 2")
+ form_add_document.do_fill_in_path("document2.sdoc")
+ form_add_document.do_form_submit()
+
+ screen_document = screen_project_index.do_click_on_the_document(2)
+ screen_document.assert_on_screen_document()
+ screen_document.assert_header_document_title("Document 2")
+
+ document_node = screen_document.get_root_node()
+ form_edit_document: Form_EditConfig = (
+ document_node.do_open_form_edit_config()
+ )
+ form_edit_document.do_fill_in_document_uid("DOC-2")
+ form_edit_document.do_form_submit()
+
+ # Go to Document 1 and link requirement statement to DOC-2
+ screen_document.do_click_on_tree_document(1)
+ screen_document.assert_header_document_title("Document 1")
+
+ req_node_1 = screen_document.get_node(1)
+ req_node_edit_form: Form_EditRequirement = (
+ req_node_1.do_open_form_edit_requirement()
+ )
+ req_node_edit_form.do_fill_in_field_statement(
+ "Modified. [LINK: DOC-2]"
+ )
+ req_node_edit_form.do_form_submit()
+
+ # Click new link and verify it brings us to Document 2
+ self.click_xpath("//sdoc-node//a[contains(., 'Document 2')]")
+ screen_document.assert_header_document_title("Document 2")
+
+ assert test_setup.compare_sandbox_and_expected_output()
diff --git a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/expected_output/document.sdoc b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/expected_output/document.sdoc
index b58dc825e..dded93345 100644
--- a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/expected_output/document.sdoc
+++ b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/expected_output/document.sdoc
@@ -1,10 +1,11 @@
[DOCUMENT]
TITLE: Document 1
+UID: DOC-1
[REQUIREMENT]
UID: REQ-1
STATEMENT: >>>
-Hello world
+Hello world [LINK: DOC-1]
<<<
[REQUIREMENT]
diff --git a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/input/document.sdoc b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/input/document.sdoc
index b58dc825e..dded93345 100644
--- a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/input/document.sdoc
+++ b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/input/document.sdoc
@@ -1,10 +1,11 @@
[DOCUMENT]
TITLE: Document 1
+UID: DOC-1
[REQUIREMENT]
UID: REQ-1
STATEMENT: >>>
-Hello world
+Hello world [LINK: DOC-1]
<<<
[REQUIREMENT]
diff --git a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/test_case.py b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/test_case.py
index 2386fca45..8c56395f0 100644
--- a/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/test_case.py
+++ b/tests/end2end/screens/document/_cross_cutting/LINK_and_ANCHOR/node/_update/_validations/update_node_must_prevent_renaming_or_removing_UID_when_incoming_links/test_case.py
@@ -1,5 +1,6 @@
from tests.end2end.e2e_case import E2ECase
from tests.end2end.end2end_test_setup import End2EndTestSetup
+from tests.end2end.helpers.components.node.document_root import Form_EditConfig
from tests.end2end.helpers.screens.document.form_edit_requirement import (
Form_EditRequirement,
)
@@ -28,6 +29,15 @@ def test(self):
screen_document.assert_on_screen_document()
screen_document.assert_header_document_title("Document 1")
+ document_node = screen_document.get_root_node()
+ form_edit_document: Form_EditConfig = (
+ document_node.do_open_form_edit_config()
+ )
+ form_edit_document.do_fill_in_document_uid("DOC-2")
+ form_edit_document.do_form_submit_and_catch_error(
+ "Renaming a node UID when the node has incoming links is not supported yet. Please delete all incoming links first."
+ )
+
text_node_1 = screen_document.get_node(1)
form_edit_requirement: Form_EditRequirement = (
text_node_1.do_open_form_edit_requirement()
diff --git a/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input1.sdoc b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input1.sdoc
new file mode 100644
index 000000000..1c06a23dc
--- /dev/null
+++ b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input1.sdoc
@@ -0,0 +1,10 @@
+[DOCUMENT]
+TITLE: Document 1
+UID: DOC-1
+
+[REQUIREMENT]
+UID: REQ-2
+TITLE: Foo Bar
+STATEMENT: >>>
+Read [LINK: DOC-1] then [LINK: DOC-2].
+<<<
diff --git a/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input2.sdoc b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input2.sdoc
new file mode 100644
index 000000000..07cc08ec1
--- /dev/null
+++ b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input2.sdoc
@@ -0,0 +1,8 @@
+[DOCUMENT]
+TITLE: Document 2
+UID: DOC-2
+
+[TEXT]
+STATEMENT: >>>
+Read [LINK: DOC-2] then [LINK: DOC-1].
+<<<
diff --git a/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/test.itest b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/test.itest
new file mode 100644
index 000000000..7085c4b97
--- /dev/null
+++ b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/test.itest
@@ -0,0 +1,10 @@
+RUN: %strictdoc export %S --output-dir Output | filecheck %s --dump-input=fail
+CHECK: Published: Document 1
+
+RUN: %cat %S/Output/html/%THIS_TEST_FOLDER/input1.html | filecheck %s --dump-input=fail --check-prefix CHECK-HTML-DOC1
+
+CHECK-HTML-DOC1: Read 🔗 Document 1 then 🔗 Document 2.
+
+RUN: %cat %S/Output/html/%THIS_TEST_FOLDER/input2.html | filecheck %s --dump-input=fail --check-prefix CHECK-HTML-DOC2
+
+CHECK-HTML-DOC2: Read 🔗 Document 2 then 🔗 Document 1.
diff --git a/tests/integration/commands/export/rst/52_link_to_document/expected/input1.rst b/tests/integration/commands/export/rst/52_link_to_document/expected/input1.rst
new file mode 100644
index 000000000..d67bf1864
--- /dev/null
+++ b/tests/integration/commands/export/rst/52_link_to_document/expected/input1.rst
@@ -0,0 +1,18 @@
+.. _DOC-1:
+
+Document 1
+$$$$$$$$$$
+
+.. _REQ-2:
+
+Foo Bar
+=======
+
+.. list-table::
+ :align: left
+ :header-rows: 0
+
+ * - **UID:**
+ - REQ-2
+
+Read :ref:`Document 1 ` then :ref:`Document 2 `.
diff --git a/tests/integration/commands/export/rst/52_link_to_document/expected/input2.rst b/tests/integration/commands/export/rst/52_link_to_document/expected/input2.rst
new file mode 100644
index 000000000..54e8d3202
--- /dev/null
+++ b/tests/integration/commands/export/rst/52_link_to_document/expected/input2.rst
@@ -0,0 +1,6 @@
+.. _DOC-2:
+
+Document 2
+$$$$$$$$$$
+
+Read :ref:`Document 2 ` then :ref:`Document 1 `.
diff --git a/tests/integration/commands/export/rst/52_link_to_document/input1.sdoc b/tests/integration/commands/export/rst/52_link_to_document/input1.sdoc
new file mode 100644
index 000000000..1c06a23dc
--- /dev/null
+++ b/tests/integration/commands/export/rst/52_link_to_document/input1.sdoc
@@ -0,0 +1,10 @@
+[DOCUMENT]
+TITLE: Document 1
+UID: DOC-1
+
+[REQUIREMENT]
+UID: REQ-2
+TITLE: Foo Bar
+STATEMENT: >>>
+Read [LINK: DOC-1] then [LINK: DOC-2].
+<<<
diff --git a/tests/integration/commands/export/rst/52_link_to_document/input2.sdoc b/tests/integration/commands/export/rst/52_link_to_document/input2.sdoc
new file mode 100644
index 000000000..07cc08ec1
--- /dev/null
+++ b/tests/integration/commands/export/rst/52_link_to_document/input2.sdoc
@@ -0,0 +1,8 @@
+[DOCUMENT]
+TITLE: Document 2
+UID: DOC-2
+
+[TEXT]
+STATEMENT: >>>
+Read [LINK: DOC-2] then [LINK: DOC-1].
+<<<
diff --git a/tests/integration/commands/export/rst/52_link_to_document/test.itest b/tests/integration/commands/export/rst/52_link_to_document/test.itest
new file mode 100644
index 000000000..fd136ab42
--- /dev/null
+++ b/tests/integration/commands/export/rst/52_link_to_document/test.itest
@@ -0,0 +1,6 @@
+RUN: %strictdoc export %S --formats=rst --output-dir Output
+
+RUN: %check_exists --file "%S/Output/rst/input1.rst"
+
+RUN: %diff "%S/Output/rst/input1.rst" "%S/expected/input1.rst"
+RUN: %diff "%S/Output/rst/input2.rst" "%S/expected/input2.rst"
diff --git a/tests/unit/helpers/test_document_builder.py b/tests/unit/helpers/test_document_builder.py
index 72905e8c8..7ebb4dc7f 100644
--- a/tests/unit/helpers/test_document_builder.py
+++ b/tests/unit/helpers/test_document_builder.py
@@ -15,8 +15,8 @@
class DocumentBuilder:
- def __init__(self):
- self.document: SDocDocument = self._create_empty_document()
+ def __init__(self, uid="DOC-1"):
+ self.document: SDocDocument = self._create_empty_document(uid)
self.requirements = []
def add_requirement(self, uid):
@@ -77,11 +77,11 @@ def build(self):
return self.document
@staticmethod
- def _create_empty_document() -> SDocDocument:
+ def _create_empty_document(uid: str) -> SDocDocument:
config = DocumentConfig(
parent=None,
version="0.0.1",
- uid="DOC-1",
+ uid=uid,
classification=None,
requirement_prefix=None,
root=None,
diff --git a/tests/unit/strictdoc/core/test_traceability_index.py b/tests/unit/strictdoc/core/test_traceability_index.py
index c037486f3..4d78f1c1a 100644
--- a/tests/unit/strictdoc/core/test_traceability_index.py
+++ b/tests/unit/strictdoc/core/test_traceability_index.py
@@ -133,11 +133,11 @@ def test__adding_parent_link__01__two_requirements_in_one_document():
def test__adding_parent_link__02__two_requirements_in_two_documents():
- document_builder_1 = DocumentBuilder()
+ document_builder_1 = DocumentBuilder("DOC-1")
requirement1 = document_builder_1.add_requirement("REQ-001")
document_1 = document_builder_1.build()
- document_builder_2 = DocumentBuilder()
+ document_builder_2 = DocumentBuilder("DOC-2")
requirement2 = document_builder_2.add_requirement("REQ-002")
document_2 = document_builder_2.build()
diff --git a/tests/unit/strictdoc/core/transforms/test_update_requirement.py b/tests/unit/strictdoc/core/transforms/test_update_requirement.py
index 5c98f9fef..4e5e01bbe 100644
--- a/tests/unit/strictdoc/core/transforms/test_update_requirement.py
+++ b/tests/unit/strictdoc/core/transforms/test_update_requirement.py
@@ -323,12 +323,12 @@ def test_25_single_document_remove_child_relation():
def test_26_two_documents_remove_child_relation():
# Document 1
- document_builder = DocumentBuilder()
+ document_builder = DocumentBuilder("DOC-1")
requirement1 = document_builder.add_requirement("REQ-001")
document_1 = document_builder.build()
# Document 2
- document_builder = DocumentBuilder()
+ document_builder = DocumentBuilder("DOC-2")
requirement2 = document_builder.add_requirement("REQ-002")
document_builder.add_requirement_relation(
relation_type="Child",