diff --git a/Neos.Neos/Tests/Behavior/Features/Bootstrap/FusionTrait.php b/Neos.Neos/Tests/Behavior/Features/Bootstrap/FusionTrait.php
new file mode 100644
index 00000000000..ecd35ee994b
--- /dev/null
+++ b/Neos.Neos/Tests/Behavior/Features/Bootstrap/FusionTrait.php
@@ -0,0 +1,184 @@
+fusionRequest = null;
+ $this->fusionContext = [];
+ $this->fusionCode = null;
+ $this->renderingResult = null;
+ }
+
+ /**
+ * @When the Fusion context node is :nodeIdentifier
+ */
+ public function theFusionContextNodeIs(string $nodeIdentifier): void
+ {
+ /** @var ContentContext $context */
+ $context = $this->getContextForProperties([]);
+ $this->fusionContext['node'] = $context->getNodeByIdentifier($nodeIdentifier);
+ if ($this->fusionContext['node'] === null) {
+ throw new \InvalidArgumentException(sprintf('Node with identifier "%s" could not be found in the "%s" workspace', $nodeIdentifier, $context->getWorkspaceName()), 1696700222);
+ }
+ $flowQuery = new FlowQuery([$this->fusionContext['node']]);
+ $this->fusionContext['documentNode'] = $flowQuery->closest('[instanceof Neos.Neos:Document]')->get(0);
+ if ($this->fusionContext['documentNode'] === null) {
+ throw new \RuntimeException(sprintf('Failed to find closest document node for node with identifier "%s"', $nodeIdentifier), 1697790940);
+ }
+ $this->fusionContext['site'] = $context->getCurrentSiteNode();
+ if ($this->fusionContext['site'] === null) {
+ throw new \RuntimeException(sprintf('Failed to resolve site node for node with identifier "%s"', $nodeIdentifier), 1697790963);
+ }
+ }
+
+ /**
+ * @When the Fusion context request URI is :requestUri
+ */
+ public function theFusionContextRequestIs(string $requestUri = null): void
+ {
+ $httpRequest = $this->objectManager->get(ServerRequestFactoryInterface::class)->createServerRequest('GET', $requestUri);
+ $httpRequest = $this->addRoutingParameters($httpRequest);
+
+ $this->fusionRequest = ActionRequest::fromHttpRequest($httpRequest);
+ }
+
+ private function addRoutingParameters(ServerRequestInterface $httpRequest): ServerRequestInterface
+ {
+ $spyMiddleware = new SpyRequestHandler();
+ (new RequestUriHostMiddleware())->process($httpRequest, $spyMiddleware);
+ return $spyMiddleware->getHandledRequest();
+ }
+
+ /**
+ * @When I have the following Fusion setup:
+ */
+ public function iHaveTheFollowingFusionSetup(PyStringNode $fusionCode): void
+ {
+ $this->fusionCode = $fusionCode->getRaw();
+ }
+
+ /**
+ * @When I execute the following Fusion code:
+ * @When I execute the following Fusion code on path :path:
+ */
+ public function iExecuteTheFollowingFusionCode(PyStringNode $fusionCode, string $path = 'test'): void
+ {
+ if ($this->fusionRequest === null) {
+ $this->theFusionContextRequestIs('http://localhost');
+ }
+ $requestHandler = new FunctionalTestRequestHandler(self::$bootstrap);
+ $requestHandler->setHttpRequest($this->fusionRequest->getHttpRequest());
+ self::$bootstrap->setActiveRequestHandler($requestHandler);
+ $this->throwExceptionIfLastRenderingLedToAnError();
+ $this->renderingResult = null;
+ $fusionAst = (new Parser())->parseFromSource(FusionSourceCodeCollection::fromString($this->fusionCode . chr(10) . $fusionCode->getRaw()));
+ $uriBuilder = new UriBuilder();
+ $uriBuilder->setRequest($this->fusionRequest);
+ $controllerContext = new ControllerContext($this->fusionRequest, new ActionResponse(), new Arguments(), $uriBuilder);
+
+ $fusionRuntime = (new RuntimeFactory())->createFromConfiguration($fusionAst, $controllerContext);
+ $fusionRuntime->pushContextArray($this->fusionContext);
+ try {
+ $this->renderingResult = $fusionRuntime->render($path);
+ } catch (\Throwable $exception) {
+ $this->lastRenderingException = $exception;
+ }
+ $fusionRuntime->popContext();
+ }
+
+ /**
+ * @Then I expect the following Fusion rendering result:
+ */
+ public function iExpectTheFollowingFusionRenderingResult(PyStringNode $expectedResult): void
+ {
+ Assert::assertSame($expectedResult->getRaw(), $this->renderingResult);
+ }
+
+ /**
+ * @Then I expect the following Fusion rendering result as HTML:
+ */
+ public function iExpectTheFollowingFusionRenderingResultAsHtml(PyStringNode $expectedResult): void
+ {
+ Assert::assertIsString($this->renderingResult, 'Previous Fusion rendering did not produce a string');
+ $stripWhitespace = static fn (string $input): string => preg_replace(['/>[^\S ]+/s', '/[^\S ]+ ', '<', '\\1', '><'], $input);
+
+ $expectedDom = new \DomDocument();
+ $expectedDom->preserveWhiteSpace = false;
+ $expectedDom->loadHTML($stripWhitespace($expectedResult->getRaw()));
+
+ $actualDom = new \DomDocument();
+ $actualDom->preserveWhiteSpace = false;
+ $actualDom->loadHTML($stripWhitespace($this->renderingResult));
+
+ Assert::assertSame($expectedDom->saveHTML(), $actualDom->saveHTML());
+ }
+ /**
+ * @Then I expect the following Fusion rendering error:
+ */
+ public function iExpectTheFollowingFusionRenderingError(PyStringNode $expectedError): void
+ {
+ Assert::assertNotNull($this->lastRenderingException, 'The previous rendering did not lead to an error');
+ Assert::assertSame($expectedError->getRaw(), $this->lastRenderingException->getMessage());
+ $this->lastRenderingException = null;
+ }
+
+ /**
+ * @AfterScenario
+ */
+ public function throwExceptionIfLastRenderingLedToAnError(): void
+ {
+ if ($this->lastRenderingException !== null) {
+ throw new \RuntimeException(sprintf('The last rendering led to an error: %s', $this->lastRenderingException->getMessage()), 1698319254, $this->lastRenderingException);
+ }
+ }
+}
diff --git a/Neos.Neos/Tests/Behavior/Features/Fusion/ContentCase.feature b/Neos.Neos/Tests/Behavior/Features/Fusion/ContentCase.feature
new file mode 100644
index 00000000000..c18aaffdb6b
--- /dev/null
+++ b/Neos.Neos/Tests/Behavior/Features/Fusion/ContentCase.feature
@@ -0,0 +1,60 @@
+@fixtures
+Feature: Tests for the "Neos.Neos:ContentCase" Fusion prototype
+
+ Background:
+ Given I have the site "a"
+ And I have the following NodeTypes configuration:
+ """yaml
+ 'unstructured': {}
+ 'Neos.Neos:FallbackNode': {}
+ 'Neos.Neos:Document': {}
+ 'Neos.Neos:Test.DocumentType1':
+ superTypes:
+ 'Neos.Neos:Document': true
+ 'Neos.Neos:Test.DocumentType2':
+ superTypes:
+ 'Neos.Neos:Document': true
+ """
+ And I have the following nodes:
+ | Identifier | Path | Node Type |
+ | root | /sites | unstructured |
+ | a | /sites/a | Neos.Neos:Test.DocumentType1 |
+ | a1 | /sites/a/a1 | Neos.Neos:Test.DocumentType2 |
+ And the Fusion context node is "a1"
+ And the Fusion context request URI is "http://localhost"
+
+ Scenario: ContentCase without corresponding implementation
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ContentCase
+ """
+ Then I expect the following Fusion rendering error:
+ """
+ The Fusion object "Neos.Neos:Test.DocumentType2" cannot be rendered:
+ Most likely you mistyped the prototype name or did not define
+ the Fusion prototype with "prototype(Neos.Neos:Test.DocumentType2) < prototype(...)".
+ Other possible reasons are a missing parent-prototype or
+ a missing "@class" annotation for prototypes without parent.
+ It is also possible your Fusion file is not read because
+ of a missing "include:" statement.
+ """
+
+ Scenario: ContentCase with corresponding implementation
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ prototype(Neos.Neos:Test.DocumentType2) < prototype(Neos.Fusion:Value) {
+ value = 'implementation for DocumentType2'
+ }
+
+ test = Neos.Neos:ContentCase
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ implementation for DocumentType2
+ """
diff --git a/Neos.Neos/Tests/Behavior/Features/Fusion/ContentCollection.feature b/Neos.Neos/Tests/Behavior/Features/Fusion/ContentCollection.feature
new file mode 100644
index 00000000000..ed84afac431
--- /dev/null
+++ b/Neos.Neos/Tests/Behavior/Features/Fusion/ContentCollection.feature
@@ -0,0 +1,94 @@
+@fixtures
+Feature: Tests for the "Neos.Neos:ContentCollection" Fusion prototype
+
+ Background:
+ Given I have the site "a"
+ And I have the following NodeTypes configuration:
+ """yaml
+ 'unstructured': {}
+ 'Neos.Neos:FallbackNode': {}
+ 'Neos.Neos:Document': {}
+ 'Neos.Neos:ContentCollection': {}
+ 'Neos.Neos:Content': {}
+ 'Neos.Neos:Test.DocumentType':
+ superTypes:
+ 'Neos.Neos:Document': true
+ childNodes:
+ main:
+ type: 'Neos.Neos:ContentCollection'
+ 'Neos.Neos:Test.ContentType':
+ superTypes:
+ 'Neos.Neos:Content': true
+ """
+ And I have the following nodes:
+ | Identifier | Path | Node Type |
+ | root | /sites | unstructured |
+ | a | /sites/a | Neos.Neos:Test.DocumentType |
+ And the Fusion context node is "a"
+ And the Fusion context request URI is "http://localhost"
+
+# Scenario: missing Neos.Neos.ContentCollection node
+# When I execute the following Fusion code:
+# """fusion
+# include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+# include: resource://Neos.Neos/Private/Fusion/Root.fusion
+#
+# test = Neos.Neos:ContentCollection
+# """
+# Then I expect the following Fusion rendering error:
+# """
+# No content collection of type Neos.Neos:ContentCollection could be found in the current node (/sites/a) or at the path "to-be-set-by-user". You might want to adjust your node type configuration and create the missing child node through the "./flow node:repair --node-type Neos.Neos:Test.DocumentType" command.
+# """
+#
+# Scenario: invalid nodePath
+# When I execute the following Fusion code:
+# """fusion
+# include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+# include: resource://Neos.Neos/Private/Fusion/Root.fusion
+#
+# test = Neos.Neos:ContentCollection {
+# nodePath = 'invalid'
+# }
+# """
+# Then I expect the following Fusion rendering error:
+# """
+# No content collection of type Neos.Neos:ContentCollection could be found in the current node (/sites/a) or at the path "invalid". You might want to adjust your node type configuration and create the missing child node through the "./flow node:repair --node-type Neos.Neos:Test.DocumentType" command.
+# """
+
+ Scenario: empty ContentCollection
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ContentCollection {
+ nodePath = 'main'
+ }
+ """
+ Then I expect the following Fusion rendering result as HTML:
+ """
+
+ """
+
+ Scenario:
+ When I have the following nodes:
+ | Identifier | Path | Node Type |
+ | content1 | /sites/a/main/content1 | Neos.Neos:Test.ContentType |
+ | content2 | /sites/a/main/content2 | Neos.Neos:Test.ContentType |
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ prototype(Neos.Neos:Test.ContentType) < prototype(Neos.Fusion:Value) {
+ value = ${node.identifier + ' (' + node.nodeType.name + ') '}
+ }
+
+ test = Neos.Neos:ContentCollection {
+ nodePath = 'main'
+ }
+ """
+ Then I expect the following Fusion rendering result as HTML:
+ """
+ content1 (Neos.Neos:Test.ContentType) content2 (Neos.Neos:Test.ContentType)
+ """
diff --git a/Neos.Neos/Tests/Behavior/Features/Fusion/ConvertUris.feature b/Neos.Neos/Tests/Behavior/Features/Fusion/ConvertUris.feature
new file mode 100644
index 00000000000..41bac4f6d41
--- /dev/null
+++ b/Neos.Neos/Tests/Behavior/Features/Fusion/ConvertUris.feature
@@ -0,0 +1,155 @@
+@fixtures
+Feature: Tests for the "Neos.Neos:ConvertUris" Fusion prototype
+
+ Background:
+ Given I have the site "a"
+ And I have the following NodeTypes configuration:
+ """yaml
+ 'unstructured': {}
+ 'Neos.Neos:FallbackNode': {}
+ 'Neos.Neos:Document': {}
+ 'Neos.Neos:ContentCollection': {}
+ 'Neos.Neos:Test.DocumentType':
+ superTypes:
+ 'Neos.Neos:Document': true
+ """
+ And I have the following nodes:
+ | Identifier | Path | Node Type | Properties |
+ | root | /sites | unstructured | |
+ | a | /sites/a | Neos.Neos:Test.DocumentType | {"uriPathSegment": "a", "title": "Node a"} |
+ | a1 | /sites/a/a1 | Neos.Neos:Test.DocumentType | {"uriPathSegment": "a1", "title": "Node a1"} |
+ And the Fusion context node is "a"
+ And the Fusion context request URI is "http://localhost"
+
+ Scenario: Default output
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ """
+
+ Scenario: Without URI
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'Some value without URI'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ Some value without URI
+ """
+
+ Scenario: URI to non-existing node
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'Some value with node URI to non-existing node: node://non-existing.'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ Some value with node URI to non-existing node: .
+ """
+
+ Scenario: URI to existing node
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'Some value with node URI: node://a1.'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ Some value with node URI: /en/a1.
+ """
+
+ Scenario: Anchor tag without node or asset URI
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'some Link'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ some Link
+ """
+
+ Scenario: Anchor tag with node URI to non-existing node
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'some Link'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ some Link
+ """
+
+ Scenario: Anchor tag with URI to existing node
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'some Link'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ some Link
+ """
+
+ Scenario: URI to non-existing asset
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'Some value with node URI to non-existing asset: asset://non-existing.'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ Some value with node URI to non-existing asset: .
+ """
+
+ Scenario: URI to existing asset
+ When an asset exists with id "362f3049-b9bb-454d-8769-6b35167e471e"
+ And I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:ConvertUris {
+ value = 'Some value with node URI: asset://362f3049-b9bb-454d-8769-6b35167e471e.'
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ Some value with node URI: http://localhost/_Resources/Testing/Persistent/d0a1342bcb0e515bea83269427d8341d5f62a43d/test.svg.
+ """
diff --git a/Neos.Neos/Tests/Behavior/Features/Fusion/Menu.feature b/Neos.Neos/Tests/Behavior/Features/Fusion/Menu.feature
new file mode 100644
index 00000000000..58dab2f4ebe
--- /dev/null
+++ b/Neos.Neos/Tests/Behavior/Features/Fusion/Menu.feature
@@ -0,0 +1,481 @@
+@fixtures
+Feature: Tests for the "Neos.Neos:Menu" and related Fusion prototypes
+
+ Background:
+ Given I have the site "a"
+ And I have the following NodeTypes configuration:
+ """yaml
+ 'unstructured': {}
+ 'Neos.Neos:FallbackNode': {}
+ 'Neos.Neos:Document':
+ properties:
+ title:
+ type: string
+ uriPathSegment:
+ type: string
+ 'Neos.Neos:Content':
+ properties:
+ title:
+ type: string
+ 'Neos.Neos:Test.DocumentType1':
+ superTypes:
+ 'Neos.Neos:Document': true
+ 'Neos.Neos:Test.DocumentType2':
+ superTypes:
+ 'Neos.Neos:Document': true
+ 'Neos.Neos:Test.DocumentType2a':
+ superTypes:
+ 'Neos.Neos:Test.DocumentType2': true
+ 'Neos.Neos:Test.Content':
+ superTypes:
+ 'Neos.Neos:Content': true
+ """
+ And I have the following nodes:
+ | Identifier | Path | Node Type | Properties | Hidden in index |
+ | root | /sites | unstructured | | false |
+ | a | /sites/a | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a", "title": "Node a"} | false |
+ | a1 | /sites/a/a1 | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a1", "title": "Node a1"} | false |
+ | a1a | /sites/a/a1/a1a | Neos.Neos:Test.DocumentType2a | {"uriPathSegment": "a1a", "title": "Node a1a"} | false |
+ | a1b | /sites/a/a1/a1b | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a1b", "title": "Node a1b"} | false |
+ | a1b1 | /sites/a/a1/a1b/a1b1 | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a1b1", "title": "Node a1b1"} | false |
+ | a1b1a | /sites/a/a1/a1b/a1b1/a1b1a | Neos.Neos:Test.DocumentType2a | {"uriPathSegment": "a1b1a", "title": "Node a1b1a"} | false |
+ | a1b1b | /sites/a/a1/a1b/a1b1/a1b1b | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a1b1b", "title": "Node a1b1b"} | false |
+ | a1b2 | /sites/a/a1/a1b/a1b2 | Neos.Neos:Test.DocumentType2 | {"uriPathSegment": "a1b2", "title": "Node a1b2"} | false |
+ | a1b3 | /sites/a/a1/a1b/a1b3 | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a1b3", "title": "Node a1b3"} | false |
+ | a1c | /sites/a/a1/a1c | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a1c", "title": "Node a1c"} | true |
+ | a1c1 | /sites/a/a1/a1c/a1c1 | Neos.Neos:Test.DocumentType1 | {"uriPathSegment": "a1c1", "title": "Node a1c1"} | false |
+ And the Fusion context node is "a1a"
+ And the Fusion context request URI is "http://localhost"
+ And I have the following Fusion setup:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ prototype(Neos.Neos:Test.Menu.ItemStateIndicator) < prototype(Neos.Fusion:Component) {
+ state = null
+ renderer = Neos.Fusion:Match {
+ @subject = ${props.state}
+ @default = '?'
+ normal = ''
+ current = '*'
+ active = '.'
+ absent = 'x'
+ }
+ }
+
+ prototype(Neos.Neos:Test.Menu) < prototype(Neos.Fusion:Component) {
+ items = ${[]}
+ renderer = Neos.Fusion:Loop {
+ items = ${props.items}
+ itemRenderer = afx`
+ {item.node.identifier} ({item.menuLevel}){String.chr(10)}
+
+ `
+ }
+ }
+ """
+
+ Scenario: MenuItems (default)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+
+ """
+
+ Scenario: MenuItems (maximumLevels = 3)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ maximumLevels = 3
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+ a1b1 (3)
+ a1b2 (3)
+ a1b3 (3)
+
+ """
+
+ Scenario: MenuItems (entryLevel = -5)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ entryLevel = -5
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+
+ """
+
+
+ Scenario: MenuItems (entryLevel = -1)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ entryLevel = -1
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1a* (1)
+ a1b (1)
+ a1b1 (2)
+ a1b2 (2)
+ a1b3 (2)
+
+ """
+
+ Scenario: MenuItems (entryLevel = 0)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ entryLevel = 0
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+
+ """
+
+ Scenario: MenuItems (entryLevel = 2)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ entryLevel = 2
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1a* (1)
+ a1b (1)
+ a1b1 (2)
+ a1b2 (2)
+ a1b3 (2)
+
+ """
+
+ Scenario: MenuItems (entryLevel = 5)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ entryLevel = 5
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+
+ """
+
+ Scenario: MenuItems (lastLevel = -5)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ lastLevel = -5
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+
+ """
+
+ Scenario: MenuItems (lastLevel = -1)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ lastLevel = -1
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+
+ """
+
+ Scenario: MenuItems (lastLevel = 0)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ lastLevel = 0
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+
+ """
+
+ Scenario: MenuItems (lastLevel = 1)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ lastLevel = 1
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+
+ """
+
+ Scenario: MenuItems (filter non existing node type)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ filter = 'Non.Existing:NodeType'
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ """
+
+ Scenario: MenuItems (filter = DocumentType1)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ filter = 'Neos.Neos:Test.DocumentType1'
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1b (2)
+
+ """
+
+ Scenario: MenuItems (filter = DocumentType2)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ filter = 'Neos.Neos:Test.DocumentType2'
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+
+ """
+
+ Scenario: MenuItems (renderHiddenInIndex)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ renderHiddenInIndex = true
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+ a1c (2)
+
+ """
+
+ Scenario: MenuItems (empty itemCollection)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ itemCollection = ${[]}
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ """
+
+ Scenario: MenuItems (itemCollection document nodes)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ itemCollection = ${q(site).filter('[instanceof Neos.Neos:Document]').get()}
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a (1)
+ a1. (2)
+
+ """
+
+ Scenario: MenuItems (startingPoint a1b1)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ startingPoint = ${q(node).children('[instanceof Neos.Neos:Document]').children('[instanceof Neos.Neos:Document]').get(0)}
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+
+ """
+
+ Scenario: MenuItems (startingPoint a1b1, negative entryLevel)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ startingPoint = ${q(node).children('[instanceof Neos.Neos:Document]').children('[instanceof Neos.Neos:Document]').get(0)}
+ entryLevel = -1
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1a* (1)
+ a1b (1)
+ a1b1 (2)
+ a1b2 (2)
+ a1b3 (2)
+
+ """
+
+ Scenario: MenuItems (startingPoint a1b1, negative lastLevel)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ startingPoint = ${q(node).children('[instanceof Neos.Neos:Document]').children('[instanceof Neos.Neos:Document]').get(0)}
+ lastLevel = -1
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1. (1)
+ a1a* (2)
+ a1b (2)
+
+ """
+
+ Scenario: MenuItems (startingPoint a1b, filter DocumentType2a)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ startingPoint = ${q(node).children('[instanceof Neos.Neos:Document]').get(0)}
+ filter = 'Neos.Neos:Test.DocumentType2a'
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+
+ """
+
+ Scenario: MenuItems (startingPoint a1c, renderHiddenInIndex)
+ When I execute the following Fusion code:
+ """fusion
+ test = Neos.Neos:Test.Menu {
+ items = Neos.Neos:MenuItems {
+ startingPoint = ${q(node).find('#a1c').get(0)}
+ renderHiddenInIndex = true
+ }
+ }
+ """
+ Then I expect the following Fusion rendering result:
+ """
+ a1c1 (1)
+
+ """
+
+ Scenario: Menu
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:Menu
+ """
+ Then I expect the following Fusion rendering result as HTML:
+ """html
+
+ """
+
+ Scenario: BreadcrumbMenu
+ When I execute the following Fusion code:
+ """fusion
+ include: resource://Neos.Fusion/Private/Fusion/Root.fusion
+ include: resource://Neos.Neos/Private/Fusion/Root.fusion
+
+ test = Neos.Neos:BreadcrumbMenu
+ """
+ Then I expect the following Fusion rendering result as HTML:
+ """html
+
+ """