Skip to content

Commit

Permalink
Fake test double for WindowManager::Class: (#422)
Browse files Browse the repository at this point in the history
- Usage of (relatively lenient) WindowManagerContract::class in Laravel container
- Implement WindowManagerFake::class with several testing assertions and helper methods
- Laravel-style facade Window::fake() method
- Tests
  • Loading branch information
XbNz authored Nov 20, 2024
1 parent c29bd0d commit 3e0c562
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 2 deletions.
23 changes: 23 additions & 0 deletions src/Contracts/WindowManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Native\Laravel\Contracts;

use Native\Laravel\Windows\Window;

interface WindowManager
{
public function open(string $id = 'main');

public function close($id = null);

public function hide($id = null);

public function current(): Window;

/**
* @return array<int, Window>
*/
public function all(): array;

public function get(string $id): Window;
}
11 changes: 10 additions & 1 deletion src/Facades/Window.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Native\Laravel\Facades;

use Illuminate\Support\Facades\Facade;
use Native\Laravel\Contracts\WindowManager as WindowManagerContract;
use Native\Laravel\Fakes\WindowManagerFake;

/**
* @method static \Native\Laravel\Windows\PendingOpenWindow open(string $id = 'main')
Expand All @@ -18,8 +20,15 @@
*/
class Window extends Facade
{
public static function fake()
{
return tap(new WindowManagerFake, function ($fake) {
static::swap($fake);
});
}

protected static function getFacadeAccessor()
{
return \Native\Laravel\Windows\WindowManager::class;
return WindowManagerContract::class;
}
}
94 changes: 94 additions & 0 deletions src/Fakes/WindowManagerFake.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace Native\Laravel\Fakes;

use Illuminate\Support\Arr;
use Native\Laravel\Contracts\WindowManager as WindowManagerContract;
use Native\Laravel\Windows\Window;
use PHPUnit\Framework\Assert as PHPUnit;
use RuntimeException;

class WindowManagerFake implements WindowManagerContract
{
public array $opened = [];

public array $closed = [];

public array $hidden = [];

public array $forcedWindowReturnValues = [];

/**
* @param array<int, Window> $windows
*/
public function alwaysReturnWindows(array $windows): self
{
$this->forcedWindowReturnValues = $windows;

return $this;
}

public function open(string $id = 'main')
{
$this->opened[] = $id;
}

public function close($id = null)
{
$this->closed[] = $id;
}

public function hide($id = null)
{
$this->hidden[] = $id;
}

public function current(): Window
{
$this->ensureForceReturnWindowsProvided();

return $this->forcedWindowReturnValues[array_rand($this->forcedWindowReturnValues)];
}

/**
* @return array<int, Window>
*/
public function all(): array
{
$this->ensureForceReturnWindowsProvided();

return $this->forcedWindowReturnValues;
}

public function get(string $id): Window
{
$this->ensureForceReturnWindowsProvided();

$matchingWindows = array_filter($this->forcedWindowReturnValues, fn (Window $window) => $window->getId() === $id);

PHPUnit::assertNotEmpty($matchingWindows);
PHPUnit::assertCount(1, $matchingWindows);

return Arr::first($matchingWindows);
}

public function assertOpened(string $id): void
{
PHPUnit::assertContains($id, $this->opened);
}

public function assertClosed(?string $id): void
{
PHPUnit::assertContains($id, $this->closed);
}

public function assertHidden(?string $id): void
{
PHPUnit::assertContains($id, $this->hidden);
}

private function ensureForceReturnWindowsProvided(): void
{
PHPUnit::assertNotEmpty($this->forcedWindowReturnValues);
}
}
7 changes: 7 additions & 0 deletions src/NativeServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Native\Laravel;

use Illuminate\Console\Application;
use Illuminate\Foundation\Application as Foundation;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
Expand All @@ -12,9 +13,11 @@
use Native\Laravel\Commands\MigrateCommand;
use Native\Laravel\Commands\MinifyApplicationCommand;
use Native\Laravel\Commands\SeedDatabaseCommand;
use Native\Laravel\Contracts\WindowManager as WindowManagerContract;
use Native\Laravel\Events\EventWatcher;
use Native\Laravel\Exceptions\Handler;
use Native\Laravel\Logging\LogWatcher;
use Native\Laravel\Windows\WindowManager as WindowManagerImplementation;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;

Expand Down Expand Up @@ -47,6 +50,10 @@ public function packageRegistered()
return new MigrateCommand($app['migrator'], $app['events']);
});

