in('Feature', 'v4/Feature', 'v4/Browser'); /* |-------------------------------------------------------------------------- | Shared Helpers |-------------------------------------------------------------------------- | | Helper functions shared across multiple test files. | */ require_once __DIR__.'/Support/BrowserTestHelpers.php'; require_once __DIR__.'/Support/ManagedDnsTestHelpers.php'; require_once __DIR__.'/Support/StripeTestHelpers.php'; function remoteOutputSource(string $path): string { $fixturePath = dirname(__DIR__).'/'.$path; if (! is_readable($fixturePath)) { throw new RuntimeException("Unable to read source fixture: {$fixturePath}"); } $source = file_get_contents($fixturePath); if ($source === false) { throw new RuntimeException("Unable to read source fixture: {$fixturePath}"); } return $source; } /** * Trigger the deferred mount of a #[Lazy] Livewire component the way the browser would * via its x-intersect `__lazyLoad(...)` call, so assertions can run against the real * (post-mount) render instead of the placeholder. */ function loadLazy(Testable $component): Testable { preg_match('/__lazyLoad\('([^&]+)'\)/', $component->html(), $matches); if (empty($matches)) { // No trigger means the component isn't lazy (or the placeholder markup changed). // Fail loudly rather than silently asserting against the un-mounted placeholder, // which would turn a lazy-load regression into a false-positive pass. throw new RuntimeException('loadLazy: no __lazyLoad trigger found — component is not #[Lazy] or its placeholder markup changed.'); } return $component->call('__lazyLoad', $matches[1]); } /* |-------------------------------------------------------------------------- | Test Hooks |-------------------------------------------------------------------------- | | Global hooks that run before/after each test. A bare beforeEach() in this file | only applies to tests in this file, so register them with uses()->in(). | */ uses()->beforeEach(function () { // Flush the Once memoization cache to ensure tests get fresh data Once::flush(); // Flush the Server identity map cache to ensure tests get fresh data Server::flushIdentityMap(); })->in('Feature', 'Unit', 'v4'); uses()->beforeEach(function () { // Browser Livewire actions often dispatch events; disable broadcasting for host-side runs. config(['broadcasting.default' => 'null']); })->in('v4/Browser'); function loginAndSkipBoarding(string $email = 'test@example.com', string $password = 'password'): mixed { $page = visit('/login') ->fill('email', $email) ->fill('password', $password) ->click('Login') ->wait(1.5); // First-login root users land on onboarding; skip when the control exists. $page->script(<<<'JS' (() => { const candidates = Array.from(document.querySelectorAll('button, a, [role="button"]')); const skip = candidates.find((el) => (el.textContent || '').trim().toLowerCase() === 'skip setup'); if (skip) { skip.click(); } })() JS); $page->wait(1.5); return $page; } /* |-------------------------------------------------------------------------- | Expectations |-------------------------------------------------------------------------- | | When you're writing tests, you often need to check that values meet certain conditions. The | "expect()" function gives you access to a set of "expectations" methods that you can use | to assert different things. Of course, you may extend the Expectation API at any time. | */ // expect()->extend('toBeOne', function () { // return $this->toBe(1); // }); /* |-------------------------------------------------------------------------- | Functions |-------------------------------------------------------------------------- | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your | project that you don't want to repeat in every file. Here you can also expose helpers as | global functions to help you to reduce the number of lines of code in your test files. | */ // function something() // { // // .. // } /** * Fakes all processes and keeps the content of every file that Server::uploadProxyFile() * copies with scp, keyed by the remote upload path. */ function fakeProxyFileUploads(): void { $GLOBALS['proxyFileUploads'] = []; Process::fake(function (PendingProcess $process) { if (preg_match("#'(/[^']*coolify-proxy-file-[^']+)'.*'(/tmp/coolify-upload-[0-9a-f]+)'#", (string) $process->command, $matches)) { $GLOBALS['proxyFileUploads'][$matches[2]] = file_get_contents($matches[1]); } return Process::result(); }); } /** * The last content written to $file on the server, with Server::uploadProxyFile() or with * `echo '' | base64 -d | tee $file`. * * @param array $lines remote script lines (see Process::assertRan) */ function writtenProxyFile(array $lines, string $file): ?string { $content = null; foreach ($lines as $line) { if (preg_match('#mv -f (/tmp/coolify-upload-[0-9a-f]+) '.preg_quote($file, '#').'$#', $line, $matches)) { $content = $GLOBALS['proxyFileUploads'][$matches[1]] ?? null; } elseif (str_contains($line, "tee $file") && preg_match("/echo '([A-Za-z0-9+\/=]+)'/", $line, $matches)) { $content = base64_decode($matches[1]); } } return $content; }