diff --git a/src/Facades/Window.php b/src/Facades/Window.php index e774755..f73e216 100644 --- a/src/Facades/Window.php +++ b/src/Facades/Window.php @@ -22,7 +22,7 @@ class Window extends Facade { public static function fake() { - return tap(new WindowManagerFake, function ($fake) { + return tap(static::getFacadeApplication()->make(WindowManagerFake::class), function ($fake) { static::swap($fake); }); } diff --git a/src/Fakes/WindowManagerFake.php b/src/Fakes/WindowManagerFake.php index c3cf791..8604224 100644 --- a/src/Fakes/WindowManagerFake.php +++ b/src/Fakes/WindowManagerFake.php @@ -2,10 +2,13 @@ namespace Native\Laravel\Fakes; +use Closure; use Illuminate\Support\Arr; +use Native\Laravel\Client\Client; use Native\Laravel\Contracts\WindowManager as WindowManagerContract; use Native\Laravel\Windows\Window; use PHPUnit\Framework\Assert as PHPUnit; +use Webmozart\Assert\Assert; class WindowManagerFake implements WindowManagerContract { @@ -17,6 +20,10 @@ class WindowManagerFake implements WindowManagerContract public array $forcedWindowReturnValues = []; + public function __construct( + protected Client $client + ) {} + /** * @param array $windows */ @@ -30,6 +37,21 @@ public function alwaysReturnWindows(array $windows): self public function open(string $id = 'main') { $this->opened[] = $id; + + $this->ensureForceReturnWindowsProvided(); + + $matchingWindows = array_filter( + $this->forcedWindowReturnValues, + fn (Window $window) => $window->getId() === $id + ); + + if (empty($matchingWindows)) { + return $this->forcedWindowReturnValues[array_rand($this->forcedWindowReturnValues)]->setClient($this->client); + } + + Assert::count($matchingWindows, 1); + + return Arr::first($matchingWindows)->setClient($this->client); } public function close($id = null) @@ -65,29 +87,92 @@ public function get(string $id): Window $matchingWindows = array_filter($this->forcedWindowReturnValues, fn (Window $window) => $window->getId() === $id); - PHPUnit::assertNotEmpty($matchingWindows); - PHPUnit::assertCount(1, $matchingWindows); + Assert::notEmpty($matchingWindows); + Assert::count($matchingWindows, 1); return Arr::first($matchingWindows); } - public function assertOpened(string $id): void + /** + * @param string|Closure(string): bool $id + */ + public function assertOpened(string|Closure $id): void + { + if (is_callable($id) === false) { + PHPUnit::assertContains($id, $this->opened); + + return; + } + + $hit = empty( + array_filter( + $this->opened, + fn (string $openedId) => $id($openedId) === true + ) + ) === false; + + PHPUnit::assertTrue($hit); + } + + /** + * @param string|Closure(string): bool $id + */ + public function assertClosed(string|Closure $id): void + { + if (is_callable($id) === false) { + PHPUnit::assertContains($id, $this->closed); + + return; + } + + $hit = empty( + array_filter( + $this->closed, + fn (mixed $closedId) => $id($closedId) === true + ) + ) === false; + + PHPUnit::assertTrue($hit); + } + + /** + * @param string|Closure(string): bool $id + */ + public function assertHidden(string|Closure $id): void + { + if (is_callable($id) === false) { + PHPUnit::assertContains($id, $this->hidden); + + return; + } + + $hit = empty( + array_filter( + $this->hidden, + fn (mixed $hiddenId) => $id($hiddenId) === true + ) + ) === false; + + PHPUnit::assertTrue($hit); + } + + public function assertOpenedCount(int $expected): void { - PHPUnit::assertContains($id, $this->opened); + PHPUnit::assertCount($expected, $this->opened); } - public function assertClosed(?string $id): void + public function assertClosedCount(int $expected): void { - PHPUnit::assertContains($id, $this->closed); + PHPUnit::assertCount($expected, $this->closed); } - public function assertHidden(?string $id): void + public function assertHiddenCount(int $expected): void { - PHPUnit::assertContains($id, $this->hidden); + PHPUnit::assertCount($expected, $this->hidden); } private function ensureForceReturnWindowsProvided(): void { - PHPUnit::assertNotEmpty($this->forcedWindowReturnValues); + Assert::notEmpty($this->forcedWindowReturnValues, 'No windows were provided to return'); } } diff --git a/tests/Fakes/FakeWindowManagerTest.php b/tests/Fakes/FakeWindowManagerTest.php index 5f0fbb1..b2f4af2 100644 --- a/tests/Fakes/FakeWindowManagerTest.php +++ b/tests/Fakes/FakeWindowManagerTest.php @@ -1,10 +1,13 @@ Http::response(status: 200)]); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + $fake->alwaysReturnWindows([ + new PendingOpenWindow('doesnt-matter'), + ]); app(WindowManagerContract::class)->open('main'); app(WindowManagerContract::class)->open('secondary'); @@ -26,8 +34,30 @@ try { $fake->assertOpened('tertiary'); } catch (AssertionFailedError) { - expect(true)->toBeTrue(); + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts that a window was opened using callable', function () { + Http::fake(['*' => Http::response(status: 200)]); + + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + $fake->alwaysReturnWindows([ + new PendingOpenWindow('doesnt-matter'), + ]); + + app(WindowManagerContract::class)->open('main'); + app(WindowManagerContract::class)->open('secondary'); + $fake->assertOpened(fn (string $id) => $id === 'main'); + $fake->assertOpened(fn (string $id) => $id === 'secondary'); + + try { + $fake->assertOpened(fn (string $id) => $id === 'tertiary'); + } catch (AssertionFailedError) { return; } @@ -35,7 +65,7 @@ }); it('asserts that a window was closed', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); app(WindowManagerContract::class)->close('main'); app(WindowManagerContract::class)->close('secondary'); @@ -46,8 +76,24 @@ try { $fake->assertClosed('tertiary'); } catch (AssertionFailedError) { - expect(true)->toBeTrue(); + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts that a window was closed using callable', function () { + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + app(WindowManagerContract::class)->close('main'); + app(WindowManagerContract::class)->close('secondary'); + + $fake->assertClosed(fn (string $id) => $id === 'main'); + $fake->assertClosed(fn (string $id) => $id === 'secondary'); + + try { + $fake->assertClosed(fn (string $id) => $id === 'tertiary'); + } catch (AssertionFailedError) { return; } @@ -55,7 +101,7 @@ }); it('asserts that a window was hidden', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); app(WindowManagerContract::class)->hide('main'); app(WindowManagerContract::class)->hide('secondary'); @@ -66,8 +112,84 @@ try { $fake->assertHidden('tertiary'); } catch (AssertionFailedError) { - expect(true)->toBeTrue(); + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts that a window was hidden using callable', function () { + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + app(WindowManagerContract::class)->hide('main'); + app(WindowManagerContract::class)->hide('secondary'); + + $fake->assertHidden(fn (string $id) => $id === 'main'); + $fake->assertHidden(fn (string $id) => $id === 'secondary'); + + try { + $fake->assertHidden(fn (string $id) => $id === 'tertiary'); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts opened count', function () { + Http::fake(['*' => Http::response(status: 200)]); + + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + $fake->alwaysReturnWindows([ + new PendingOpenWindow('doesnt-matter'), + ]); + + app(WindowManagerContract::class)->open('main'); + app(WindowManagerContract::class)->open(); + app(WindowManagerContract::class)->open(); + + $fake->assertOpenedCount(3); + + try { + $fake->assertOpenedCount(4); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts closed count', function () { + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + app(WindowManagerContract::class)->close('main'); + app(WindowManagerContract::class)->close(); + app(WindowManagerContract::class)->close(); + + $fake->assertClosedCount(3); + + try { + $fake->assertClosedCount(4); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts hidden count', function () { + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + app(WindowManagerContract::class)->hide('main'); + app(WindowManagerContract::class)->hide(); + app(WindowManagerContract::class)->hide(); + + $fake->assertHiddenCount(3); + + try { + $fake->assertHiddenCount(4); + } catch (AssertionFailedError) { return; } @@ -75,7 +197,7 @@ }); it('forces the return value of current window', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); $fake->alwaysReturnWindows($windows = [ new WindowClass('testA'), @@ -86,7 +208,7 @@ }); it('forces the return value of all windows', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); $fake->alwaysReturnWindows($windows = [ new WindowClass('testA'), @@ -97,7 +219,7 @@ }); it('forces the return value of a specific window', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); $fake->alwaysReturnWindows($windows = [ new WindowClass('testA'), @@ -109,7 +231,7 @@ }); test('that the get method throws an exception if multiple matching window ids exist', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); $fake->alwaysReturnWindows($windows = [ new WindowClass('testA'), @@ -117,26 +239,79 @@ ]); app(WindowManagerContract::class)->get('testA'); -})->throws(AssertionFailedError::class); +})->throws(InvalidArgumentException::class); test('that the get method throws an exception if no matching window id exists', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); $fake->alwaysReturnWindows($windows = [ new WindowClass('testA'), ]); app(WindowManagerContract::class)->get('testB'); -})->throws(AssertionFailedError::class); +})->throws(InvalidArgumentException::class); test('that the current method throws an exception if no forced window return values are provided', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); app(WindowManagerContract::class)->current(); -})->throws(AssertionFailedError::class); +})->throws(InvalidArgumentException::class); test('that the all method throws an exception if no forced window return values are provided', function () { - swap(WindowManagerContract::class, $fake = new WindowManagerFake); + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); app(WindowManagerContract::class)->all(); -})->throws(AssertionFailedError::class); +})->throws(InvalidArgumentException::class); + +test('that the open method throws an exception if no forced window return values are provided', function () { + Http::fake([ + '*' => Http::response(status: 200), + ]); + + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + app(WindowManagerContract::class)->open('test'); +})->throws(InvalidArgumentException::class); + +test('that the open method throws an exception if multiple matching window ids exist', function () { + Http::fake([ + '*' => Http::response(status: 200), + ]); + + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + $fake->alwaysReturnWindows($windows = [ + new WindowClass('testA'), + new WindowClass('testA'), + ]); + + app(WindowManagerContract::class)->open('testA'); +})->throws(InvalidArgumentException::class); + +test('that the open method returns a random window if none match the id provided', function () { + Http::fake([ + '*' => Http::response(status: 200), + ]); + + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + $fake->alwaysReturnWindows($windows = [ + new PendingOpenWindow('testA'), + ]); + + expect($windows)->toContain(app(WindowManagerContract::class)->open('testC')); +}); + +test('that the open method returns a window if a matching window id exists', function () { + Http::fake([ + '*' => Http::response(status: 200), + ]); + + swap(WindowManagerContract::class, $fake = app(WindowManagerFake::class)); + + $fake->alwaysReturnWindows($windows = [ + new PendingOpenWindow('testA'), + ]); + + expect(app(WindowManagerContract::class)->open('testA'))->toBe($windows[0]); +});