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
4 changes: 2 additions & 2 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ An example of usage is available in `EllipseNodeLayoutHandler.ts`.
- [releng] Switch to AQL 8.1.0 from https://download.eclipse.org/acceleo/updates/releases/4.2/R202510230846/[Acceleo 4.2.0].
This also includes an update from ANTLR 4.10.1 to 4.13.2.
- https://github.com/eclipse-sirius/sirius-web/issues/5653[#5653] Add a dependency to `spring-boot-starter-data-elasticsearch` in `sirius-web-infrastructure`

- [releng] Switch to react-window 2.2.2.

=== Bug fixes

Expand Down Expand Up @@ -210,7 +210,7 @@ The executor typically needs to check the `menuEntryId` of the input to ensure i
- https://github.com/eclipse-sirius/sirius-web/issues/5558[#5558] [diagram] Prevent the node from overlapping its edge bend points
- https://github.com/eclipse-sirius/sirius-web/issues/5635[#5635] [diagram] Improve the position of border nodes when they are used as edge handles
- https://github.com/eclipse-sirius/sirius-web/issues/5821[#5821] [sirius-web] Provide a way to customize the tree displayed in the object duplication modal.

- https://github.com/eclipse-sirius/sirius-web/issues/5726[#5726] [sirius-web] Virtualize the display of search results to improve performance on large result sets

== 2025.10.0

