From e1d5a55d4cbe29da1691df4de26f7e886862dfc6 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Sat, 18 Jul 2026 19:13:54 -0400 Subject: [PATCH 1/2] Add test suite, FakeBridge testing macros, and testing docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pest suite: Testbench TestCase + core provider, PluginTest (manifest/ native-code/composer validation), and a facade↔bridge contract test driven through the FakeBridge. - src/Testing/*Macros.php: plugin-specific FakeBridge with*/assert* vocabulary, auto-registered under test runs on a macroable core. - *MacrosTest covering the macros (pass + failure cases). - README "## Testing" section showing app-test usage. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 37 ++++++ src/NetworkServiceProvider.php | 11 ++ src/Testing/NetworkMacros.php | 94 ++++++++++++++ tests/NetworkBridgeTest.php | 223 +++++++++++++++++++++++++++++++++ tests/NetworkMacrosTest.php | 146 +++++++++++++++++++++ tests/Pest.php | 3 + tests/PluginTest.php | 126 +++++++++++++++++++ tests/TestCase.php | 32 +++++ 8 files changed, 672 insertions(+) create mode 100644 src/Testing/NetworkMacros.php create mode 100644 tests/NetworkBridgeTest.php create mode 100644 tests/NetworkMacrosTest.php create mode 100644 tests/Pest.php create mode 100644 tests/PluginTest.php create mode 100644 tests/TestCase.php diff --git a/README.md b/README.md index 8fdda2e..8f5868d 100644 --- a/README.md +++ b/README.md @@ -122,3 +122,40 @@ async function checkBeforeDownload() { - Uses `NWPathMonitor` from Network framework - `isConstrained` reflects Low Data Mode setting - No special permissions required + +## Testing + +The plugin extends the NativePHP testing suite with network-specific helpers, so your app tests can fake connectivity and assert it was checked without knowing any bridge internals: + +```php +use Native\Mobile\Testing\Native; + +it('syncs everything on wifi', function () { + Native::fakeBridge()->withWifi(); + + Native::test(SyncButton::class) + ->tap('Sync now') + ->assertNetworkChecked(); +}); + +it('warns instead of syncing when offline', function () { + Native::fakeBridge()->withOffline(); + + Native::test(SyncButton::class) + ->tap('Sync now') + ->assertSee('No connection'); +}); +``` + +### Helpers + +- `withNetworkStatus(array $status = [])` — fake the raw response to `status()`. Accepts any of `connected`, `type`, `isExpensive`, `isConstrained`, `error` — the same fields the native bridge returns. +- `withWifi(array $extra = [])` — fake a connected, unmetered, unconstrained Wi-Fi status. Pass `$extra` to override individual fields. +- `withCellular(array $extra = [])` — fake a connected, metered cellular status. +- `withOffline(array $extra = [])` — fake a disconnected status (`type: 'unknown'`). +- `withError(string $error = 'Unknown error', array $extra = [])` — fake the native error path (e.g. an Android catch reporting failure to read connectivity state). +- `assertNetworkChecked()` — assert `status()` was called. + +The helpers are available on `Native::fakeBridge()` and chain directly off `Native::test(...)`. They register automatically while running tests (requires a core with a macroable FakeBridge; on older cores they simply don't register). + +Note that `status()` decodes the bridge's JSON response without the `true` flag, so it returns a `stdClass` object (`$status->type`), not an array — the same is true of the objects returned while faked. diff --git a/src/NetworkServiceProvider.php b/src/NetworkServiceProvider.php index d7dffdd..1f5a26a 100644 --- a/src/NetworkServiceProvider.php +++ b/src/NetworkServiceProvider.php @@ -4,6 +4,8 @@ use Illuminate\Support\ServiceProvider; use Native\Mobile\Network; +use Native\Mobile\Providers\Testing\NetworkMacros; +use Native\Mobile\Testing\FakeBridge; class NetworkServiceProvider extends ServiceProvider { @@ -12,5 +14,14 @@ public function register(): void $this->app->singleton(Network::class, function () { return new Network; }); + + // Test sugar (withWifi(), assertNetworkChecked(), etc.) — only under + // a test runner, and only on a core whose FakeBridge is macroable + // (the method_exists guard keeps older v4 and v3 cores fatal-free). + if ($this->app->runningUnitTests() + && class_exists(FakeBridge::class) + && method_exists(FakeBridge::class, 'macro')) { + NetworkMacros::register(); + } } } diff --git a/src/Testing/NetworkMacros.php b/src/Testing/NetworkMacros.php new file mode 100644 index 0000000..8473b60 --- /dev/null +++ b/src/Testing/NetworkMacros.php @@ -0,0 +1,94 @@ +withWifi(); + * + * Native::test(SyncButton::class) + * ->tap('sync') + * ->assertNetworkChecked(); + * + * Network::status() decodes the raw JSON response with json_decode($result) + * (no `true` flag), so a scripted response comes back to the app as a + * stdClass, not an array — the shape mirrors the real bridge's fields: + * connected, type (wifi/cellular/ethernet/unknown/error), isExpensive, + * isConstrained, and error (only on the error path). + * + * Registered by NetworkServiceProvider when the app is running unit tests + * on a core whose FakeBridge supports macros. + */ +class NetworkMacros +{ + public static function register(): void + { + /** + * Fake the response to Network::status(). Pass the raw response + * shape — connected, type, isExpensive, isConstrained, error — or + * reach for one of the convenience helpers below for the common + * cases. + */ + FakeBridge::macro('withNetworkStatus', function (array $status = []) { + // status() decodes with json_decode() (objects, not arrays), and + // an empty PHP array would JSON-encode to "[]" — a list, decoding + // back to an array and tripping status()'s ?object return type. + // Emit a literal "{}" for the empty case so it stays an object. + return $this->respondTo('Network.Status', $status === [] ? '{}' : $status); + }); + + /** Fake a connected Wi-Fi status (unmetered, unconstrained). */ + FakeBridge::macro('withWifi', function (array $extra = []) { + return $this->withNetworkStatus(array_merge([ + 'connected' => true, + 'type' => 'wifi', + 'isExpensive' => false, + 'isConstrained' => false, + ], $extra)); + }); + + /** Fake a connected cellular status (metered by default). */ + FakeBridge::macro('withCellular', function (array $extra = []) { + return $this->withNetworkStatus(array_merge([ + 'connected' => true, + 'type' => 'cellular', + 'isExpensive' => true, + 'isConstrained' => false, + ], $extra)); + }); + + /** Fake a disconnected status — no network at all. */ + FakeBridge::macro('withOffline', function (array $extra = []) { + return $this->withNetworkStatus(array_merge([ + 'connected' => false, + 'type' => 'unknown', + 'isExpensive' => false, + 'isConstrained' => false, + ], $extra)); + }); + + /** + * Fake the native error path (e.g. an Android catch reporting + * failure to read connectivity state). + */ + FakeBridge::macro('withError', function (string $error = 'Unknown error', array $extra = []) { + return $this->withNetworkStatus(array_merge([ + 'connected' => false, + 'type' => 'error', + 'isExpensive' => false, + 'isConstrained' => false, + 'error' => $error, + ], $extra)); + }); + + /** Assert the network status was checked (status()). */ + FakeBridge::macro('assertNetworkChecked', function () { + return $this->assertCalled('Network.Status'); + }); + } +} diff --git a/tests/NetworkBridgeTest.php b/tests/NetworkBridgeTest.php new file mode 100644 index 0000000..5ac16f1 --- /dev/null +++ b/tests/NetworkBridgeTest.php @@ -0,0 +1,223 @@ +bridge = Native::fakeBridge(); +}); + +describe('status()', function () { + it('fires Network.Status with an empty payload', function () { + $this->bridge->respondTo('Network.Status', [ + 'connected' => true, + 'type' => 'wifi', + 'isExpensive' => false, + 'isConstrained' => false, + ]); + + (new Network)->status(); + + $this->bridge->assertCalled('Network.Status', function (array $p) { + expect($p)->toBe([]); + + return true; + }); + }); + + it('calls Network.Status exactly once per status() call', function () { + (new Network)->status(); + + $this->bridge->assertCalledTimes('Network.Status', 1); + }); + + it('fires a fresh bridge call on every invocation', function () { + (new Network)->status(); + (new Network)->status(); + (new Network)->status(); + + $this->bridge->assertCalledTimes('Network.Status', 3); + }); + + it('decodes a connected wifi status', function () { + $this->bridge->respondTo('Network.Status', [ + 'connected' => true, + 'type' => 'wifi', + 'isExpensive' => false, + 'isConstrained' => false, + ]); + + $status = (new Network)->status(); + + expect($status)->toBeObject(); + expect($status->connected)->toBeTrue(); + expect($status->type)->toBe('wifi'); + expect($status->isExpensive)->toBeFalse(); + expect($status->isConstrained)->toBeFalse(); + }); + + it('decodes a connected cellular status as expensive', function () { + $this->bridge->respondTo('Network.Status', [ + 'connected' => true, + 'type' => 'cellular', + 'isExpensive' => true, + 'isConstrained' => false, + ]); + + $status = (new Network)->status(); + + expect($status->connected)->toBeTrue(); + expect($status->type)->toBe('cellular'); + expect($status->isExpensive)->toBeTrue(); + }); + + it('decodes a connected ethernet status', function () { + $this->bridge->respondTo('Network.Status', [ + 'connected' => true, + 'type' => 'ethernet', + 'isExpensive' => false, + 'isConstrained' => false, + ]); + + $status = (new Network)->status(); + + expect($status->connected)->toBeTrue(); + expect($status->type)->toBe('ethernet'); + }); + + it('decodes a disconnected status with unknown type', function () { + $this->bridge->respondTo('Network.Status', [ + 'connected' => false, + 'type' => 'unknown', + 'isExpensive' => false, + 'isConstrained' => false, + ]); + + $status = (new Network)->status(); + + expect($status->connected)->toBeFalse(); + expect($status->type)->toBe('unknown'); + }); + + it('decodes isConstrained when Low Data Mode is enabled (iOS)', function () { + $this->bridge->respondTo('Network.Status', [ + 'connected' => true, + 'type' => 'cellular', + 'isExpensive' => true, + 'isConstrained' => true, + ]); + + $status = (new Network)->status(); + + expect($status->isConstrained)->toBeTrue(); + }); + + it('decodes an error response reported by native (Android catch path)', function () { + $this->bridge->respondTo('Network.Status', [ + 'connected' => false, + 'type' => 'error', + 'isExpensive' => false, + 'isConstrained' => false, + 'error' => 'Unknown error', + ]); + + $status = (new Network)->status(); + + expect($status)->not->toBeNull(); + expect($status->connected)->toBeFalse(); + expect($status->type)->toBe('error'); + expect($status->error)->toBe('Unknown error'); + }); + + it('exposes only the properties present in a partial response', function () { + // e.g. a stubbed/older native build that only reports connectivity. + $this->bridge->respondTo('Network.Status', '{"connected":true}'); + + $status = (new Network)->status(); + + expect($status)->toBeObject(); + expect($status->connected)->toBeTrue(); + expect(property_exists($status, 'type'))->toBeFalse(); + expect(property_exists($status, 'isExpensive'))->toBeFalse(); + expect(property_exists($status, 'isConstrained'))->toBeFalse(); + }); + + it('returns an object with no properties for an empty JSON object response', function () { + $this->bridge->respondTo('Network.Status', '{}'); + + $status = (new Network)->status(); + + expect($status)->toBeObject(); + expect(get_object_vars($status))->toBe([]); + }); + + it('returns null when nothing is scripted for the bridge call', function () { + $status = (new Network)->status(); + + $this->bridge->assertCalled('Network.Status'); + expect($status)->toBeNull(); + }); + + it('returns null when the bridge responds with an empty string', function () { + $this->bridge->respondTo('Network.Status', ''); + + expect((new Network)->status())->toBeNull(); + }); + + it('returns null when the bridge responds with the string "0"', function () { + // PHP's `if ($result)` treats the string "0" as falsy, so the + // facade short-circuits before ever calling json_decode(). + $this->bridge->respondTo('Network.Status', '0'); + + expect((new Network)->status())->toBeNull(); + }); + + it('returns null when the bridge responds with literal JSON null', function () { + $this->bridge->respondTo('Network.Status', 'null'); + + expect((new Network)->status())->toBeNull(); + }); + + it('returns null when the bridge responds with malformed JSON', function () { + $this->bridge->respondTo('Network.Status', '{not valid json'); + + expect((new Network)->status())->toBeNull(); + }); + + it('resolves the response dynamically via a closure', function () { + $this->bridge->respondTo('Network.Status', function (array $params) { + expect($params)->toBe([]); + + return ['connected' => true, 'type' => 'wifi', 'isExpensive' => false, 'isConstrained' => false]; + }); + + $status = (new Network)->status(); + + expect($status->connected)->toBeTrue(); + expect($status->type)->toBe('wifi'); + }); + + it('does not fire any other bridge method', function () { + (new Network)->status(); + + $this->bridge->assertNotCalled('Network.WriteText'); + $this->bridge->assertNotCalled('Network.Configure'); + }); +}); diff --git a/tests/NetworkMacrosTest.php b/tests/NetworkMacrosTest.php new file mode 100644 index 0000000..83737ca --- /dev/null +++ b/tests/NetworkMacrosTest.php @@ -0,0 +1,146 @@ +markTestSkipped('This core\'s FakeBridge does not support macros.'); + } + + $this->bridge = Native::fakeBridge(); +}); + +describe('withNetworkStatus()', function () { + it('scripts a raw response that decodes through status()', function () { + $this->bridge->withNetworkStatus([ + 'connected' => true, + 'type' => 'ethernet', + 'isExpensive' => false, + 'isConstrained' => false, + ]); + + $status = (new Network)->status(); + + expect($status)->toBeObject(); + expect($status->connected)->toBeTrue(); + expect($status->type)->toBe('ethernet'); + expect($status->isExpensive)->toBeFalse(); + expect($status->isConstrained)->toBeFalse(); + }); + + it('defaults to an empty scripted response', function () { + $this->bridge->withNetworkStatus(); + + $status = (new Network)->status(); + + expect($status)->toBeObject(); + expect(get_object_vars($status))->toBe([]); + }); +}); + +describe('withWifi()', function () { + it('fakes a connected, unmetered, unconstrained wifi status', function () { + $this->bridge->withWifi(); + + $status = (new Network)->status(); + + expect($status->connected)->toBeTrue(); + expect($status->type)->toBe('wifi'); + expect($status->isExpensive)->toBeFalse(); + expect($status->isConstrained)->toBeFalse(); + }); + + it('allows overriding fields', function () { + $this->bridge->withWifi(['isConstrained' => true]); + + $status = (new Network)->status(); + + expect($status->type)->toBe('wifi'); + expect($status->isConstrained)->toBeTrue(); + }); +}); + +describe('withCellular()', function () { + it('fakes a connected, metered cellular status', function () { + $this->bridge->withCellular(); + + $status = (new Network)->status(); + + expect($status->connected)->toBeTrue(); + expect($status->type)->toBe('cellular'); + expect($status->isExpensive)->toBeTrue(); + expect($status->isConstrained)->toBeFalse(); + }); + + it('allows overriding fields', function () { + $this->bridge->withCellular(['isConstrained' => true]); + + $status = (new Network)->status(); + + expect($status->isExpensive)->toBeTrue(); + expect($status->isConstrained)->toBeTrue(); + }); +}); + +describe('withOffline()', function () { + it('fakes a disconnected status with unknown type', function () { + $this->bridge->withOffline(); + + $status = (new Network)->status(); + + expect($status->connected)->toBeFalse(); + expect($status->type)->toBe('unknown'); + expect($status->isExpensive)->toBeFalse(); + expect($status->isConstrained)->toBeFalse(); + }); +}); + +describe('withError()', function () { + it('fakes the native error path with a default message', function () { + $this->bridge->withError(); + + $status = (new Network)->status(); + + expect($status->connected)->toBeFalse(); + expect($status->type)->toBe('error'); + expect($status->error)->toBe('Unknown error'); + }); + + it('accepts a custom error message', function () { + $this->bridge->withError('permission denied'); + + $status = (new Network)->status(); + + expect($status->type)->toBe('error'); + expect($status->error)->toBe('permission denied'); + }); +}); + +describe('assertNetworkChecked()', function () { + it('passes after status() was called', function () { + $this->bridge->withWifi(); + + (new Network)->status(); + + $this->bridge->assertNetworkChecked(); + }); + + it('fails when status() was never called', function () { + expect(fn () => $this->bridge->assertNetworkChecked()) + ->toThrow(AssertionFailedError::class); + }); +}); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..88a51b7 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,3 @@ +in('.'); diff --git a/tests/PluginTest.php b/tests/PluginTest.php new file mode 100644 index 0000000..3bfd447 --- /dev/null +++ b/tests/PluginTest.php @@ -0,0 +1,126 @@ +pluginPath = getenv('PLUGIN_PATH') ?: dirname(__DIR__); + $this->manifestPath = $this->pluginPath.'/nativephp.json'; +}); + +describe('Plugin Manifest', function () { + it('has a valid nativephp.json file', function () { + expect(file_exists($this->manifestPath))->toBeTrue(); + + json_decode(file_get_contents($this->manifestPath), true); + + expect(json_last_error())->toBe(JSON_ERROR_NONE); + }); + + it('has required fields', function () { + $manifest = json_decode(file_get_contents($this->manifestPath), true); + + expect($manifest)->toHaveKeys(['namespace', 'bridge_functions']); + expect($manifest['namespace'])->toBe('Network'); + }); + + it('declares every bridge function for both platforms', function () { + $manifest = json_decode(file_get_contents($this->manifestPath), true); + + expect($manifest['bridge_functions'])->toBeArray()->not->toBeEmpty(); + + $names = array_column($manifest['bridge_functions'], 'name'); + expect($names)->toContain('Network.Status'); + + foreach ($manifest['bridge_functions'] as $function) { + expect($function)->toHaveKeys(['name']); + expect(isset($function['android']) || isset($function['ios']))->toBeTrue(); + } + }); + + it('declares no events', function () { + $manifest = json_decode(file_get_contents($this->manifestPath), true); + + expect($manifest['events'])->toBeArray()->toBeEmpty(); + }); + + it('requests the ACCESS_NETWORK_STATE android permission', function () { + $manifest = json_decode(file_get_contents($this->manifestPath), true); + + expect($manifest['android']['permissions']) + ->toBeArray() + ->toContain('android.permission.ACCESS_NETWORK_STATE'); + }); + + it('requests no iOS Info.plist entries', function () { + $manifest = json_decode(file_get_contents($this->manifestPath), true); + + expect($manifest['ios']['info_plist'])->toBeArray()->toBeEmpty(); + }); +}); + +describe('Native Code', function () { + it('has matching bridge function classes in native code', function () { + $manifest = json_decode(file_get_contents($this->manifestPath), true); + + $kotlinContent = implode('', array_map('file_get_contents', glob($this->pluginPath.'/resources/android/*.kt'))); + $swiftContent = implode('', array_map('file_get_contents', glob($this->pluginPath.'/resources/ios/*.swift'))); + + foreach ($manifest['bridge_functions'] as $function) { + if (isset($function['android'])) { + $parts = explode('.', $function['android']); + expect($kotlinContent)->toContain('class '.end($parts)); + } + + if (isset($function['ios'])) { + $parts = explode('.', $function['ios']); + expect($swiftContent)->toContain('class '.end($parts)); + } + } + }); +}); + +describe('Composer Configuration', function () { + it('has valid composer.json', function () { + $composer = json_decode(file_get_contents($this->pluginPath.'/composer.json'), true); + + expect(json_last_error())->toBe(JSON_ERROR_NONE); + expect($composer['type'])->toBe('nativephp-plugin'); + expect($composer['require'])->toHaveKey('php'); + expect($composer['require']['php'])->not->toBeEmpty(); + expect($composer['require'])->toHaveKey('nativephp/mobile'); + }); + + it('registers a provider that maps to an existing file', function () { + $composer = json_decode(file_get_contents($this->pluginPath.'/composer.json'), true); + + $providers = $composer['extra']['laravel']['providers'] ?? []; + expect($providers)->not->toBeEmpty(); + + foreach ($providers as $provider) { + $matched = false; + + foreach ($composer['autoload']['psr-4'] as $prefix => $path) { + if (! str_starts_with($provider, $prefix)) { + continue; + } + + $relative = str_replace('\\', '/', substr($provider, strlen($prefix))); + $file = $this->pluginPath.'/'.rtrim($path, '/').'/'.$relative.'.php'; + + if (file_exists($file)) { + $matched = true; + break; + } + } + + expect($matched)->toBeTrue("Provider {$provider} does not map to a file"); + } + }); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..0b4b42a --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,32 @@ +set('nativephp.app_id', 'com.test.app'); + $app['config']->set('nativephp.version', '1.0.0'); + $app['config']->set('nativephp.version_code', 1); + $app['config']->set('app.name', 'Test App'); + } +} From 3f3bfb260c4078b7950d98ef06b2534f627a7ab6 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Sat, 18 Jul 2026 21:56:38 -0400 Subject: [PATCH 2/2] Add CI (Pest + Pint), phpunit.xml, and apply pint fixes - GitHub Actions: Pest job (resolves the v4 core as dev-main from the public mobile-air repo) + a standalone Pint job. - phpunit.xml so PHPUnit has a config + cache directory on CI. - Apply pint fixes to the generated test files. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/tests.yml | 63 +++++++++++++++++++++++++++++++++++++ phpunit.xml | 13 ++++++++ tests/NetworkBridgeTest.php | 3 +- tests/NetworkMacrosTest.php | 3 +- tests/PluginTest.php | 1 - 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/tests.yml create mode 100644 phpunit.xml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..f1ec123 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,63 @@ +name: tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + pest: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + tools: composer:v2 + + # nativephp/mobile v4 is not a stable Packagist release yet, and this + # suite needs its FakeBridge testing classes. Resolve the core from the + # public mobile-air repo's main branch (dev-main) — the same override + # test-plugins.sh applies locally. The default GITHUB_TOKEN is used only + # to avoid GitHub API rate limits while reading the public repo. + - name: Point Composer at the v4 core (dev-main) + run: | + composer config repositories.nativephp-mobile vcs https://github.com/nativephp/mobile-air + composer config minimum-stability dev + composer config prefer-stable true + composer config github-oauth.github.com "${{ secrets.GITHUB_TOKEN }}" + composer require "nativephp/mobile:dev-main" --no-update --no-interaction + + - name: Install dependencies + run: composer update --prefer-dist --no-interaction --no-progress + + - name: Run Pest + run: vendor/bin/pest + + pint: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + # Pint is a standalone formatter — it needs no project vendor/, so this + # job skips composer entirely. + - name: Setup PHP with Pint + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + tools: pint + + - name: Check code style + run: pint --test diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..13ad511 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,13 @@ + + + + + tests + + + diff --git a/tests/NetworkBridgeTest.php b/tests/NetworkBridgeTest.php index 5ac16f1..4073221 100644 --- a/tests/NetworkBridgeTest.php +++ b/tests/NetworkBridgeTest.php @@ -17,8 +17,9 @@ use Native\Mobile\Network; use Native\Mobile\Testing\Native; +use Tests\TestCase; -uses(Tests\TestCase::class); +uses(TestCase::class); beforeEach(function () { $this->bridge = Native::fakeBridge(); diff --git a/tests/NetworkMacrosTest.php b/tests/NetworkMacrosTest.php index 83737ca..9bee47b 100644 --- a/tests/NetworkMacrosTest.php +++ b/tests/NetworkMacrosTest.php @@ -13,8 +13,9 @@ use Native\Mobile\Testing\FakeBridge; use Native\Mobile\Testing\Native; use PHPUnit\Framework\AssertionFailedError; +use Tests\TestCase; -uses(Tests\TestCase::class); +uses(TestCase::class); beforeEach(function () { if (! method_exists(FakeBridge::class, 'macro')) { diff --git a/tests/PluginTest.php b/tests/PluginTest.php index 3bfd447..292cbe8 100644 --- a/tests/PluginTest.php +++ b/tests/PluginTest.php @@ -5,7 +5,6 @@ * * Run with: ./vendor/bin/pest */ - beforeEach(function () { // PLUGIN_PATH lets CI run this suite from an external Pest harness // (the plugin's own composer deps aren't resolvable on public