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
10 changes: 4 additions & 6 deletions packages/view-latte/module.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@

declare(strict_types=1);

use Latte\Engine;
use Marko\Core\Container\ContainerInterface;
use Marko\View\Latte\LatteEngineFactory;
use Marko\View\Latte\LatteView;
use Marko\View\TemplateResolverInterface;
use Marko\View\ViewInterface;

return [
'bindings' => [
ViewInterface::class => function (ContainerInterface $container): ViewInterface {
$engine = $container->get(LatteEngineFactory::class)->create();
$resolver = $container->get(TemplateResolverInterface::class);

return new LatteView($engine, $resolver);
ViewInterface::class => LatteView::class,
Engine::class => function (ContainerInterface $container): Engine {
return $container->get(LatteEngineFactory::class)->create();
},
],
];
38 changes: 17 additions & 21 deletions packages/view-latte/tests/ModuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
use Marko\Core\Container\ContainerInterface;
use Marko\View\Latte\LatteEngineFactory;
use Marko\View\Latte\LatteView;
use Marko\View\TemplateResolverInterface;
use Marko\View\ViewInterface;

describe('view-latte module.php', function (): void {
test('module.php exists with correct structure', function (): void {
test('it has a bindings array', function (): void {
$modulePath = dirname(__DIR__) . '/module.php';

expect(file_exists($modulePath))->toBeTrue();
Expand All @@ -22,43 +21,40 @@
->and($module['bindings'])->toBeArray();
});

test('module.php binds ViewInterface via factory', function (): void {
test('it binds ViewInterface to LatteView as a simple class mapping', function (): void {
$module = require dirname(__DIR__) . '/module.php';

expect($module['bindings'])->toHaveKey(ViewInterface::class)
->and($module['bindings'][ViewInterface::class])->toBeInstanceOf(Closure::class);
->and($module['bindings'][ViewInterface::class])->toBe(LatteView::class);
});

test('module.php uses LatteEngineFactory', function (): void {
test('it binds Latte Engine via a closure that calls LatteEngineFactory', function (): void {
$module = require dirname(__DIR__) . '/module.php';

expect($module['bindings'])->toHaveKey(Engine::class)
->and($module['bindings'][Engine::class])->toBeInstanceOf(Closure::class);
});

test('the Engine closure resolves the engine via LatteEngineFactory::create()', function (): void {
$module = require dirname(__DIR__) . '/module.php';

// Mock the engine from factory
$engine = $this->createMock(Engine::class);

// Mock the LatteEngineFactory
$engineFactory = $this->createMock(LatteEngineFactory::class);
$engineFactory->expects($this->once())
->method('create')
->willReturn($engine);

// Mock the TemplateResolverInterface
$resolver = $this->createMock(TemplateResolverInterface::class);

// Mock the container
$container = $this->createMock(ContainerInterface::class);
$container->method('get')
->willReturnCallback(function (string $class) use ($engineFactory, $resolver) {
return match ($class) {
LatteEngineFactory::class => $engineFactory,
TemplateResolverInterface::class => $resolver,
default => throw new Exception("Unexpected class: $class"),
};
->willReturnCallback(fn (string $class) => match ($class) {
LatteEngineFactory::class => $engineFactory,
default => throw new Exception("Unexpected class: $class"),
});

// Call the factory closure
$factory = $module['bindings'][ViewInterface::class];
$view = $factory($container);
$closure = $module['bindings'][Engine::class];
$result = $closure($container);

expect($view)->toBeInstanceOf(LatteView::class);
expect($result)->toBe($engine);
});
});
10 changes: 4 additions & 6 deletions packages/view-twig/module.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@
declare(strict_types=1);

use Marko\Core\Container\ContainerInterface;
use Marko\View\TemplateResolverInterface;
use Marko\View\Twig\TwigEngineFactory;
use Marko\View\Twig\TwigView;
use Marko\View\ViewInterface;
use Twig\Environment;

return [
'bindings' => [
ViewInterface::class => function (ContainerInterface $container): ViewInterface {
$engine = $container->get(TwigEngineFactory::class)->create();
$resolver = $container->get(TemplateResolverInterface::class);

return new TwigView($engine, $resolver);
ViewInterface::class => TwigView::class,
Environment::class => function (ContainerInterface $container): Environment {
return $container->get(TwigEngineFactory::class)->create();
},
],
];
85 changes: 17 additions & 68 deletions packages/view-twig/tests/ModuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
declare(strict_types=1);

use Marko\Core\Container\ContainerInterface;
use Marko\View\TemplateResolverInterface;
use Marko\View\Twig\TwigEngineFactory;
use Marko\View\Twig\TwigView;
use Marko\View\ViewInterface;
use Twig\Environment;

describe('view-twig module.php', function (): void {
test('it registers a ViewInterface binding', function (): void {
test('it has a bindings array', function (): void {
$modulePath = dirname(__DIR__) . '/module.php';

expect(file_exists($modulePath))->toBeTrue();
Expand All @@ -19,93 +18,43 @@

expect($module)->toBeArray()
->and($module)->toHaveKey('bindings')
->and($module['bindings'])->toBeArray()
->and($module['bindings'])->toHaveKey(ViewInterface::class);
->and($module['bindings'])->toBeArray();
});

test('it resolves ViewInterface to a TwigView instance', function (): void {
test('it binds ViewInterface to TwigView as a simple class mapping', function (): void {
$module = require dirname(__DIR__) . '/module.php';

$engine = $this->createMock(Environment::class);

$engineFactory = $this->createMock(TwigEngineFactory::class);
$engineFactory->expects($this->once())
->method('create')
->willReturn($engine);

$resolver = $this->createMock(TemplateResolverInterface::class);

$container = $this->createMock(ContainerInterface::class);
$container->method('get')
->willReturnCallback(function (string $class) use ($engineFactory, $resolver) {
return match ($class) {
TwigEngineFactory::class => $engineFactory,
TemplateResolverInterface::class => $resolver,
default => throw new Exception("Unexpected class: $class"),
};
});

$factory = $module['bindings'][ViewInterface::class];
$view = $factory($container);

expect($view)->toBeInstanceOf(TwigView::class);
expect($module['bindings'])->toHaveKey(ViewInterface::class)
->and($module['bindings'][ViewInterface::class])->toBe(TwigView::class);
});

test('it injects a configured Twig Environment into TwigView', function (): void {
test('it binds Twig Environment via a closure that calls TwigEngineFactory', function (): void {
$module = require dirname(__DIR__) . '/module.php';

$engine = $this->createMock(Environment::class);

$engineFactory = $this->createMock(TwigEngineFactory::class);
$engineFactory->expects($this->once())
->method('create')
->willReturn($engine);

$resolver = $this->createMock(TemplateResolverInterface::class);

$container = $this->createMock(ContainerInterface::class);
$container->method('get')
->willReturnCallback(function (string $class) use ($engineFactory, $resolver) {
return match ($class) {
TwigEngineFactory::class => $engineFactory,
TemplateResolverInterface::class => $resolver,
default => throw new Exception("Unexpected class: $class"),
};
});

$factory = $module['bindings'][ViewInterface::class];

// The factory closure calls engineFactory->create() — expectation above asserts this
$view = $factory($container);

expect($view)->toBeInstanceOf(TwigView::class);
expect($module['bindings'])->toHaveKey(Environment::class)
->and($module['bindings'][Environment::class])->toBeInstanceOf(Closure::class);
});

test('it injects the shared TemplateResolverInterface into TwigView', function (): void {
test('the Environment closure resolves the engine via TwigEngineFactory::create()', function (): void {
$module = require dirname(__DIR__) . '/module.php';

$engine = $this->createMock(Environment::class);

$engineFactory = $this->createMock(TwigEngineFactory::class);
$engineFactory->method('create')
$engineFactory->expects($this->once())
->method('create')
->willReturn($engine);

$resolver = $this->createMock(TemplateResolverInterface::class);

$container = $this->createMock(ContainerInterface::class);
$container->method('get')
->willReturnCallback(function (string $class) use ($engineFactory, $resolver) {
return match ($class) {
TwigEngineFactory::class => $engineFactory,
TemplateResolverInterface::class => $resolver,
default => throw new Exception("Unexpected class: $class"),
};
->willReturnCallback(fn (string $class) => match ($class) {
TwigEngineFactory::class => $engineFactory,
default => throw new Exception("Unexpected class: $class"),
});

$factory = $module['bindings'][ViewInterface::class];
$view = $factory($container);
$closure = $module['bindings'][Environment::class];
$result = $closure($container);

expect($view)->toBeInstanceOf(TwigView::class)
->and($view)->toBeInstanceOf(ViewInterface::class);
expect($result)->toBe($engine);
});
});