diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index d3c19dadd..5f99342eb 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -20,6 +20,8 @@ Downstream code should now depend on the diagram services module for that bean. === Bug fixes +- https://github.com/eclipse-syson/syson/issues/2359[#2359] [diagrams] Fix `Comment` graphical nodes so changing their background color no longer changes their border color. + === Improvements === New features diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLImportedPackageNodeAppearanceControllerTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLImportedPackageNodeAppearanceControllerTests.java index f7366b2f0..3003d9215 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLImportedPackageNodeAppearanceControllerTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLImportedPackageNodeAppearanceControllerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -29,6 +29,7 @@ import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.GivenSysONServer; import org.eclipse.syson.application.controllers.diagrams.graphql.EditSysMLImportedPackageNodeAppearanceMutationRunner; import org.eclipse.syson.application.data.AllCustomNodesProjectData; import org.eclipse.syson.application.nodes.SysMLImportedPackageNodeStyle; @@ -40,8 +41,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; @@ -82,9 +81,7 @@ private Flux givenDiagramSubscription() { } @DisplayName("GIVEN a diagram, WHEN we edit all its appearance and reset changes, THEN the diagram is properly updated") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramIsProperlyUpdated() { var flux = this.givenDiagramSubscription(); @@ -166,9 +163,7 @@ public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramI } @DisplayName("GIVEN a diagram, WHEN we edit two of its appearance and reset one change, THEN only one property is reset") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditTwoOfItsAppearanceAndResetOneChangeThenOnlyOnePropertyIsReset() { var flux = this.givenDiagramSubscription(); diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLNoteNodeAppearanceControllerTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLNoteNodeAppearanceControllerTests.java index 6186762de..b8faeebad 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLNoteNodeAppearanceControllerTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLNoteNodeAppearanceControllerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -29,6 +29,7 @@ import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.GivenSysONServer; import org.eclipse.syson.application.controllers.diagrams.graphql.EditSysMLNoteNodeAppearanceMutationRunner; import org.eclipse.syson.application.data.AllCustomNodesProjectData; import org.eclipse.syson.application.nodes.SysMLNoteNodeStyle; @@ -40,8 +41,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; @@ -82,9 +81,7 @@ private Flux givenDiagramSubscription() { } @DisplayName("GIVEN a diagram, WHEN we edit all its appearance and reset changes, THEN the diagram is properly updated") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramIsProperlyUpdated() { var flux = this.givenDiagramSubscription(); @@ -166,9 +163,7 @@ public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramI } @DisplayName("GIVEN a diagram, WHEN we edit two of its appearance and reset one change, THEN only one property is reset") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditTwoOfItsAppearanceAndResetOneChangeThenOnlyOnePropertyIsReset() { var flux = this.givenDiagramSubscription(); @@ -209,6 +204,7 @@ public void givenDiagramWhenWeEditTwoOfItsAppearanceAndResetOneChangeThenOnlyOne .allMatch(node -> node.getStyle() instanceof SysMLNoteNodeStyle) .extracting(node -> (SysMLNoteNodeStyle) node.getStyle()) .allMatch(sysMLNoteNodeStyle -> "red".equals(sysMLNoteNodeStyle.getBackground())) + .allMatch(sysMLNoteNodeStyle -> "#000000".equals(sysMLNoteNodeStyle.getBorderColor())) .allMatch(sysMLNoteNodeStyle -> sysMLNoteNodeStyle.getBorderSize() == 5); }); @@ -230,6 +226,7 @@ public void givenDiagramWhenWeEditTwoOfItsAppearanceAndResetOneChangeThenOnlyOne .allMatch(node -> node.getStyle() instanceof SysMLNoteNodeStyle) .extracting(node -> (SysMLNoteNodeStyle) node.getStyle()) .allMatch(sysMLNoteNodeStyle -> "red".equals(sysMLNoteNodeStyle.getBackground())) + .allMatch(sysMLNoteNodeStyle -> "#000000".equals(sysMLNoteNodeStyle.getBorderColor())) .allMatch(sysMLNoteNodeStyle -> sysMLNoteNodeStyle.getBorderSize() == 1); }); @@ -243,4 +240,4 @@ public void givenDiagramWhenWeEditTwoOfItsAppearanceAndResetOneChangeThenOnlyOne .verify(Duration.ofSeconds(10)); } -} \ No newline at end of file +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLPackageNodeAppearanceControllerTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLPackageNodeAppearanceControllerTests.java index c120ced0f..5bde21b26 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLPackageNodeAppearanceControllerTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLPackageNodeAppearanceControllerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -29,6 +29,7 @@ import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.GivenSysONServer; import org.eclipse.syson.application.controllers.diagrams.graphql.EditSysMLPackageNodeAppearanceMutationRunner; import org.eclipse.syson.application.data.AllCustomNodesProjectData; import org.eclipse.syson.application.nodes.SysMLPackageNodeStyle; @@ -40,8 +41,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; @@ -82,9 +81,7 @@ private Flux givenDiagramSubscription() { } @DisplayName("GIVEN a diagram, WHEN we edit all its appearance and reset changes, THEN the diagram is properly updated") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramIsProperlyUpdated() { var flux = this.givenDiagramSubscription(); @@ -166,9 +163,7 @@ public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramI } @DisplayName("GIVEN a diagram, WHEN we edit two of its appearance and reset one change, THEN only one property is reset") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditTwoOfItsAppearanceAndResetOneChangeThenOnlyOnePropertyIsReset() { var flux = this.givenDiagramSubscription(); diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLViewFrameNodeAppearanceControllerTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLViewFrameNodeAppearanceControllerTests.java index 0ca4f2cf1..fc3c78563 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLViewFrameNodeAppearanceControllerTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/EditSysMLViewFrameNodeAppearanceControllerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -29,6 +29,7 @@ import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.GivenSysONServer; import org.eclipse.syson.application.controllers.diagrams.graphql.EditSysMLViewFrameNodeAppearanceMutationRunner; import org.eclipse.syson.application.data.AllCustomNodesProjectData; import org.eclipse.syson.application.nodes.SysMLViewFrameNodeStyle; @@ -40,8 +41,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; @@ -82,9 +81,7 @@ private Flux givenDiagramSubscription() { } @DisplayName("GIVEN a diagram, WHEN we edit all its appearance and reset changes, THEN the diagram is properly updated") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramIsProperlyUpdated() { var flux = this.givenDiagramSubscription(); @@ -169,9 +166,7 @@ public void givenDiagramWhenWeEditAllItsAppearanceAndResetChangesThenTheDiagramI } @DisplayName("GIVEN a diagram, WHEN we edit two of its appearance and reset one change, THEN only one property is reset") - @Sql(scripts = { AllCustomNodesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, - config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @GivenSysONServer({ AllCustomNodesProjectData.SCRIPT_PATH }) @Test public void givenDiagramWhenWeEditTwoOfItsAppearanceAndResetOneChangeThenOnlyOnePropertyIsReset() { var flux = this.givenDiagramSubscription(); diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ProjectWithLibraryDependencyTestProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ProjectWithLibraryDependencyTestProjectData.java index d4e44aa7f..d7df0b716 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ProjectWithLibraryDependencyTestProjectData.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ProjectWithLibraryDependencyTestProjectData.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -26,6 +26,8 @@ public class ProjectWithLibraryDependencyTestProjectData { public static final String PROJECT_NAME = "ProjectUsingMyLibraryV1"; + public static final String PROJECT_ID = "3a06824a-a634-42ea-918f-817adc4b17a3"; + public static final String LIBRARY_PROJECT_ID = "c493d950-f81a-4e56-a454-f9e5ce1c839f"; public static final String EDITING_CONTEXT = "09ba2e1b-7f2b-4c5f-a536-9624ba470f74"; diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ProjectWithoutLibraryDependencyTestProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ProjectWithoutLibraryDependencyTestProjectData.java new file mode 100644 index 000000000..180844edc --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ProjectWithoutLibraryDependencyTestProjectData.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.data; + +import org.eclipse.syson.application.libraries.SysONLibraryImportTestServer; + +/** + * Identifiers for the "ProjectUsingNoLibraries" project. + *

