'; } function checked( $expected, $actual ) { if ( $expected === $actual ) echo 'checked="checked"'; } function submit_button() { echo ''; } function date_i18n( $format, $timestamp ) { return gmdate( $format, $timestamp ); } function number_format_i18n( $number ) { return number_format( $number ); } require_once __DIR__ . '/../includes/class-plugin.php'; require_once __DIR__ . '/../includes/class-status.php'; require_once __DIR__ . '/../includes/class-connect.php'; require_once __DIR__ . '/../includes/class-settings.php'; } namespace Aisthetix\TryOn { class SettingsTestStatus extends Status { public $payload = array(); public $get_calls = 0; public $refresh_calls = 0; public function get( $force = false ) { ++$this->get_calls; return $this->payload; } public function refresh() { ++$this->refresh_calls; return $this->payload; } } class SettingsTestConnect extends Connect { public function start_url() { return 'https://woo.aisthetix.fyi/connect'; } public function disconnect_url() { return 'https://merchant.example/disconnect'; } } } namespace { use Aisthetix\TryOn\Connect; use Aisthetix\TryOn\Plugin; use Aisthetix\TryOn\Settings; use Aisthetix\TryOn\SettingsTestConnect; use Aisthetix\TryOn\SettingsTestStatus; $passed = 0; $failed = 0; function settings_check( $name, $condition ) { global $passed, $failed; if ( $condition ) { echo " ✓ {$name}\n"; ++$passed; return; } echo " ✗ {$name}\n"; ++$failed; } function settings_render( $callable ) { ob_start(); $callable(); return ob_get_clean(); } function settings_connected( $connected ) { $GLOBALS['settings_options'][ Plugin::OPTION_PUBLISHABLE_KEY ] = $connected ? 'pk_test' : ''; $GLOBALS['settings_options'][ Plugin::OPTION_SECRET_KEY ] = $connected ? 'sk_test' : ''; } echo "Settings\n"; $status = new SettingsTestStatus(); $connect = new SettingsTestConnect( $status ); $settings = new Settings( $status, $connect ); $settings->init(); settings_check( 'registers all three admin hooks', 3 === count( $GLOBALS['settings_hooks'] ) ); $settings->register_menu(); settings_check( 'registers under WooCommerce', 'woocommerce' === $GLOBALS['settings_menu'][0] ); $settings->register_settings(); settings_check( 'registers an array sanitizer', is_callable( $GLOBALS['settings_registration'][2]['sanitize_callback'] ) ); $clean = $settings->sanitize_settings( array( 'placement' => 'manual', 'button_text' => ' Try now ', 'unavailable_title' => str_repeat( 'T', 90 ), 'unavailable_message' => str_repeat( 'M', 250 ), 'unavailable_button_text' => str_repeat( 'B', 50 ), 'unavailable_button_url' => 'https://merchant.example/new', 'enable_analytics' => '1', 'enable_session_replay' => '1', ) ); settings_check( 'keeps an allowed placement', 'manual' === $clean['placement'] ); settings_check( 'sanitizes the button label', 'Try now' === $clean['button_text'] ); settings_check( 'applies UI length limits', 80 === strlen( $clean['unavailable_title'] ) && 240 === strlen( $clean['unavailable_message'] ) && 40 === strlen( $clean['unavailable_button_text'] ) ); settings_check( 'keeps a same-site fallback URL', 'https://merchant.example/new' === $clean['unavailable_button_url'] ); settings_check( 'normalizes opt-ins to booleans', true === $clean['enable_analytics'] && true === $clean['enable_session_replay'] ); $invalid = $settings->sanitize_settings( array( 'placement' => 'header', 'unavailable_button_url' => 'https://evil.example/' ) ); settings_check( 'rejects placement and cross-site URL injection', 'after_add_to_cart' === $invalid['placement'] && '' === $invalid['unavailable_button_url'] ); $relative = $settings->sanitize_settings( array( 'unavailable_button_url' => '/relative' ) ); settings_check( 'rejects hostless fallback URLs', '' === $relative['unavailable_button_url'] ); settings_connected( false ); $_GET = array( 'aisthetix_notice' => 'connect_success' ); $out = settings_render( array( $settings, 'render_notices' ) ); settings_check( 'renders a connect result but no usage notice while disconnected', false !== strpos( $out, 'Connected to aisthetix successfully' ) && 0 === $status->get_calls ); settings_connected( true ); $status->payload = array( 'trial' => array( 'remaining' => 7 ), 'plan' => 'Starter', 'includedTryOns' => 100 ); $out = settings_render( array( $settings, 'render_notices' ) ); settings_check( 'warns before trial exhaustion', false !== strpos( $out, '7 try-ons remaining' ) && false !== strpos( $out, 'notice-warning' ) ); $status->payload = array( 'trial' => array( 'remaining' => 0 ), 'plan' => '', 'includedTryOns' => 300 ); $out = settings_render( array( $settings, 'render_notices' ) ); settings_check( 'explains a paused trial and its allowance', false !== strpos( $out, 'currently paused' ) && false !== strpos( $out, '300 monthly' ) ); $_GET = array(); $status->payload = array( 'trial' => array( 'remaining' => 11 ) ); settings_check( 'does not nag above the low-trial threshold', '' === settings_render( array( $settings, 'render_notices' ) ) ); $status->payload = array( 'trial' => null ); settings_check( 'does not fabricate a notice without trial data', '' === settings_render( array( $settings, 'render_notices' ) ) ); $GLOBALS['settings_can_manage'] = false; settings_check( 'denies the settings page without merchant capability', '' === settings_render( array( $settings, 'render_page' ) ) ); $GLOBALS['settings_can_manage'] = true; settings_connected( false ); $status->payload = array( 'error' => 'Not connected' ); $out = settings_render( array( $settings, 'render_page' ) ); settings_check( 'renders the disconnected page and privacy promise', false !== strpos( $out, 'Connect aisthetix' ) && false !== strpos( $out, 'never stored on your WordPress server' ) ); settings_connected( true ); $GLOBALS['settings_options'][ Plugin::OPTION_SETTINGS ] = array( 'placement' => 'manual', 'enable_analytics' => true, 'enable_session_replay' => true ); foreach ( array( array( 'error' => 'offline' ), array( 'active' => true, 'withinQuota' => true, 'remaining' => 240, 'resetsAt' => '2026-10-01T00:00:00Z', 'plan' => 'Pro' ), array( 'active' => false, 'withinQuota' => false, 'remaining' => 0, 'resetsAt' => '', 'plan' => '' ), ) as $payload ) { $status->payload = $payload; $out = settings_render( array( $settings, 'render_page' ) ); settings_check( 'connected settings page renders its plan state', false !== strpos( $out, 'Disconnect' ) && false !== strpos( $out, 'Plan & usage' ) ); } settings_check( 'connected page force-refreshes status', 3 <= $status->refresh_calls ); echo "\n{$passed} passed, {$failed} failed\n"; exit( $failed > 0 ? 1 : 0 ); }