'127.0.0.1', 'MYSQL_PORT' => (string) (getenv('MYSQL_TUNNEL_LOCAL_PORT') ?: '13306'), 'MYSQL_USER' => Env::require('REMOTE_MYSQL_USER'), 'MYSQL_PASSWORD' => Env::require('REMOTE_MYSQL_PASSWORD'), 'MAILSHOTS_REMOTE_MYSQL_DB' => Env::require('MAILSHOTS_REMOTE_MYSQL_DB'), 'MEMBERS_REMOTE_MYSQL_DB' => Env::require('MEMBERS_REMOTE_MYSQL_DB'), ]; $wp = new FixtureWordPressFacade(); $container = Plugin::buildContainer($dbConfig, $wp); /** @param callable():void $fn */ $capture = static function (callable $fn): string { ob_start(); try { $fn(); return (string) ob_get_clean(); } catch (\Throwable $e) { ob_end_clean(); throw $e; } }; /** @param string $needle */ $assertContains = static function (string $needle, string $haystack, string $label): void { if (strpos($haystack, $needle) === false) { fwrite(STDERR, "Missing expected text in {$label}: {$needle}\n"); exit(1); } }; /** @param string $needle */ $assertNotContains = static function (string $needle, string $haystack, string $label): void { if (strpos($haystack, $needle) !== false) { fwrite(STDERR, "Unexpected text in {$label}: {$needle}\n"); exit(1); } }; $mailshotsHtml = $capture(static function () use ($container): void { $container->get(FecaMailshots\Admin\MailshotsAdminPage::class)->render(); }); $assertContains('New Mailshot', $mailshotsHtml, 'Mailshots'); $assertContains('feca_mailshots_mailshots_ui_save', $mailshotsHtml, 'Mailshots'); $assertContains('Existing Mailshots', $mailshotsHtml, 'Mailshots'); $assertNotContains('Use API endpoint', $mailshotsHtml, 'Mailshots'); $dataSourcesHtml = $capture(static function () use ($container): void { $container->get(FecaMailshots\Admin\DataSourcesAdminPage::class)->render(); }); $assertContains('New Data Source', $dataSourcesHtml, 'Data Sources'); $assertContains('feca_mailshots_data_sources_ui_save', $dataSourcesHtml, 'Data Sources'); $assertContains('Existing Data Sources', $dataSourcesHtml, 'Data Sources'); $assertNotContains('Use endpoint', $dataSourcesHtml, 'Data Sources'); $attachmentsHtml = $capture(static function () use ($container): void { $container->get(FecaMailshots\Admin\AttachmentsAdminPage::class)->render(); }); $assertContains('Create Attachment', $attachmentsHtml, 'Attachments'); $assertContains('feca_mailshots_attachments_ui_save', $attachmentsHtml, 'Attachments'); $assertContains('Existing Attachments', $attachmentsHtml, 'Attachments'); $assertNotContains('Use API endpoint', $attachmentsHtml, 'Attachments'); $assertNotContains('id="att_mime"', $attachmentsHtml, 'Attachments'); $pdfAssetsHtml = $capture(static function () use ($container): void { $container->get(FecaMailshots\Admin\PdfAssetsAdminPage::class)->render(); }); $assertContains('Create PDF Asset', $pdfAssetsHtml, 'PDF Assets'); $assertContains('feca_mailshots_pdf_assets_ui_save', $pdfAssetsHtml, 'PDF Assets'); $assertContains('Existing PDF Assets', $pdfAssetsHtml, 'PDF Assets'); $assertNotContains('Use API endpoint', $pdfAssetsHtml, 'PDF Assets'); $assertNotContains('id="pdf_mime"', $pdfAssetsHtml, 'PDF Assets'); $reviewRecipientsHtml = $capture(static function () use ($container): void { $container->get(FecaMailshots\Admin\ReviewRecipientsAdminPage::class)->render(); }); $assertContains('Review Recipients', $reviewRecipientsHtml, 'Review Recipients'); $assertContains('Sort Direction', $reviewRecipientsHtml, 'Review Recipients'); $assertNotContains('>Apply<', $reviewRecipientsHtml, 'Review Recipients'); $downloadPdfHtml = $capture(static function () use ($container): void { $container->get(FecaMailshots\Admin\DownloadPdfAdminPage::class)->render(); }); $assertContains('Download PDF', $downloadPdfHtml, 'Download PDF'); $assertContains('Download Merged PDF', $downloadPdfHtml, 'Download PDF'); echo "Phase 3 admin CRUD UI integration test passed\n";