Skip to content

Commit

Permalink
Merge pull request #18 from sandstorm/feature/12-persistent-resource-…
Browse files Browse the repository at this point in the history
…fixtures

add table printer / BDD step for creating persistent resource / asset fixtures
  • Loading branch information
erickloss authored Sep 21, 2022
2 parents 0076f74 + 4336bde commit 342f7ac
Show file tree
Hide file tree
Showing 8 changed files with 507 additions and 109 deletions.
15 changes: 14 additions & 1 deletion Classes/StepGenerator/GherkinTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,23 @@ public function print()
echo ' ';
foreach ($row as $i => $value) {
echo ' | ';
echo str_pad($value, $this->maxColumnWidth[$i]);
echo self::escapeGherkinTableCellValue(str_pad($value, $this->maxColumnWidth[$i]));
}
echo ' |';
echo "\n";
}
}

public function isEmpty(): bool {
return count($this->rows) === 0;
}

private static function escapeGherkinTableCellValue(?string $value): ?string
{
if ($value === null) {
return null;
} else {
return str_replace('|', '\|', $value);
}
}
}
64 changes: 64 additions & 0 deletions Classes/StepGenerator/ImageTable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Sandstorm\E2ETestTools\StepGenerator;

use Neos\Media\Domain\Model\ImageInterface;

/**
* Builder for printing a persistent resource fixture gherkin table.
*/
class ImageTable
{

private static array $IMAGE_DEFAULT_HEADER = [
'Image ID',
'Width',
'Height'
];

private GherkinTable $gherkinTable;
private PersistentResourceFixtures $persistentResourceFixtures;

private array $defaultProperties;

public function __construct(PersistentResourceFixtures $persistentResourceFixtures, array $defaultProperties = [])
{
$this->persistentResourceFixtures = $persistentResourceFixtures;
$this->defaultProperties = $defaultProperties;
$this->gherkinTable = new GherkinTable(
array_merge(
self::$IMAGE_DEFAULT_HEADER,
array_keys($defaultProperties),
PersistentResourceFixtures::$PERSISTENT_RESOURCE_DEFAULT_HEADER
)
);
}

public function addImage(string $objectIdentifier, ImageInterface $image): void
{
$persistentResourceCells = $this->persistentResourceFixtures->addPersistentResource($image->getResource());
$imageCells = array_merge(
$this->defaultProperties,
[
'Image ID' => $objectIdentifier,
'Width' => $image->getWidth(),
'Height' => $image->getHeight()
],
$persistentResourceCells
);
$this->gherkinTable->addRow($imageCells);
}

public function print(): void
{
if ($this->gherkinTable->isEmpty()) {
echo '# I have no images';
echo "\n";
return;
}
echo 'Given I have the following images:';
echo "\n";
$this->gherkinTable->print();
}

}
55 changes: 49 additions & 6 deletions Classes/StepGenerator/NodeTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,39 @@
namespace Sandstorm\E2ETestTools\StepGenerator;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Media\Domain\Model\ImageInterface;
use Neos\Media\Domain\Model\ResourceBasedInterface;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;