+ * Tests relying on this test data should use the {@link SysONLibraryImportTestServer} annotation to ensure the project + * is loaded in the application. + *

+ * + * @author flatombe + */ +public class ProjectWithoutLibraryDependencyTestProjectData { + + public static final String PROJECT_ID = "85ed22a4-81ad-46ac-a584-bf7259cd8568"; + + public static final String EDITING_CONTEXT = "bb9678c1-8f2e-440f-be06-628d1930ffc2"; + +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByCopyTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByCopyTests.java index fcbab1aed..0dc6a4051 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByCopyTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByCopyTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -20,12 +20,11 @@ import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.sirius.components.core.api.IEditingContextSearchService; -import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; import org.eclipse.sirius.web.application.library.dto.ImportLibrariesInput; import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter; import org.eclipse.sirius.web.domain.boundedcontexts.library.Library; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; +import org.eclipse.syson.InvalidateStandardLibrariesCache; import org.eclipse.syson.application.libraries.SysONLibraryImportTestServer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -33,7 +32,6 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; @@ -49,22 +47,25 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ExtendWith(OutputCaptureExtension.class) @TestInstance(Lifecycle.PER_CLASS) +@InvalidateStandardLibrariesCache public class SysONLibraryImportByCopyTests extends SysONLibraryImportTests { - @Autowired - private IEditingContextSearchService editingContextSearchService; + private ResourceSet projectResourceSet; @Override @BeforeEach public void initializeServerState() { super.initializeServerState(); - final ImportLibrariesInput importLibrariesInput = new ImportLibrariesInput( - UUID.randomUUID(), - super.projectEditingContext.getId(), - "copy", - List.of(this.loadMyLibraryV1().getId().toString())); - this.projectEditingContextEventProcessor.handle(importLibrariesInput); + this.executeInEditingContext(this.projectEditingContextId, projectEditingContext -> { + final ImportLibrariesInput importLibrariesInput = new ImportLibrariesInput( + UUID.randomUUID(), + this.projectEditingContextId, + "copy", + List.of(this.loadMyLibraryV1().getId().toString())); + this.createEditingContextEventProcessor(projectEditingContext).handle(importLibrariesInput); + this.projectResourceSet = projectEditingContext.getDomain().getResourceSet(); + }); } @Test @@ -95,20 +96,29 @@ public void testImportingProjectDependenciesHaveNotChanged(CapturedOutput captur @DisplayName("The project importing the libraries now has a copy of the library documents") @SysONLibraryImportTestServer public void testImportingProjectSemanticDataHaveChanged(CapturedOutput capturedOutput) { - final ResourceSet projectResourceSet = ((IEMFEditingContext) this.projectEditingContext).getDomain().getResourceSet(); final SemanticData myLibraryV1SemanticData = this.loadMyLibraryV1SemanticData(); - final List libraryResources = ((IEMFEditingContext) this.editingContextSearchService.findById(myLibraryV1SemanticData.getId().toString()) - .orElseThrow()) - .getDomain() - .getResourceSet() - .getResources(); + final List libraryResources = this.loadPublishedLibraryResources(myLibraryV1SemanticData.getId().toString()); + this.assertImportedProjectResources(this.projectResourceSet, myLibraryV1SemanticData, libraryResources); + } + + /** + * Asserts that the project contains independent copies of the library resources. + * + * @param resourceSet + * the project resource set + * @param myLibraryV1SemanticData + * the imported library semantic data + * @param libraryResources + * the source library resources + */ + private void assertImportedProjectResources(final ResourceSet resourceSet, final SemanticData myLibraryV1SemanticData, final List libraryResources) { // The importing project already had a resource before the import. - assertThat(projectResourceSet.getResources()).hasSize(libraryResources.size() + 1); + assertThat(resourceSet.getResources()).hasSize(libraryResources.size() + 1); // Now we want to compare the new contents in our project with the contents of the library (disregarding the // default libraries). - final List projectResourcesCopiedFromLibrary = projectResourceSet.getResources().stream() - .skip(projectResourceSet.getResources().size() - myLibraryV1SemanticData.getDocuments().size()) + final List projectResourcesCopiedFromLibrary = resourceSet.getResources().stream() + .skip(resourceSet.getResources().size() - myLibraryV1SemanticData.getDocuments().size()) .toList(); final List libraryCopiedResources = libraryResources.stream() .skip(libraryResources.size() - myLibraryV1SemanticData.getDocuments().size()) diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByReferenceTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByReferenceTests.java index ccec0d599..8629dee73 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByReferenceTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportByReferenceTests.java @@ -27,13 +27,12 @@ import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.sirius.components.collaborative.validation.dto.ValidationEventInput; import org.eclipse.sirius.components.collaborative.validation.dto.ValidationRefreshedEventPayload; -import org.eclipse.sirius.components.core.api.IEditingContextSearchService; -import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; import org.eclipse.sirius.web.application.library.dto.ImportLibrariesInput; import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter; import org.eclipse.sirius.web.domain.boundedcontexts.library.Library; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; import org.eclipse.sirius.web.tests.graphql.ValidationEventSubscriptionRunner; +import org.eclipse.syson.InvalidateStandardLibrariesCache; import org.eclipse.syson.application.libraries.SysONLibraryImportTestServer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -59,11 +58,9 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ExtendWith(OutputCaptureExtension.class) @TestInstance(Lifecycle.PER_CLASS) +@InvalidateStandardLibrariesCache public class SysONLibraryImportByReferenceTests extends SysONLibraryImportTests { - @Autowired - private IEditingContextSearchService editingContextSearchService; - @Autowired private ValidationEventSubscriptionRunner validationEventSubscriptionRunner; @@ -106,13 +103,24 @@ public void testImportingProjectDependenciesHaveChanged(CapturedOutput capturedO @SysONLibraryImportTestServer public void testImportingProjectSemanticDataHaveChanged(CapturedOutput capturedOutput) { this.importLibraryV1(); - final ResourceSet projectResourceSet = ((IEMFEditingContext) this.projectEditingContext).getDomain().getResourceSet(); final SemanticData myLibraryV1SemanticData = this.loadMyLibraryV1SemanticData(); - final List libraryResources = ((IEMFEditingContext) this.editingContextSearchService.findById(myLibraryV1SemanticData.getId().toString()) - .orElseThrow()) - .getDomain() - .getResourceSet() - .getResources(); + this.executeInEditingContext(this.projectEditingContextId, projectEditingContext -> { + final List libraryResources = this.loadPublishedLibraryResources(myLibraryV1SemanticData.getId().toString()); + this.assertImportedProjectResources(projectEditingContext.getDomain().getResourceSet(), myLibraryV1SemanticData, libraryResources); + }); + } + + /** + * Asserts that the project contains local library resources linked to their source library metadata. + * + * @param projectResourceSet + * the project resource set + * @param myLibraryV1SemanticData + * the imported library semantic data + * @param libraryResources + * the source library resources + */ + private void assertImportedProjectResources(final ResourceSet projectResourceSet, final SemanticData myLibraryV1SemanticData, final List libraryResources) { // The importing project already had a resource before the import. assertThat(projectResourceSet.getResources()).hasSize(libraryResources.size() + 1); @@ -162,7 +170,7 @@ public void testImportingProjectSemanticDataHaveChanged(CapturedOutput capturedO @DisplayName("The validation view of the project displays the same amount of validation rules after the import") @SysONLibraryImportTestServer public void testValidationAfterImport() { - ValidationEventInput validationEventInput = new ValidationEventInput(UUID.randomUUID(), this.projectEditingContext.getId(), "validation://"); + ValidationEventInput validationEventInput = new ValidationEventInput(UUID.randomUUID(), this.projectEditingContextId, "validation://"); var validationFlux = this.validationEventSubscriptionRunner.run(validationEventInput).flux(); AtomicReference diagnosticCount = new AtomicReference<>(0); @@ -199,11 +207,13 @@ public void testValidationAfterImport() { } private void importLibraryV1() { - final ImportLibrariesInput importLibrariesInput = new ImportLibrariesInput( - UUID.randomUUID(), - super.projectEditingContext.getId(), - "import", - List.of(this.loadMyLibraryV1().getId().toString())); - this.projectEditingContextEventProcessor.handle(importLibrariesInput); + this.executeInEditingContext(this.projectEditingContextId, projectEditingContext -> { + final ImportLibrariesInput importLibrariesInput = new ImportLibrariesInput( + UUID.randomUUID(), + this.projectEditingContextId, + "import", + List.of(this.loadMyLibraryV1().getId().toString())); + this.createEditingContextEventProcessor(projectEditingContext).handle(importLibrariesInput); + }); } } diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportTests.java index 078ac8713..0631eb70c 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/imports/SysONLibraryImportTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -16,27 +16,33 @@ import java.time.Instant; import java.util.List; -import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessor; import org.eclipse.sirius.components.collaborative.editingcontext.EditingContextEventProcessorFactory; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IEditingContextSearchService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.core.api.SuccessPayload; import org.eclipse.sirius.components.emf.ResourceMetadataAdapter; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionInput; +import org.eclipse.sirius.components.graphql.tests.api.IExecuteEditingContextFunctionRunner; import org.eclipse.sirius.web.domain.boundedcontexts.library.Library; import org.eclipse.sirius.web.domain.boundedcontexts.library.services.api.ILibrarySearchService; -import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; -import org.eclipse.sirius.web.domain.boundedcontexts.project.services.api.IProjectSearchService; import org.eclipse.sirius.web.domain.boundedcontexts.projectsemanticdata.ProjectSemanticData; import org.eclipse.sirius.web.domain.boundedcontexts.projectsemanticdata.services.api.IProjectSemanticDataSearchService; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataSearchService; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.application.data.ProjectWithLibraryDependencyTestProjectData; +import org.eclipse.syson.application.data.ProjectWithoutLibraryDependencyTestProjectData; +import org.eclipse.syson.sysml.metamodel.util.ElementUtil; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.ScrollPosition; import org.springframework.data.jdbc.core.mapping.AggregateReference; /** @@ -48,9 +54,7 @@ */ public abstract class SysONLibraryImportTests extends AbstractIntegrationTests { - protected IEditingContext projectEditingContext; - - protected IEditingContextEventProcessor projectEditingContextEventProcessor; + protected String projectEditingContextId; protected Instant myLibraryV1LastModifiedInstantBefore; @@ -59,23 +63,23 @@ public abstract class SysONLibraryImportTests extends AbstractIntegrationTests { @Autowired private IGivenInitialServerState givenInitialServerState; - @Autowired - private IProjectSearchService projectSearchService; - @Autowired private ISemanticDataSearchService semanticDataSearchService; @Autowired private ILibrarySearchService librarySearchService; + @Autowired + private IProjectSemanticDataSearchService projectSemanticDataSearchService; + @Autowired private IEditingContextSearchService editingContextSearchService; @Autowired - private IProjectSemanticDataSearchService projectSemanticDataSearchService; + private IExecuteEditingContextFunctionRunner executeEditingContextFunctionRunner; @Autowired - private EditingContextEventProcessorFactory factory; + private EditingContextEventProcessorFactory editingContextEventProcessorFactory; @BeforeEach public void initializeServerState() { @@ -91,8 +95,7 @@ public void initializeServerState() { assertThat(projectSemanticData.getDependencies().isEmpty()); assertThat(projectSemanticData.getDocuments()).hasSize(1); - this.projectEditingContext = this.editingContextSearchService.findById(projectSemanticData.getId().toString()).orElseThrow(); - this.projectEditingContextEventProcessor = this.factory.createEditingContextEventProcessor(this.projectEditingContext); + this.projectEditingContextId = projectSemanticData.getId().toString(); this.myLibraryV1LastModifiedInstantBefore = this.loadMyLibraryV1().getLastModifiedOn(); this.myLibraryV1SemanticDataLastModifiedInstantBefore = myLibraryV1SemanticData.getLastModifiedOn(); @@ -101,7 +104,7 @@ public void initializeServerState() { protected Library loadMyLibraryV1() { return this.librarySearchService .findByNamespaceAndNameAndVersion( - this.loadProjectByName("MyLibrary").getId(), + ProjectWithLibraryDependencyTestProjectData.LIBRARY_PROJECT_ID, "MyLibrary", "v1") .orElseThrow(); @@ -112,8 +115,9 @@ protected SemanticData loadMyLibraryV1SemanticData() { } protected SemanticData loadProjectSemanticData() { - final Project project = this.loadProjectByName("ProjectUsingNoLibraries"); - final ProjectSemanticData projectSemanticData = this.projectSemanticDataSearchService.findByProjectId(AggregateReference.to(project.getId())).orElseThrow(); + final ProjectSemanticData projectSemanticData = this.projectSemanticDataSearchService + .findByProjectId(AggregateReference.to(ProjectWithoutLibraryDependencyTestProjectData.PROJECT_ID)) + .orElseThrow(); return this.semanticDataSearchService.findById(projectSemanticData.getSemanticData().getId()).orElseThrow(); } @@ -126,14 +130,58 @@ protected String getResourceName(final Resource resource) { .getName(); } - private Project loadProjectByName(final String projectName) { - final List candidates = this.projectSearchService.findAll(ScrollPosition.keyset(), 10, Map.of()).stream() - .filter(project -> project.getName().equals(projectName)) - .toList(); - if (candidates.size() != 1) { - return null; - } else { - return candidates.get(0); - } + /** + * Loads the resources of a published library editing context. + *

+ * Published libraries are read-only, so the editing-context dispatcher rejects the test function runner for their + * semantic data. Loading this context through the search service is therefore required to inspect its resources. + *

+ * + * @param librarySemanticDataId + * the semantic data identifier of the published library + * @return the library resources + */ + protected List loadPublishedLibraryResources(final String librarySemanticDataId) { + return ((IEMFEditingContext) this.editingContextSearchService.findById(librarySemanticDataId).orElseThrow()) + .getDomain() + .getResourceSet() + .getResources(); + } + + /** + * Executes a function using an editing context initialized with its standard libraries. + * + * @param editingContextId + * the identifier of the editing context to use + * @param consumer + * the function to execute + */ + protected void executeInEditingContext(final String editingContextId, final Consumer consumer) { + var input = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), editingContextId, (editingContext, executeInput) -> { + assertThat(editingContext) + .as("Editing context %s must be an EMF editing context", editingContextId) + .isInstanceOf(IEMFEditingContext.class); + IEMFEditingContext emfEditingContext = (IEMFEditingContext) editingContext; + assertThat(emfEditingContext.getDomain().getResourceSet().getResources()) + .as("Editing context %s must contain standard libraries", editingContextId) + .anyMatch(ElementUtil::isStandardLibraryResource); + consumer.accept(emfEditingContext); + return new SuccessPayload(executeInput.id()); + }); + + IPayload payload = this.executeEditingContextFunctionRunner.execute(input).block(); + assertThat(payload).isInstanceOf(SuccessPayload.class); } + + /** + * Creates an event processor for an editing context supplied by the editing-context function runner. + * + * @param editingContext + * the editing context to handle events + * @return an event processor for the editing context + */ + protected IEditingContextEventProcessor createEditingContextEventProcessor(final IEditingContext editingContext) { + return this.editingContextEventProcessorFactory.createEditingContextEventProcessor(editingContext); + } + } diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/update/SysONLibraryUpdateTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/update/SysONLibraryUpdateTests.java index 334acae65..90f99fa42 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/update/SysONLibraryUpdateTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/update/SysONLibraryUpdateTests.java @@ -16,7 +16,6 @@ import java.time.Instant; import java.util.List; -import java.util.Map; import java.util.UUID; import org.apache.commons.lang3.stream.Streams; @@ -32,8 +31,6 @@ import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter; import org.eclipse.sirius.web.domain.boundedcontexts.library.Library; import org.eclipse.sirius.web.domain.boundedcontexts.library.services.api.ILibrarySearchService; -import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; -import org.eclipse.sirius.web.domain.boundedcontexts.project.services.api.IProjectSearchService; import org.eclipse.sirius.web.domain.boundedcontexts.projectsemanticdata.ProjectSemanticData; import org.eclipse.sirius.web.domain.boundedcontexts.projectsemanticdata.services.api.IProjectSemanticDataSearchService; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; @@ -41,6 +38,7 @@ import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; import org.eclipse.syson.InvalidateStandardLibrariesCache; +import org.eclipse.syson.application.data.ProjectWithLibraryDependencyTestProjectData; import org.eclipse.syson.application.libraries.SysONLibraryImportTestServer; import org.eclipse.syson.sysml.AttributeUsage; import org.eclipse.syson.sysml.Type; @@ -53,7 +51,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.data.domain.ScrollPosition; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -85,9 +82,6 @@ public class SysONLibraryUpdateTests extends AbstractIntegrationTests { @Autowired private IGivenInitialServerState givenInitialServerState; - @Autowired - private IProjectSearchService projectSearchService; - @Autowired private ISemanticDataSearchService semanticDataSearchService; @@ -238,7 +232,7 @@ public void testProjectContentsHaveBeenUpdated() { protected Library loadMyLibraryV1() { return this.librarySearchService .findByNamespaceAndNameAndVersion( - this.loadProjectByName("MyLibrary").getId(), + ProjectWithLibraryDependencyTestProjectData.LIBRARY_PROJECT_ID, "MyLibrary", "v1") .orElseThrow(); @@ -251,7 +245,7 @@ protected SemanticData loadMyLibraryV1SemanticData() { protected Library loadMyLibraryV2() { return this.librarySearchService .findByNamespaceAndNameAndVersion( - this.loadProjectByName("MyLibrary").getId(), + ProjectWithLibraryDependencyTestProjectData.LIBRARY_PROJECT_ID, "MyLibrary", "v2") .orElseThrow(); @@ -262,8 +256,9 @@ protected SemanticData loadMyLibraryV2SemanticData() { } protected SemanticData loadProjectSemanticData() { - final Project project = this.loadProjectByName("ProjectUsingMyLibraryV1"); - final ProjectSemanticData projectSemanticData = this.projectSemanticDataSearchService.findByProjectId(AggregateReference.to(project.getId())).orElseThrow(); + final ProjectSemanticData projectSemanticData = this.projectSemanticDataSearchService + .findByProjectId(AggregateReference.to(ProjectWithLibraryDependencyTestProjectData.PROJECT_ID)) + .orElseThrow(); return this.semanticDataSearchService.findById(projectSemanticData.getSemanticData().getId()).orElseThrow(); } @@ -276,14 +271,4 @@ protected String getResourceName(final Resource resource) { .getName(); } - private Project loadProjectByName(final String projectName) { - final List candidates = this.projectSearchService.findAll(ScrollPosition.keyset(), 10, Map.of()).stream() - .filter(project -> project.getName().equals(projectName)) - .toList(); - if (candidates.size() != 1) { - return null; - } else { - return candidates.get(0); - } - } } diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc index 821339cd7..d21a54fc3 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc @@ -14,6 +14,8 @@ * In diagrams: +** Fix `Comment` graphical nodes so changing their background color no longer changes their border color. + * In textual import/export: diff --git a/frontend/syson-components/src/nodes/note/SysMLNoteNode.test.tsx b/frontend/syson-components/src/nodes/note/SysMLNoteNode.test.tsx new file mode 100644 index 000000000..2f1b31832 --- /dev/null +++ b/frontend/syson-components/src/nodes/note/SysMLNoteNode.test.tsx @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { Theme } from '@mui/material/styles'; +import { describe, expect, it } from 'vitest'; + +import { getSysMLNoteNodeContainerStyle, getSysMLNotePathProps } from './SysMLNoteNode'; + +describe('getSysMLNotePathProps', () => { + it('keeps the border color when the background changes', () => { + const theme = {} as Theme; + const initialStyle = { background: '#ffffff', borderColor: '#000000', borderWidth: 1 }; + const updatedStyle = { ...initialStyle, background: '#ffff00' }; + + const initialPathProps = getSysMLNotePathProps(theme, initialStyle, false); + const updatedPathProps = getSysMLNotePathProps(theme, updatedStyle, false); + + expect(initialPathProps.fill).toBe('#ffffff'); + expect(updatedPathProps.fill).toBe('#ffff00'); + expect(updatedPathProps.stroke).toBe(initialPathProps.stroke); + expect(updatedPathProps.stroke).toBe('#000000'); + }); +}); + +describe('getSysMLNoteNodeContainerStyle', () => { + it('does not apply the node appearance outside the SVG paths', () => { + const containerStyle = getSysMLNoteNodeContainerStyle({} as Theme, false, false, false); + + expect(containerStyle.background).toBe('transparent'); + expect(containerStyle.border).toBe('none'); + }); +}); diff --git a/frontend/syson-components/src/nodes/note/SysMLNoteNode.tsx b/frontend/syson-components/src/nodes/note/SysMLNoteNode.tsx index 1d27b8d8d..f13a50433 100644 --- a/frontend/syson-components/src/nodes/note/SysMLNoteNode.tsx +++ b/frontend/syson-components/src/nodes/note/SysMLNoteNode.tsx @@ -35,9 +35,8 @@ import React, { memo } from 'react'; import { NodeComponentsMap, SysMLNoteNodeData } from './SysMLNoteNode.types'; -const sysMLNoteNodeStyle = ( +export const getSysMLNoteNodeContainerStyle = ( theme: Theme, - style: React.CSSProperties, selected: boolean, hovered: boolean, faded: boolean @@ -49,10 +48,9 @@ const sysMLNoteNodeStyle = ( height: '100%', position: 'relative', opacity: faded ? '0.4' : '', - ...style, - // No border nor background color: this is handled by the SVG image + // Appearance is rendered exclusively by the SVG paths below. border: 'none', - backgroundColor: 'transparent', + background: 'transparent', }; if (selected || hovered) { @@ -62,8 +60,12 @@ const sysMLNoteNodeStyle = ( return sysMLNoteNodeStyle; }; -const svgPathStyle = (theme: Theme, style: React.CSSProperties, faded: boolean): React.CSSProperties => { - const svgPathStyle: React.CSSProperties = { +export const getSysMLNotePathProps = ( + theme: Theme, + style: React.CSSProperties, + faded: boolean +): React.SVGProps => { + const svgPathProps: React.SVGProps = { stroke: getCSSColor(String(style.borderColor), theme), fill: getCSSColor(String(style.background), theme), fillOpacity: faded ? '0.4' : '1', @@ -71,7 +73,7 @@ const svgPathStyle = (theme: Theme, style: React.CSSProperties, faded: boolean): strokeWidth: style.borderWidth, vectorEffect: 'non-scaling-stroke', }; - return svgPathStyle; + return svgPathProps; }; export const SysMLNoteNode: NodeComponentsMap['sysMLNoteNode'] = memo( @@ -126,7 +128,7 @@ export const SysMLNoteNode: NodeComponentsMap['sysMLNoteNode'] = memo(