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 + + """