Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ This fix ensure that imported models containing, for example, a top-level `Libra
- https://github.com/eclipse-syson/syson/issues/1765[#1765] [diagrams] Allow to create a `SatisfyRequirement` on `PartDefinition` or `PartUsage`.
A new compartment named _satisfy requirements_ has also been added to `PartDefinition` and `PartUsage` graphical nodes in diagrams.
- https://github.com/eclipse-syson/syson/issues/1762[#1762] [diagrams] Increase the default size of nodes.
- https://github.com/eclipse-syson/syson/issues/1752[#1752] [diagrams] In diagrams, hide the node displayed on the top of the diagram if the candidate compartment is not hidden.

=== New features

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,21 @@ private void deleteExpose(List<Expose> exposed, Object exposedElement) {

private void hideNodeIfVisibleCompartmentCouldHostTheFutureNode(Element element, IEditingContext editingContext, DiagramContext diagramContext, Node selectedNode,
Map<org.eclipse.sirius.components.view.diagram.NodeDescription, NodeDescription> convertedNodes) {
// If the creation tool was executed from a compartment, we're looking for sibling from the parent node of the compartment
// We're comparing the targetObjectId of the selected node and its parent in order to know if we're on the same semantic element
var referenceParent = selectedNode;
var selectedNodeTargetObjectId = selectedNode.getTargetObjectId();
var nullableParentNode = new NodeFinder(diagramContext.diagram()).getParent(selectedNode);
if (nullableParentNode instanceof Node parentNode) {
var parentTargetObjectId = parentNode.getTargetObjectId();
if (parentTargetObjectId.equals(selectedNodeTargetObjectId)) {
referenceParent = parentNode;
}
}
// 1- get visible compartments of selected node
// 2- get visible compartments that could host the future node
// 3- if a visible compartment could host the future node, then hide the tree node
List<Node> visibleCompartments = selectedNode.getChildNodes().stream().filter(n -> n.getState().equals(ViewModifier.Normal)).toList();
List<Node> visibleCompartments = referenceParent.getChildNodes().stream().filter(n -> n.getState().equals(ViewModifier.Normal)).toList();
boolean visibleCompartmentsThatCouldHostTheFutureNode = false;
for (Node visibleCompartment : visibleCompartments) {
Optional<String> childNodeDescriptionIdForRendering = this.diagramMutationElementService.getChildNodeDescriptionIdForRendering(element, editingContext, diagramContext, visibleCompartment,
Expand All @@ -361,7 +372,7 @@ private void hideNodeIfVisibleCompartmentCouldHostTheFutureNode(Element element,
}
}
if (visibleCompartmentsThatCouldHostTheFutureNode) {
var parentId = this.diagramQueryElementService.getGraphicalParentId(diagramContext, selectedNode);
var parentId = this.diagramQueryElementService.getGraphicalParentId(diagramContext, referenceParent);
var descriptionId = this.diagramQueryElementService.getNodeDescriptionId(element, diagramContext.diagram(), editingContext);
if (parentId != null && descriptionId.isPresent()) {
var nodeId = new NodeIdProvider().getNodeId(parentId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.sirius.components.core.api.IIdentityService;
import org.eclipse.sirius.components.core.api.IObjectSearchService;
import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService;
import org.eclipse.sirius.components.core.api.IURLParser;
import org.springframework.stereotype.Service;

/**
Expand All @@ -28,13 +29,14 @@
*/
@Service
public record SiriusWebCoreServices(IObjectSearchService objectSearchService, IIdentityService identityService, IFeedbackMessageService feedbackMessageService,
IEditingContextSearchService editingContextSearchService, IRepresentationDescriptionSearchService representationDescriptionSearchService) {
IEditingContextSearchService editingContextSearchService, IRepresentationDescriptionSearchService representationDescriptionSearchService, IURLParser urlParser) {

public SiriusWebCoreServices {
Objects.requireNonNull(objectSearchService);
Objects.requireNonNull(identityService);
Objects.requireNonNull(feedbackMessageService);
Objects.requireNonNull(editingContextSearchService);
Objects.requireNonNull(representationDescriptionSearchService);
Objects.requireNonNull(urlParser);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ a|image::release-notes-enumeration-definition-after.png[Enumeration definition n

- In diagrams, an _items_ compartment is now available on `PortDefinition` graphical nodes to display `ItemUsage`.
- In diagrams, an `ItemUsage` graphical border node is now available on `PortDefinition` graphical nodes.

- In diagrams, when creating an element from a compartment, it will not be displayed by default on the diagram, but only in the compartment.

== Technical details

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*******************************************************************************
* Copyright (c) 2025 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 { PlaywrightDiagram } from '../helpers/PlaywrightDiagram';
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 view');
projectId = project.projectId;
await page.goto(`/projects/${projectId}/edit`);
await page.locator(`[data-testid="onboard-open-view1"]`).first().click();
});

test.afterEach(async ({ request }) => {
await new PlaywrightProject(request).deleteProject(projectId);
});

test('when creating a port on a part definition, then the port top node is hidden', async ({ page }) => {
const diagram = new PlaywrightDiagram(page);
// Create PartDef
await page.getByTestId('rf__wrapper').click({ button: 'right' });
await expect(page.getByTestId('Palette')).toBeAttached();
await page.getByTestId('toolSection-Structure').click();
await page.getByTestId('tool-New Part Definition').click();
diagram.expectNumberOfTopNodes(1);
// Create Port
const partDefinitionNode = new PlaywrightNode(page, 'PartDefinition1', 'List');
await expect(partDefinitionNode.nodeLocator).toBeAttached();
await partDefinitionNode.openPalette();
await page.getByTestId('toolSection-Structure').click();
await page.getByTestId('tool-New Port Inout').click();
// The port is only displayed as a border node of the parent
const portBorderNode = new PlaywrightNode(page, 'port1Inout');
await expect(portBorderNode.nodeLocator).toBeAttached();
diagram.expectNumberOfTopNodes(2);
// The port can be displayed as a top node
await diagram.revealElement('port1Inout');
const portNode = new PlaywrightNode(page, 'port1Inout', 'List');
await expect(portNode.nodeLocator).toBeAttached();
diagram.expectNumberOfTopNodes(3);
// The port can be displayed as a list item
await partDefinitionNode.revealElement('ports');
const portsListNode = new PlaywrightNode(page, 'ports', 'List');
await expect(portsListNode.nodeLocator).toBeAttached();
const portListItemNode = new PlaywrightNode(page, 'inout port1Inout', 'IconLabel');
await expect(portListItemNode.nodeLocator).toBeAttached();
await diagram.expectNumberOfTopNodes(4);
});

test('when creating a port on a port compartment of a part definition, then the port top node is hidden', async ({
page,
}) => {
const diagram = new PlaywrightDiagram(page);
// Create PartDef
await page.getByTestId('rf__wrapper').click({ button: 'right' });
await expect(page.getByTestId('Palette')).toBeAttached();
await page.getByTestId('toolSection-Structure').click();
await page.getByTestId('tool-New Part Definition').click();
diagram.expectNumberOfTopNodes(1);
const partDefinitionNode = new PlaywrightNode(page, 'PartDefinition1', 'List');
await expect(partDefinitionNode.nodeLocator).toBeAttached();
// Show the port compartment
await partDefinitionNode.revealElement('ports');
const portsListNode = new PlaywrightNode(page, 'ports', 'List');
await expect(portsListNode.nodeLocator).toBeAttached();
await portsListNode.openPalette();
// Create Port
await diagram.expectNumberOfTopNodes(2);
await page.getByTestId('toolSection-Structure').click();
await page.getByTestId('tool-New Port Inout').click();
// The port is displayed as an item node
const portListItemNode = new PlaywrightNode(page, 'inout port1Inout', 'IconLabel');
await expect(portListItemNode.nodeLocator).toBeAttached();
// The port is displayed as a border node of the parent
const portBorderNode = new PlaywrightNode(page, 'port1Inout');
await expect(portBorderNode.nodeLocator).toBeAttached();
diagram.expectNumberOfTopNodes(4);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2025 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, type Page } from '@playwright/test';

export class PlaywrightDiagram {
readonly page: Page;

constructor(page: Page) {
this.page = page;
}

async expectNumberOfTopNodes(expectedNumberOfTopNodes: number) {
expect(await this.page.locator('.react-flow__nodes > div').count()).toBe(expectedNumberOfTopNodes);
}

async revealElement(name: string) {
await this.page.getByTestId('filter-elements').click();
const popup = this.page.getByTestId('group-Filter elements');
await expect(popup).toBeAttached();
await popup.locator(`[data-testid$="${name}"]`).click();
//The drop down lack a proper data-testid so we use its sibling
await expect(popup.locator(`[data-testid="Apply to 1 selected element:"]`)).toBeAttached();
await popup
.locator(`[data-testid="Apply to 1 selected element:"]`)
.locator('..')
.locator('> button')
.last()
.click();
await this.page.getByAltText('Show').click();
await popup.locator(`[data-testid="Apply to 1 selected element:"]`).click();
await this.page.keyboard.press('Escape');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,13 @@ export class PlaywrightNode {
// see https://playwright.dev/docs/actionability
await this.nodeLocator.click();
}

async revealElement(name: string) {
await this.nodeLocator.hover({ position: { x: 10, y: 10 } });
await this.page
.getByTestId('manage-visibility')
.locator('> svg')
.click({ position: { x: 1, y: 1 } });
await this.page.getByTestId(`manage_visibility_list_item_button_${name}`).click();
}
}
Loading