/**
*
* @internal
*/
class NodeTable
{
private GherkinTable $gherkinTable;
private GherkinTable $nodeTable;
private PersistentResourceFixtures $persistentResourceFixtures;
private ImageTable $imageTable;

private array $defaultProperties;

public function __construct(array $defaultProperties = [])
public function __construct(
array $defaultProperties = [],
?string $fixtureBasePath = null,
array $defaultPersistentResourceProperties = [],
array $defaultImageProperties = []
)
{
$this->defaultProperties = $defaultProperties;
$this->gherkinTable = new GherkinTable(array_merge([
$this->nodeTable = new GherkinTable(array_merge([
'Path',
'Node Type',
'Properties',
'HiddenInIndex'
], array_keys($defaultProperties)));
$this->persistentResourceFixtures = new PersistentResourceFixtures($fixtureBasePath, $defaultPersistentResourceProperties);
$this->imageTable = new ImageTable($this->persistentResourceFixtures, $defaultImageProperties);
}

public function addNode(NodeInterface $node): void
Expand All @@ -34,14 +47,39 @@ public function addNode(NodeInterface $node): void
if ($node->isAutoCreated()) {
return;
}
$this->gherkinTable->addRow(array_merge($this->defaultProperties, [
$this->nodeTable->addRow(array_merge($this->defaultProperties, [
'Path' => $node->getPath(),
'Node Type' => $node->getNodeType()->getName(),
'Properties' => json_encode($node->getNodeData()->getProperties()),
'Properties' => $this->serializeNodeProperties($node),
'HiddenInIndex' => $node->isHiddenInIndex() ? 'true' : 'false'
]));
}

private function serializeNodeProperties(NodeInterface $node): string
{
$properties = $node->getNodeData()->getProperties();
$propertiesSerialized = [];
foreach ($properties as $propertyName => $propertyValue) {
if ($propertyValue instanceof ResourceBasedInterface) {
$objectType = TypeHandling::getTypeForValue($propertyValue);
$objectIdentifier = ObjectAccess::getProperty($propertyValue, 'Persistence_Object_Identifier', true);
$serializedReference = [
'__flow_object_type' => $objectType,
'__identifier' => $objectIdentifier
];
$propertiesSerialized[$propertyName] = $serializedReference;
// keep track of external resources that needs to be stored as fixtures later
if ($propertyValue instanceof ImageInterface) {
// add image
$this->imageTable->addImage($objectIdentifier, $propertyValue);
}
} else {
$propertiesSerialized[$propertyName] = $propertyValue;
}
}
return json_encode($propertiesSerialized);
}

/**
* Auto generated child-nodes are excluded here. But we don't need them anyways, since they
* are created automatically during import based on the node type yaml.
Expand Down Expand Up @@ -76,8 +114,13 @@ public function addParents(NodeInterface $node): void

public function print(): void
{
$this->persistentResourceFixtures->storeFixtures();

$this->imageTable->print();

echo 'Given I have the following nodes:';
echo "\n";
$this->gherkinTable->print();
$this->nodeTable->print();
}

}
59 changes: 59 additions & 0 deletions Classes/StepGenerator/NodeTableBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Sandstorm\E2ETestTools\StepGenerator;

use Neos\Flow\Package\PackageManager;

/**
* Public builder API to configure and create a NodeTable to use in your step generator command controller.
*/
class NodeTableBuilder
{

private PackageManager $packageManager;
private array $defaultNodeProperties = [];
private array $defaultPersistentResourceProperties = [];
private array $defaultImageProperties = [];
private ?string $fixtureBasePath = null;

public function __construct(PackageManager $packageManager)
{
$this->packageManager = $packageManager;
}

public function withDefaultNodeProperties(array $defaultNodeProperties): NodeTableBuilder
{
$this->defaultNodeProperties = $defaultNodeProperties;
return $this;
}

public function withDefaultPersistentResourceProperties(array $defaultPersistentResourceProperties): NodeTableBuilder
{
$this->defaultPersistentResourceProperties = $defaultPersistentResourceProperties;
return $this;
}

public function withDefaultImageProperties(array $defaultNodeProperties): NodeTableBuilder
{
$this->defaultNodeProperties = $defaultNodeProperties;
return $this;
}

public function withFixturesBaseDirectory(string $packageKey, string $subPath): NodeTableBuilder
{
$package = $this->packageManager->getPackage($packageKey);
$this->fixtureBasePath = $package->getPackagePath() . $subPath;
return $this;
}

public function build(): NodeTable
{
return new NodeTable(
$this->defaultNodeProperties,
$this->fixtureBasePath,
$this->defaultPersistentResourceProperties,
$this->defaultImageProperties
);
}

}
26 changes: 26 additions & 0 deletions Classes/StepGenerator/NodeTableBuilderService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Sandstorm\E2ETestTools\StepGenerator;

use Neos\Flow\Package\PackageManager;
use Neos\Flow\Annotations as Flow;

/**
* Public builder API to configure and create a NodeTable to use in your step generator command controller.
*
* @Flow\Scope("singleton")
*/
class NodeTableBuilderService
{

/**
* @Flow\Inject
*/
protected PackageManager $packageManager;

public function nodeTable(): NodeTableBuilder
{
return new NodeTableBuilder($this->packageManager);
}

}
86 changes: 86 additions & 0 deletions Classes/StepGenerator/PersistentResourceFixtures.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace Sandstorm\E2ETestTools\StepGenerator;

use Neos\Flow\ResourceManagement\PersistentResource;

/**
* Model for persistent resources fixtures collected in a NodeTable.
*/
class PersistentResourceFixtures
{

public static array $PERSISTENT_RESOURCE_DEFAULT_HEADER = [
'Filename',
'Collection',
'Relative Publication Path',
'Path'
];

private ?string $fixtureBasePath;

private array $defaultProperties = [];
private array $fixtureResources = [];

public function __construct(?string $fixtureBasePath, array $defaultProperties = [])
{
if ($fixtureBasePath !== null) {
$this->fixtureBasePath = $fixtureBasePath . (str_ends_with($fixtureBasePath, '/') ? '' : '/');
if (!str_starts_with($fixtureBasePath, FLOW_PATH_PACKAGES)) {
throw new \Exception("fixtureBasePath must be sub dir of Flow packages directory '" . FLOW_PATH_PACKAGES . "'; but was: " . $fixtureBasePath);
}
} else {
$this->fixtureBasePath = null;
}
$this->defaultProperties = $defaultProperties;
}

/**
* Adds a persistent resource to the fixtures for later storing.
* Returns the gherkin table cells of the PersistentResource to be printed in a Media fixture table row.
*
* @param PersistentResource $persistentResource
* @return array the PersistentResource table cells
*/
public function addPersistentResource(PersistentResource $persistentResource): array
{
if ($this->fixtureBasePath === null) {
throw new \Exception("No fixture base path given for NodeTable");
}
$fixturePath = $this->fixtureBasePath . $persistentResource->getSha1() . '.' . $persistentResource->getFileExtension();
if (!array_key_exists($persistentResource->getSha1(), $this->fixtureResources)) {
$this->fixtureResources[$persistentResource->getSha1()] = [
'object' => $persistentResource,
'path' => $fixturePath
];
}
$fixturePathRelativeToFlowRoot = substr($fixturePath, strlen(FLOW_PATH_PACKAGES));
return array_merge($this->defaultProperties, [
'Filename' => $persistentResource->getFilename(),
'Collection' => $persistentResource->getCollectionName(),
'Relative Publication Path' => $persistentResource->getRelativePublicationPath(),
'Path' => $fixturePathRelativeToFlowRoot
]);
}

public function storeFixtures(): void
{
if (count($this->fixtureResources) === 0) {
return;
}
if (!file_exists($this->fixtureBasePath)) {
mkdir($this->fixtureBasePath, 0777, true);
}
foreach ($this->fixtureResources as $sha1 => $persistentResourceObjectAndPath) {
$path = $persistentResourceObjectAndPath['path'];
if (!file_exists($path)) {
/**
* @var PersistentResource $persistentResource
*/
$persistentResource = $persistentResourceObjectAndPath['object'];
file_put_contents($path, $persistentResource->getStream());
}
}
}

}
Loading

0 comments on commit 342f7ac

Please sign in to comment.