Expand Down
37 changes: 12 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,12 @@ export const Panels = forwardRef<WorkbenchPanelsHandle | null, PanelsProps>(
<PanelGroup direction="vertical">
{leftSelectedContributions.map((leftContribution, index) => (
<>
<Panel key={leftContribution.id} className={classes.panel} minSize={10}>
<Panel
id={leftContribution.id}
key={leftContribution.id}
order={index}
className={classes.panel}
minSize={10}>
<WorkbenchPart
editingContextId={editingContextId}
readOnly={readOnly}
Expand Down Expand Up @@ -325,7 +330,12 @@ export const Panels = forwardRef<WorkbenchPanelsHandle | null, PanelsProps>(
<PanelGroup direction="vertical">
{rightSelectedContributions.map((rightContribution, index) => (
<>
<Panel key={rightContribution.id} className={classes.panel} minSize={10}>
<Panel
id={rightContribution.id}
key={rightContribution.id}
order={index}
className={classes.panel}
minSize={10}>
<WorkbenchPart
editingContextId={editingContextId}
readOnly={readOnly}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.sirius.web.tests.graphql;

import java.util.Map;
import java.util.Objects;

import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor;
import org.eclipse.sirius.components.graphql.tests.api.IQueryRunner;
import org.springframework.stereotype.Service;

/**
* Used to search for elements inside an editing context.
*
* @author pcdavid
*/
@Service
public class SearchQueryRunner implements IQueryRunner {

private static final String SEARCH_QUERY = """
query search($editingContextId: ID!, $query: SearchQuery!) {
viewer {
editingContext(editingContextId: $editingContextId) {
search(query: $query) {
__typename
... on SearchSuccessPayload {
result {
__typename
matches {
id
label
iconURLs
}
}
}
}
}
}
}
""";

private final IGraphQLRequestor graphQLRequestor;

public SearchQueryRunner(IGraphQLRequestor graphQLRequestor) {
this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor);
}

@Override
public String run(Map<String, Object> variables) {
return this.graphQLRequestor.execute(SEARCH_QUERY, variables);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.sirius.web.application.controllers.search;

import static org.assertj.core.api.Assertions.assertThat;

import com.jayway.jsonpath.JsonPath;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.eclipse.sirius.web.AbstractIntegrationTests;
import org.eclipse.sirius.web.application.views.search.dto.SearchResult;
import org.eclipse.sirius.web.application.views.search.dto.SearchSuccessPayload;
import org.eclipse.sirius.web.data.PapayaIdentifiers;
import org.eclipse.sirius.web.tests.data.GivenSiriusWebServer;
import org.eclipse.sirius.web.tests.graphql.SearchQueryRunner;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

/**
* Integration tests for the Search view.
*
* @author pcdavid
*/
@Transactional
@SuppressWarnings("checkstyle:MultipleStringLiterals")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SearchIntegrationTests extends AbstractIntegrationTests {

@Autowired
private IGivenInitialServerState givenInitialServerState;

@Autowired
private SearchQueryRunner searchQueryRunner;

@BeforeEach
public void beforeEach() {
this.givenInitialServerState.initialize();
}

@Test
@GivenSiriusWebServer
@DisplayName("Given a project, when we execute a basic search, then all the matching semantic elements are returned")
public void givenProjectWhenWeExecuteBasicSearchThenAllMatchingElementsAreReturned() {
List<String> matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "sirius-web", false, false, false, false);
assertThat(matches).containsExactlyInAnyOrder(
"sirius-web-tests-data",
"sirius-web-domain",
"sirius-web-application",
"sirius-web-infrastructure",
"sirius-web-starter",
"sirius-web"
);
}

@Test
@GivenSiriusWebServer
@DisplayName("Given a project, when we execute a whole word search, then only the matching semantic elements are returned")
public void givenProjectWhenWeExecuteWholeWordSearchThenOnlyMatchingElementsAreReturned() {
// "Command" appear multiple times as part of "Command1" and "Command2"
List<String> matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "Command", false, /* mathcWholeWord */ false, false, false);
assertThat(matches).containsExactly("Command1 over HTTP", "Command2 over null", "Command1", "Command2");
// but never as a distinct whole word.
matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "Command", false, /* mathcWholeWord */ true, false, false);
assertThat(matches).isEmpty();
}

@Test
@GivenSiriusWebServer
@DisplayName("Given a project, when we execute a case insensitive search, then all the matching semantic elements are returned")
public void givenProjectWhenWeExecuteCaseInsensitiveSearchThenAllMatchingElementsAreReturned() {
List<String> matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "project creation", /* matchCase */ false, false, false, false);
assertThat(matches).containsExactlyInAnyOrder("Project Creation");

matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "project creation", /* matchCase */ true, false, false, false);
assertThat(matches).isEmpty();
}

@Test
@GivenSiriusWebServer
@DisplayName("Given a project, when we execute a search including attributes, then all the matching semantic elements are returned")
public void givenProjectWhenWeExecuteSearchIncludingAttributesThenAllMatchingElementsAreReturned() {
// There are no matches in object names/labels
List<String> matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "2024-02", false, false, false, /* searchInAttributes */ false);
assertThat(matches).isEmpty();
// but there are in several object attributes
matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "2024-02", false, false, false, /* searchInAttributes */ true);
// Note: the matches are in these object's attributes, but we return only the objects themselves
assertThat(matches).containsExactlyInAnyOrder("2024.3.0", "2024.5.0");
}

@Test
@GivenSiriusWebServer
@DisplayName("Given a project, when we execute a search with regular expression, then all the matching semantic elements are returned")
public void givenProjectWhenWeExecuteSearchWithRegexpThenAllMatchingElementsAreReturned() {
List<String> matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "^sirius-web-([dt]|ap{2}).*[an]$", false, false, /* useRegularExpressions */ false, false);
assertThat(matches).isEmpty();
matches = this.search(PapayaIdentifiers.PAPAYA_EDITING_CONTEXT_ID, "^sirius-web-([dt]|ap{2}).*[an]$", false, false, /* useRegularExpressions */ true, false);
assertThat(matches).containsExactlyInAnyOrder(
"sirius-web-tests-data",
"sirius-web-domain",
"sirius-web-application"
);
}

private List<String> search(UUID editingContextId, String text, boolean matchCase, boolean matchWholeWord, boolean useRegularExpressions, boolean searchInAttributes) {
// The SearchQuery object must be passed as a plain Map here
var queryMap = Map.of(
"text", text,
"matchCase", matchCase,
"matchWholeWord", matchWholeWord,
"useRegularExpression", useRegularExpressions,
"searchInAttributes", searchInAttributes
);
Map<String, Object> variables = Map.of(
"editingContextId", editingContextId.toString(),
"query", queryMap
);
var result = this.searchQueryRunner.run(variables);

String payloadTypename = JsonPath.read(result, "$.data.viewer.editingContext.search.__typename");
assertThat(payloadTypename).isEqualTo(SearchSuccessPayload.class.getSimpleName());

String resultTypename = JsonPath.read(result, "$.data.viewer.editingContext.search.result.__typename");
assertThat(resultTypename).isEqualTo(SearchResult.class.getSimpleName());

return JsonPath.read(result, "$.data.viewer.editingContext.search.result.matches[*].label");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"react-dom": "18.3.1",
"react-i18next": "16.2.3",
"react-router-dom": "6.26.0",
"react-window": "1.8.11",
"react-window": "2.2.2",
"@xyflow/react": "12.6.0",
"tss-react": "4.9.16",
"react-resizable-panels": "3.0.2"
Expand Down Expand Up @@ -129,7 +129,7 @@
"react-dom": "18.3.1",
"react-i18next": "16.2.3",
"react-router-dom": "6.26.0",
"react-window": "1.8.11",
"react-window": "2.2.2",
"rollup-plugin-peer-deps-external": "2.2.4",
"react-resizable-panels": "3.0.2",
"typescript": "5.4.5",
Expand Down
Loading
Loading