$this->app->bind(WindowManagerContract::class, function (Foundation $app) {
return $app->make(WindowManagerImplementation::class);
});

if (config('nativephp-internal.running')) {
$this->app->singleton(
\Illuminate\Contracts\Debug\ExceptionHandler::class,
Expand Down
3 changes: 2 additions & 1 deletion src/Windows/WindowManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

use Native\Laravel\Client\Client;
use Native\Laravel\Concerns\DetectsWindowId;
use Native\Laravel\Contracts\WindowManager as WindowManagerContract;

class WindowManager
class WindowManager implements WindowManagerContract
{
use DetectsWindowId;

Expand Down
142 changes: 142 additions & 0 deletions tests/Fakes/FakeWindowManagerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

use Native\Laravel\Contracts\WindowManager as WindowManagerContract;
use Native\Laravel\Facades\Window;
use Native\Laravel\Fakes\WindowManagerFake;
use Native\Laravel\Windows\Window as WindowClass;
use PHPUnit\Framework\AssertionFailedError;

use function Pest\Laravel\swap;

it('swaps implementations using facade', function () {
Window::fake();

expect(app(WindowManagerContract::class))->toBeInstanceOf(WindowManagerFake::class);
});

it('asserts that a window was opened', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

app(WindowManagerContract::class)->open('main');
app(WindowManagerContract::class)->open('secondary');

$fake->assertOpened('main');
$fake->assertOpened('secondary');

try {
$fake->assertOpened('tertiary');
} catch (AssertionFailedError) {
expect(true)->toBeTrue();

return;
}

$this->fail('Expected assertion to fail');
});

it('asserts that a window was closed', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

app(WindowManagerContract::class)->close('main');
app(WindowManagerContract::class)->close('secondary');

$fake->assertClosed('main');
$fake->assertClosed('secondary');

try {
$fake->assertClosed('tertiary');
} catch (AssertionFailedError) {
expect(true)->toBeTrue();

return;
}

$this->fail('Expected assertion to fail');
});

it('asserts that a window was hidden', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

app(WindowManagerContract::class)->hide('main');
app(WindowManagerContract::class)->hide('secondary');

$fake->assertHidden('main');
$fake->assertHidden('secondary');

try {
$fake->assertHidden('tertiary');
} catch (AssertionFailedError) {
expect(true)->toBeTrue();

return;
}

$this->fail('Expected assertion to fail');
});

it('forces the return value of current window', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

$fake->alwaysReturnWindows($windows = [
new WindowClass('testA'),
new WindowClass('testB'),
]);

expect($windows)->toContain(app(WindowManagerContract::class)->current());
});

it('forces the return value of all windows', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

$fake->alwaysReturnWindows($windows = [
new WindowClass('testA'),
new WindowClass('testB'),
]);

expect(app(WindowManagerContract::class)->all())->toBe($windows);
});

it('forces the return value of a specific window', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

$fake->alwaysReturnWindows($windows = [
new WindowClass('testA'),
new WindowClass('testB'),
]);

expect(app(WindowManagerContract::class)->get('testA'))->toBe($windows[0]);
expect(app(WindowManagerContract::class)->get('testB'))->toBe($windows[1]);
});

test('that the get method throws an exception if multiple matching window ids exist', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

$fake->alwaysReturnWindows($windows = [
new WindowClass('testA'),
new WindowClass('testA'),
]);

app(WindowManagerContract::class)->get('testA');
})->throws(AssertionFailedError::class);

test('that the get method throws an exception if no matching window id exists', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

$fake->alwaysReturnWindows($windows = [
new WindowClass('testA'),
]);

app(WindowManagerContract::class)->get('testB');
})->throws(AssertionFailedError::class);

test('that the current method throws an exception if no forced window return values are provided', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

app(WindowManagerContract::class)->current();
})->throws(AssertionFailedError::class);

test('that the all method throws an exception if no forced window return values are provided', function () {
swap(WindowManagerContract::class, $fake = new WindowManagerFake);

app(WindowManagerContract::class)->all();
})->throws(AssertionFailedError::class);

0 comments on commit 3e0c562

Please sign in to comment.