diff --git a/inc/compatibility/elementor.php b/inc/compatibility/elementor.php index 1b4f945956..45f0d4b352 100644 --- a/inc/compatibility/elementor.php +++ b/inc/compatibility/elementor.php @@ -140,11 +140,17 @@ public function enqueue() { * @return \WP_REST_Response|\WP_Error */ public function alter_global_colors_front_end( $response, $handler, \WP_REST_Request $request ) { - if ( is_wp_error( $response ) ) { + $route = $request->get_route(); + + // Elementor never knows Neve IDs, so not-found is expected on the colors route. + $is_color_not_found = is_wp_error( $response ) + && 'global_not_found' === $response->get_error_code() + && 0 === strpos( $route, '/elementor/v1/globals/colors/' ); + + if ( is_wp_error( $response ) && ! $is_color_not_found ) { return $response; } - $route = $request->get_route(); $rest_to_slugs = [ 'nvprimaryaccent' => 'nv-primary-accent', 'nvsecondaryaccent' => 'nv-secondary-accent', diff --git a/tests/test-elementor-compatibility.php b/tests/test-elementor-compatibility.php index 845d0246ba..6dc7d7b69a 100644 --- a/tests/test-elementor-compatibility.php +++ b/tests/test-elementor-compatibility.php @@ -20,24 +20,53 @@ class TestElementorCompatibility extends WP_UnitTestCase { */ const COLOR_ROUTE = self::GLOBALS_ROUTE . '/colors/nvprimaryaccent'; + /** + * Reset the static custom colors so they don't leak into other tests. + */ + public function tearDown(): void { + $this->set_custom_global_colors( null ); + + parent::tearDown(); + } + + /** + * Set the static custom global colors on the compatibility class. + * + * @param array|null $colors Custom global colors, or null for the unloaded default. + */ + private function set_custom_global_colors( ?array $colors ): void { + $custom_colors = new ReflectionProperty( \Neve\Compatibility\Elementor::class, 'custom_global_colors' ); + $custom_colors->setAccessible( true ); + $custom_colors->setValue( null, $colors ); + } + /** * Build a compatibility instance in a post-init state. * * Avoids ::init(), which needs ELEMENTOR_VERSION defined and would leak that * constant plus its hooks into the rest of the suite. * + * @param array $custom_global_colors Custom global colors theme mod value. + * * @return \Neve\Compatibility\Elementor */ - private function get_elementor_compat() { + private function get_elementor_compat( array $custom_global_colors = [] ) { $elementor = new \Neve\Compatibility\Elementor(); - $custom_colors = new ReflectionProperty( $elementor, 'custom_global_colors' ); - $custom_colors->setAccessible( true ); - $custom_colors->setValue( $elementor, [] ); + $this->set_custom_global_colors( $custom_global_colors ); return $elementor; } + /** + * Elementor's error for a global ID missing from its kit. + * + * @return WP_Error + */ + private function get_global_not_found_error() { + return new WP_Error( 'global_not_found', 'The Global value you are trying to use is not available.', [ 'status' => 404 ] ); + } + /** * Errored responses should be passed through untouched. */ @@ -89,4 +118,69 @@ public function test_global_colors_front_end_overrides_color() { $this->assertSame( 'nvprimaryaccent', $data['id'] ); $this->assertArrayHasKey( 'value', $data ); } + + /** + * Elementor's not-found error for a Neve color should resolve to the palette color. + */ + public function test_global_colors_front_end_resolves_neve_color_on_not_found() { + $elementor = $this->get_elementor_compat(); + $request = new WP_REST_Request( 'GET', self::COLOR_ROUTE ); + + $filtered = $elementor->alter_global_colors_front_end( $this->get_global_not_found_error(), [], $request ); + + $this->assertInstanceOf( WP_REST_Response::class, $filtered ); + + $palettes = neve_get_global_colors_default( true ); + $expected = $palettes['palettes'][ $palettes['activePalette'] ]['colors']['nv-primary-accent']; + $data = $filtered->get_data(); + + $this->assertSame( 'nvprimaryaccent', $data['id'] ); + $this->assertSame( neve_sanitize_colors( $expected ), $data['value'] ); + } + + /** + * Elementor's not-found error for a custom Neve color should resolve to that color. + */ + public function test_global_colors_front_end_resolves_custom_color_on_not_found() { + $elementor = $this->get_elementor_compat( + [ + 'custom' => [ + 'label' => 'Custom', + 'val' => '#123456', + ], + ] + ); + $request = new WP_REST_Request( 'GET', self::GLOBALS_ROUTE . '/colors/custom' ); + + $filtered = $elementor->alter_global_colors_front_end( $this->get_global_not_found_error(), [], $request ); + + $this->assertInstanceOf( WP_REST_Response::class, $filtered ); + + $data = $filtered->get_data(); + + $this->assertSame( 'custom', $data['id'] ); + $this->assertSame( '#123456', $data['value'] ); + } + + /** + * Elementor's not-found error for a non-Neve color should be passed through untouched. + */ + public function test_global_colors_front_end_passes_through_not_found_for_unknown_color() { + $elementor = $this->get_elementor_compat(); + $request = new WP_REST_Request( 'GET', self::GLOBALS_ROUTE . '/colors/primary' ); + $error = $this->get_global_not_found_error(); + + $this->assertSame( $error, $elementor->alter_global_colors_front_end( $error, [], $request ) ); + } + + /** + * Elementor's not-found error on a non-color route should be passed through for a Neve ID. + */ + public function test_global_colors_front_end_passes_through_not_found_on_typography_route() { + $elementor = $this->get_elementor_compat(); + $request = new WP_REST_Request( 'GET', self::GLOBALS_ROUTE . '/typography/nvprimaryaccent' ); + $error = $this->get_global_not_found_error(); + + $this->assertSame( $error, $elementor->alter_global_colors_front_end( $error, [], $request ) ); + } }