From c32cb82769c70109c45113b4be639fdcec5e5e49 Mon Sep 17 00:00:00 2001 From: Pierre-Charles David Date: Fri, 12 Dec 2025 09:44:14 +0100 Subject: [PATCH] [1747] Automatically select more newly created elements Bug: https://github.com/eclipse-syson/syson/issues/1747 Signed-off-by: Pierre-Charles David --- CHANGELOG.adoc | 1 + .../syson/JavaServiceIsCalledChecker.java | 166 ++---------------- .../AbstractCompartmentNodeToolProvider.java | 24 ++- ...ActionFlowCompartmentNodeToolProvider.java | 26 ++- .../pages/release-notes/2026.1.0.adoc | 2 + .../playwright/e2e/diagram-selection.spec.ts | 114 ++++++++++++ .../e2e/diagram_node_creation.spec.ts | 3 +- .../resources/SysMLv2WithGeneralView.sysml | 6 + 8 files changed, 173 insertions(+), 169 deletions(-) create mode 100644 integration-tests-playwright/playwright/e2e/diagram-selection.spec.ts create mode 100644 integration-tests-playwright/playwright/resources/SysMLv2WithGeneralView.sysml diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c6fb206e3..ec18ec370 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -71,6 +71,7 @@ A new compartment named _satisfy requirements_ has also been added to `PartDefin - https://github.com/eclipse-syson/syson/issues/1786[#1786] [export] Implement textual export of `RequirementConstraintMembership`. - https://github.com/eclipse-syson/syson/issues/1771[#1740] [services] Add `IDetailViewHelpTextProvider` service in order to add help text to properties widgets - https://github.com/eclipse-syson/syson/issues/1738[#1738] [diagrams] Add `ConnectionUsage` as edges in _General View_ diagram. +- https://github.com/eclipse-syson/syson/issues/1747[#1747] [diagrams] Most new elements created by invoking a tool on a diagram are now automatically selected == v2025.12.0 diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/JavaServiceIsCalledChecker.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/JavaServiceIsCalledChecker.java index 8e1cbb471..22393e71f 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/JavaServiceIsCalledChecker.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/JavaServiceIsCalledChecker.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 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 @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -28,24 +27,11 @@ import org.eclipse.acceleo.query.runtime.IService; import org.eclipse.acceleo.query.runtime.Query; import org.eclipse.acceleo.query.runtime.QueryParsing; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; import org.eclipse.sirius.components.interpreter.SimpleCrossReferenceProvider; -import org.eclipse.sirius.components.view.ChangeContext; -import org.eclipse.sirius.components.view.Operation; -import org.eclipse.sirius.components.view.RepresentationDescription; import org.eclipse.sirius.components.view.View; -import org.eclipse.sirius.components.view.diagram.ConditionalEdgeStyle; -import org.eclipse.sirius.components.view.diagram.ConditionalNodeStyle; -import org.eclipse.sirius.components.view.diagram.CreateView; -import org.eclipse.sirius.components.view.diagram.DiagramDescription; -import org.eclipse.sirius.components.view.diagram.EdgeDescription; -import org.eclipse.sirius.components.view.diagram.InsideLabelDescription; -import org.eclipse.sirius.components.view.diagram.InsideLabelStyle; -import org.eclipse.sirius.components.view.diagram.LabelEditTool; -import org.eclipse.sirius.components.view.diagram.NodeDescription; -import org.eclipse.sirius.components.view.diagram.NodeTool; -import org.eclipse.sirius.components.view.diagram.OutsideLabelDescription; -import org.eclipse.sirius.components.view.diagram.SelectionDialogDescription; -import org.eclipse.sirius.components.view.diagram.Tool; +import org.eclipse.sirius.components.view.ViewPackage; import org.eclipse.syson.sysml.helper.EMFUtils; import org.eclipse.syson.util.AQLConstants; @@ -66,7 +52,7 @@ public JavaServiceIsCalledChecker(View view) { this.aqlServiceNames = new HashSet<>(); - Collection aqlExpressions = this.collectAQLExpressionsInView(view); + Collection aqlExpressions = this.collectAQLExpressions(view); List aqlBodyExpressions = aqlExpressions.stream() .filter(expression -> expression != null && !expression.isBlank()) @@ -93,140 +79,20 @@ public void check(Method service) { .contains(service.getName()); } - private Collection collectAQLExpressionsInView(View view) { + private Collection collectAQLExpressions(EObject eObject) { Set expressions = new HashSet<>(); - for (RepresentationDescription description : view.getDescriptions()) { - if (description instanceof DiagramDescription diagramDescription) { - expressions.addAll(this.collectAQLExpressionsInDiagramDescription(diagramDescription)); - } - } - return expressions; - } - - private Collection collectAQLExpressionsInDiagramDescription(DiagramDescription diagramDescription) { - Set expressions = new HashSet<>(); - - expressions.add(diagramDescription.getPreconditionExpression()); - - final List allDiagramTools = EMFUtils.allContainedObjectOfType(diagramDescription, Tool.class).toList(); - - for (Tool diagramTool : allDiagramTools) { - expressions.add(diagramTool.getPreconditionExpression()); - for (Operation bodyOperation : diagramTool.getBody()) { - EMFUtils.allContainedObjectOfType(bodyOperation, ChangeContext.class) - .map(ChangeContext::getExpression) - .forEach(expressions::add); - } - if (diagramTool instanceof NodeTool nodeTool) { - if (nodeTool.getDialogDescription() instanceof SelectionDialogDescription selectionDialogDescription) { - expressions.add(selectionDialogDescription.getSelectionDialogTreeDescription().getElementsExpression()); - expressions.add(selectionDialogDescription.getSelectionDialogTreeDescription().getChildrenExpression()); - expressions.add(selectionDialogDescription.getSelectionDialogTreeDescription().getIsSelectableExpression()); - } - } - } - - EMFUtils.allContainedObjectOfType(diagramDescription, NodeDescription.class) - .map(this::collectAQLExpressionsInNodeDescription) - .forEach(expressions::addAll); - EMFUtils.allContainedObjectOfType(diagramDescription, EdgeDescription.class) - .map(this::collectAQLExpressionsInEdgeDescription) - .forEach(expressions::addAll); - - EMFUtils.allContainedObjectOfType(diagramDescription, ConditionalNodeStyle.class) - .map(ConditionalNodeStyle::getCondition) - .forEach(expressions::add); - EMFUtils.allContainedObjectOfType(diagramDescription, ConditionalEdgeStyle.class) - .map(ConditionalEdgeStyle::getCondition) - .forEach(expressions::add); - - return expressions; - } - - private Collection collectAQLExpressionsInNodeDescription(NodeDescription nodeDescription) { - Set expressions = new HashSet<>(); - Optional.ofNullable(nodeDescription.getSemanticCandidatesExpression()) - .map(expressions::add); - - Optional.ofNullable(nodeDescription.getInsideLabel()).map(InsideLabelDescription::getLabelExpression) - .map(expressions::add); - - Optional.ofNullable(nodeDescription.getInsideLabel()).map(InsideLabelDescription::getStyle) - .map(InsideLabelStyle::getShowIconExpression) - .map(expressions::add); - - Optional.ofNullable(nodeDescription.getOutsideLabels()) - .ifPresent(outsideLabelDescriptions -> outsideLabelDescriptions.stream().map(OutsideLabelDescription::getLabelExpression).forEach(expressions::add)); - - Optional.ofNullable(nodeDescription.getIsHiddenByDefaultExpression()) - .map(expressions::add); - - Optional.ofNullable(nodeDescription.getPreconditionExpression()) - .map(expressions::add); - - - List nodeTools = Optional.ofNullable(nodeDescription.getPalette()).stream() - .flatMap(palette -> EMFUtils.allContainedObjectOfType(palette, Tool.class)) - .toList(); - - for (Tool nodeTool : nodeTools) { - expressions.add(nodeTool.getPreconditionExpression()); - if (nodeTool instanceof LabelEditTool labelEditTool) { - expressions.add(labelEditTool.getInitialDirectEditLabelExpression()); - } - if (nodeTool instanceof NodeTool createNodeTool) { - if (createNodeTool.getDialogDescription() instanceof SelectionDialogDescription selectionDialogDescription) { - expressions.add(selectionDialogDescription.getSelectionDialogTreeDescription().getElementsExpression()); - } - } - for (Operation bodyOperation : nodeTool.getBody()) { - EMFUtils.allContainedObjectOfType(bodyOperation, ChangeContext.class) - .forEach(changeContext -> expressions.add(changeContext.getExpression())); - EMFUtils.allContainedObjectOfType(bodyOperation, CreateView.class) - .forEach(changeContext -> expressions.add(changeContext.getParentViewExpression())); - } - } + this.collectAQLExpressions(eObject, expressions); return expressions; } - private Collection collectAQLExpressionsInEdgeDescription(EdgeDescription edgeDescription) { - Set expressions = new HashSet<>(); - Optional.ofNullable(edgeDescription.getSemanticCandidatesExpression()) - .map(expressions::add); - - Optional.ofNullable(edgeDescription.getBeginLabelExpression()) - .map(expressions::add); - - Optional.ofNullable(edgeDescription.getCenterLabelExpression()) - .map(expressions::add); - - Optional.ofNullable(edgeDescription.getEndLabelExpression()) - .map(expressions::add); - - Optional.ofNullable(edgeDescription.getSourceExpression()) - .map(expressions::add); - - Optional.ofNullable(edgeDescription.getTargetExpression()) - .map(expressions::add); - - Optional.ofNullable(edgeDescription.getPreconditionExpression()) - .map(expressions::add); - - final List allEdgeTools = EMFUtils.allContainedObjectOfType(edgeDescription, Tool.class).toList(); - - for (Tool edgeTool : allEdgeTools) { - expressions.add(edgeTool.getPreconditionExpression()); - if (edgeTool instanceof LabelEditTool labelEditTool) { - expressions.add(labelEditTool.getInitialDirectEditLabelExpression()); - } - for (Operation bodyOperation : edgeTool.getBody()) { - EMFUtils.allContainedObjectOfType(bodyOperation, ChangeContext.class) - .forEach(changeContext -> expressions.add(changeContext.getExpression())); - EMFUtils.allContainedObjectOfType(bodyOperation, CreateView.class) - .forEach(changeContext -> expressions.add(changeContext.getParentViewExpression())); - } - } - return expressions; + private void collectAQLExpressions(EObject eObject, Set expressions) { + // All interpreted expressions on the eOject itself + eObject.eClass().getEAllStructuralFeatures().stream() + .filter(EAttribute.class::isInstance) + .map(EAttribute.class::cast) + .filter(attr -> attr.getEType() == ViewPackage.Literals.INTERPRETED_EXPRESSION) + .forEach(expressionAttribute -> expressions.add((String) eObject.eGet(expressionAttribute))); + // Recurse on all its descendants + eObject.eAllContents().forEachRemaining(o -> this.collectAQLExpressions(o, expressions)); } - } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractCompartmentNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractCompartmentNodeToolProvider.java index 852297ce9..54e4278ac 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractCompartmentNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractCompartmentNodeToolProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 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 @@ -12,8 +12,6 @@ *******************************************************************************/ package org.eclipse.syson.diagram.common.view.tools; -import java.util.List; - import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.diagrams.Node; @@ -25,9 +23,9 @@ import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.diagram.SelectionDialogDescription; import org.eclipse.sirius.components.view.emf.diagram.ViewDiagramDescriptionConverter; +import org.eclipse.syson.diagram.common.view.services.ViewNodeService; import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; import org.eclipse.syson.util.AQLConstants; -import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.ServiceMethod; /** @@ -37,6 +35,8 @@ */ public abstract class AbstractCompartmentNodeToolProvider implements INodeToolProvider { + private static final String NEW_INSTANCE = "newInstance"; + protected final DiagramBuilders diagramBuilderHelper = new DiagramBuilders(); protected final ViewBuilders viewBuilderHelper = new ViewBuilders(); @@ -77,8 +77,8 @@ public NodeTool create(IViewDiagramElementFinder cache) { ChangeContextBuilder revealOperation; if (this.revealOnCreate()) { revealOperation = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getServiceCallExpression(Node.SELECTED_NODE, "revealCompartment", - List.of("self", DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE))); + .expression(ServiceMethod.of4(ViewNodeService::revealCompartment).aql(Node.SELECTED_NODE, AQLConstants.SELF, DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, + ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); } else { revealOperation = this.viewBuilderHelper.newChangeContext().expression(AQLConstants.AQL_SELF); } @@ -87,19 +87,25 @@ public NodeTool create(IViewDiagramElementFinder cache) { .expression(ServiceMethod.of4(DiagramMutationAQLService::expose).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT, Node.SELECTED_NODE, ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); - var creationCompartmentItemServiceCall = this.viewBuilderHelper.newChangeContext() - .expression(this.getServiceCallExpression()) + var exposeAndRevealNewInstance = this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + NEW_INSTANCE) .children(addToExposedElements.build(), revealOperation.build()); + var letNewInstance = this.viewBuilderHelper.newLet() + .variableName(NEW_INSTANCE) + .valueExpression(this.getServiceCallExpression()) + .children(exposeAndRevealNewInstance.build()); + var rootChangContext = this.viewBuilderHelper.newChangeContext() .expression(AQLConstants.AQL_SELF) - .children(creationCompartmentItemServiceCall.build()) + .children(letNewInstance.build()) .build(); return builder.name(this.getNodeToolName()) .iconURLsExpression(this.getNodeToolIconURLsExpression()) .body(rootChangContext) .preconditionExpression(this.getPreconditionExpression()) + .elementsToSelectExpression(AQLConstants.AQL + NEW_INSTANCE) .build(); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java index 33c9ddcfc..e7aceb7ce 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 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 @@ -12,8 +12,6 @@ *******************************************************************************/ package org.eclipse.syson.diagram.common.view.tools; -import java.util.List; - import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.diagrams.Node; @@ -23,8 +21,10 @@ import org.eclipse.sirius.components.view.builder.providers.INodeToolProvider; import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.emf.diagram.ViewDiagramDescriptionConverter; +import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewNodeService; import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; -import org.eclipse.syson.util.AQLUtils; +import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.ServiceMethod; /** @@ -34,6 +34,8 @@ */ public class ActionFlowCompartmentNodeToolProvider implements INodeToolProvider { + private static final String NEW_INSTANCE = "newInstance"; + private final DiagramBuilders diagramBuilderHelper = new DiagramBuilders(); private final ViewBuilders viewBuilderHelper = new ViewBuilders(); @@ -47,18 +49,24 @@ public NodeTool create(IViewDiagramElementFinder cache) { ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); var revealOperation = this.viewBuilderHelper.newChangeContext() - .expression( - AQLUtils.getServiceCallExpression(Node.SELECTED_NODE, "revealCompartment", - List.of("self", DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE))); + .expression(ServiceMethod.of4(ViewNodeService::revealCompartment).aql(Node.SELECTED_NODE, AQLConstants.SELF, DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, + ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); var creationServiceCall = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("createSubActionUsage")) + .expression(AQLConstants.AQL + NEW_INSTANCE) .children(addToExposedElements.build(), revealOperation.build()) .build(); + var letNewInstance = this.viewBuilderHelper.newLet() + .variableName(NEW_INSTANCE) + .valueExpression(ServiceMethod.of0(ViewCreateService::createSubActionUsage).aqlSelf()) + .children(creationServiceCall) + .build(); + return builder.name("New Action") .iconURLsExpression("/icons/full/obj16/ActionUsage.svg") - .body(creationServiceCall) + .body(letNewInstance) + .elementsToSelectExpression(AQLConstants.AQL + NEW_INSTANCE) .build(); } } diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc index f72340d0c..09086cfcd 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc @@ -122,6 +122,8 @@ a|image::release-notes-enumeration-definition-after.png[Enumeration definition n - In diagrams, when creating an element from a compartment, it will not be displayed by default on the diagram, but only in the compartment. - In the _Advanced_ tab of the _Details_ view, _isComposite_ and _isReference_ properties (checkbox widgets) now have a help text. - In diagrams, a new tool named `Reference Subsetting` is available in the palette to create `ReferenceSubsetting` relationships between `Usage` elements. +- In diagrams, many new tools now automatically select the `Elements` they create or expose on a diagram. +This makes it easy to start editing newly created elements to give them proper names by starting to type directly when the new element appears. == Technical details diff --git a/integration-tests-playwright/playwright/e2e/diagram-selection.spec.ts b/integration-tests-playwright/playwright/e2e/diagram-selection.spec.ts new file mode 100644 index 000000000..5d5bda4b3 --- /dev/null +++ b/integration-tests-playwright/playwright/e2e/diagram-selection.spec.ts @@ -0,0 +1,114 @@ +/******************************************************************************* + * 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { expect, test } from '@playwright/test'; +import { PlaywrightExplorer } from '../helpers/PlaywrightExplorer'; +import { PlaywrightNode } from '../helpers/PlaywrightNode'; +import { PlaywrightProject } from '../helpers/PlaywrightProject'; + +test.describe('diagram - general view', () => { + let projectId; + test.beforeEach(async ({ page, request }) => { + await page.addInitScript(() => { + // @ts-expect-error: we use a variable in the DOM to disable `fitView` functionality for Cypress tests. + window.document.DEACTIVATE_FIT_VIEW_FOR_CYPRESS_TESTS = true; + }); + const project = await new PlaywrightProject(request).createSysMLV2Project('general'); + projectId = project.projectId; + + await page.goto(`/projects/${projectId}/edit`); + const playwrightExplorer = new PlaywrightExplorer(page); + await playwrightExplorer.uploadDocument('SysMLv2WithGeneralView.sysml'); + await playwrightExplorer.expand('SysMLv2WithGeneralView.sysml'); + await playwrightExplorer.expand('Package1'); + await playwrightExplorer.createRepresentation('view1 [GeneralView]', 'General View', 'view1'); + + // Make sure all the elements are visible and at well-defined locations + await page.getByTestId('arrange-all-menu').click(); + await page.getByTestId('arrange-all-elk-layered').click(); + await page.getByTestId('zoom-out').click(); + }); + + test.afterEach(async ({ request }) => { + await new PlaywrightProject(request).deleteProject(projectId); + }); + + test('WHEN creating an attribute inside a part, THEN the new attribute is automatically selected and can be renamed immediately', async ({ + page, + }) => { + const partNode = new PlaywrightNode(page, 'part1', 'List'); + await expect(partNode.nodeLocator).toBeAttached(); + + // Create a new attribute + await partNode.click(); + await partNode.openPalette(); + await page.getByTestId('toolSection-Structure').click(); + await page.getByTestId('tool-New Attribute').click(); + + // It should be selected + const newNode = new PlaywrightNode(page, 'attribute1', 'IconLabel'); + await expect(newNode.nodeLocator).toBeAttached(); + await expect(newNode.nodeLocator).toContainClass('selected'); + + // Start typing a new name immediately + const newName = 'editedAttribute'; + await page.keyboard.type(newName); + await page.keyboard.press('Enter'); + await expect(newNode.nodeLocator).not.toBeAttached(); + + // The newly created node has changed its label and is still selected + const editedNode = new PlaywrightNode(page, newName, 'IconLabel'); + await expect(editedNode.nodeLocator).toBeAttached(); + await expect(editedNode.nodeLocator).toContainClass('selected'); + + // The selection can be seen from the rest of the workbench, e.g. in the Explorer + const explorer = new PlaywrightExplorer(page); + const treeItem = await explorer.getTreeItemLabel(newName); + await expect(treeItem).not.toBeVisible(); + await explorer.revealGlobalSelection(); + await expect(treeItem).toBeVisible(); + }); + + test('WHEN creating a port on a part, THEN the new port is automatically selected', async ({ page }) => { + const partNode = new PlaywrightNode(page, 'part1', 'List'); + await expect(partNode.nodeLocator).toBeAttached(); + + // Create a new port + await partNode.click(); + await partNode.openPalette(); + await page.getByTestId('toolSection-Structure').click(); + await page.getByTestId('tool-New Port').click(); + + // It should be selected + const newNode = new PlaywrightNode(page, 'port1', 'FreeForm'); + await expect(newNode.nodeLocator).toBeAttached(); + await expect(newNode.nodeLocator).toContainClass('selected'); + + // Start typing a new name immediately + const newName = 'editedPort'; + await page.keyboard.type(newName); + await page.keyboard.press('Enter'); + await expect(newNode.nodeLocator).not.toBeAttached(); + + // The newly created node has changed its label and is still selected + const editedNode = new PlaywrightNode(page, newName, 'FreeForm'); + await expect(editedNode.nodeLocator).toBeAttached(); + await expect(editedNode.nodeLocator).toContainClass('selected'); + + // The selection can be seen from the rest of the workbench, e.g. in the Explorer + const explorer = new PlaywrightExplorer(page); + const treeItem = await explorer.getTreeItemLabel(newName); + await expect(treeItem).not.toBeVisible(); + await explorer.revealGlobalSelection(); + await expect(treeItem).toBeVisible(); + }); +}); diff --git a/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts b/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts index de5ee75b5..1778fe436 100644 --- a/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts +++ b/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts @@ -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 @@ -56,6 +56,7 @@ test.describe('diagram - general view', () => { await expect(portNode.nodeLocator).toBeAttached(); diagram.expectNumberOfTopNodes(3); // The port can be displayed as a list item + await partDefinitionNode.click(); await partDefinitionNode.revealElement('ports'); const portsListNode = new PlaywrightNode(page, 'ports', 'List'); await expect(portsListNode.nodeLocator).toBeAttached(); diff --git a/integration-tests-playwright/playwright/resources/SysMLv2WithGeneralView.sysml b/integration-tests-playwright/playwright/resources/SysMLv2WithGeneralView.sysml new file mode 100644 index 000000000..23b1151b8 --- /dev/null +++ b/integration-tests-playwright/playwright/resources/SysMLv2WithGeneralView.sysml @@ -0,0 +1,6 @@ +package Package1 { + view view1 : StandardViewDefinitions::GeneralView { + expose part1; + } + part part1; +} \ No newline at end of file