diff --git a/includes/JtlConnectorAdmin.php b/includes/JtlConnectorAdmin.php index 7501ff2b..121c251a 100644 --- a/includes/JtlConnectorAdmin.php +++ b/includes/JtlConnectorAdmin.php @@ -505,6 +505,8 @@ private static function add_constraints_for_multi_linking_tables(string $prefix, */ private static function initDefaultConfigValues(string $buildVersion): void { + $isFirstInstall = self::isFirstInstall(); + Config::set(Config::OPTIONS_TOKEN, self::create_password()); Config::set(Config::OPTIONS_INSTALLED_VERSION, $buildVersion); @@ -514,21 +516,34 @@ private static function initDefaultConfigValues(string $buildVersion): void } } - self::setDefaultWooCommerceTaxOptions(); + if ($isFirstInstall) { + self::setDefaultWooCommerceTaxOptions(); + } + } + + /** + * @return bool + */ + private static function isFirstInstall(): bool + { + return Config::get(Config::OPTIONS_INSTALLED_VERSION) === null; } /** + * WooCommerce writes its own default of 'excl' for these options during its own + * installation, so an explicit 'incl' has to be forced once on first activation of + * this plugin. Gated behind $isFirstInstall so a later, deliberate change by the + * shop owner is not reverted on every plugin re-activation. + * * @return void */ private static function setDefaultWooCommerceTaxOptions(): void { - $shopDisplay = \get_option('woocommerce_tax_display_shop', false); - if ($shopDisplay === false) { + if (\get_option('woocommerce_tax_display_shop') !== 'incl') { \update_option('woocommerce_tax_display_shop', 'incl', true); } - $cartDisplay = \get_option('woocommerce_tax_display_cart', false); - if ($cartDisplay === false) { + if (\get_option('woocommerce_tax_display_cart') !== 'incl') { \update_option('woocommerce_tax_display_cart', 'incl', true); } } diff --git a/tests/src/JtlConnectorAdminTest.php b/tests/src/JtlConnectorAdminTest.php index c6046f63..1b490d47 100644 --- a/tests/src/JtlConnectorAdminTest.php +++ b/tests/src/JtlConnectorAdminTest.php @@ -39,17 +39,17 @@ protected function tearDown(): void * @return void * @throws \ReflectionException */ - public function testSetDefaultWooCommerceTaxOptionsSetsValuesOnFirstActivation(): void + public function testSetDefaultWooCommerceTaxOptionsSetsValuesWhenOptionsDoNotExistYet(): void { WP_Mock::userFunction('get_option', [ 'times' => 1, - 'args' => ['woocommerce_tax_display_shop', false], + 'args' => ['woocommerce_tax_display_shop'], 'return' => false, ]); WP_Mock::userFunction('get_option', [ 'times' => 1, - 'args' => ['woocommerce_tax_display_cart', false], + 'args' => ['woocommerce_tax_display_cart'], 'return' => false, ]); @@ -71,21 +71,62 @@ public function testSetDefaultWooCommerceTaxOptionsSetsValuesOnFirstActivation() } /** + * Regression test for CO-3447: WooCommerce writes its own 'excl' default for these + * options during its own installation, so get_option() never returns false on a real + * install. The fix has to correct that pre-existing 'excl' value, not just handle the + * (rarely occurring) case where the option is entirely unset. + * * @covers \JtlConnectorAdmin::setDefaultWooCommerceTaxOptions * @return void * @throws \ReflectionException */ - public function testSetDefaultWooCommerceTaxOptionsDoesNotOverwriteOnReActivation(): void + public function testSetDefaultWooCommerceTaxOptionsCorrectsWooCommerceDefaultExclValue(): void { WP_Mock::userFunction('get_option', [ 'times' => 1, - 'args' => ['woocommerce_tax_display_shop', false], + 'args' => ['woocommerce_tax_display_shop'], + 'return' => 'excl', + ]); + + WP_Mock::userFunction('get_option', [ + 'times' => 1, + 'args' => ['woocommerce_tax_display_cart'], + 'return' => 'excl', + ]); + + WP_Mock::userFunction('update_option', [ + 'times' => 1, + 'args' => ['woocommerce_tax_display_shop', 'incl', true], + ]); + + WP_Mock::userFunction('update_option', [ + 'times' => 1, + 'args' => ['woocommerce_tax_display_cart', 'incl', true], + ]); + + $reflection = new \ReflectionClass(\JtlConnectorAdmin::class); + $method = $reflection->getMethod('setDefaultWooCommerceTaxOptions'); + $method->invoke(null); + + $this->addToAssertionCount(1); + } + + /** + * @covers \JtlConnectorAdmin::setDefaultWooCommerceTaxOptions + * @return void + * @throws \ReflectionException + */ + public function testSetDefaultWooCommerceTaxOptionsDoesNotOverwriteWhenAlreadyIncl(): void + { + WP_Mock::userFunction('get_option', [ + 'times' => 1, + 'args' => ['woocommerce_tax_display_shop'], 'return' => 'incl', ]); WP_Mock::userFunction('get_option', [ 'times' => 1, - 'args' => ['woocommerce_tax_display_cart', false], + 'args' => ['woocommerce_tax_display_cart'], 'return' => 'incl', ]); @@ -100,4 +141,42 @@ public function testSetDefaultWooCommerceTaxOptionsDoesNotOverwriteOnReActivatio \Mockery::close(); $this->addToAssertionCount(1); } + + /** + * @covers \JtlConnectorAdmin::isFirstInstall + * @return void + * @throws \ReflectionException + */ + public function testIsFirstInstallReturnsTrueWhenNoVersionIsStoredYet(): void + { + WP_Mock::userFunction('get_option', [ + 'times' => 1, + 'args' => ['jtlconnector_installed_version', null], + 'return' => null, + ]); + + $reflection = new \ReflectionClass(\JtlConnectorAdmin::class); + $method = $reflection->getMethod('isFirstInstall'); + + $this->assertTrue($method->invoke(null)); + } + + /** + * @covers \JtlConnectorAdmin::isFirstInstall + * @return void + * @throws \ReflectionException + */ + public function testIsFirstInstallReturnsFalseWhenVersionIsAlreadyStored(): void + { + WP_Mock::userFunction('get_option', [ + 'times' => 1, + 'args' => ['jtlconnector_installed_version', null], + 'return' => '1.0.0', + ]); + + $reflection = new \ReflectionClass(\JtlConnectorAdmin::class); + $method = $reflection->getMethod('isFirstInstall'); + + $this->assertFalse($method->invoke(null)); + } }