Skip to content

Commit 0bf75d4

Browse files
authored
SEO: Say when Jetpack AI is why the GEO tab's enhancer is off (#53871)
* SEO: Say when Jetpack AI is why the GEO tab's enhancer is off The GEO tab blamed AI SEO whenever the enhancer was disabled, even when the site-wide Jetpack AI switch was the one off. Report that switch in the GEO data and pick the message from it, as the Traffic page does. * SEO: Fold Jetpack AI into the test stub's AI SEO state The real is_ai_seo_enabled() is off whenever Jetpack AI is, so the stub follows suit and tests cannot build a state production never reports.
1 parent ec0ddac commit 0bf75d4

11 files changed

Lines changed: 71 additions & 20 deletions

File tree

‎projects/packages/seo/_inc/data/geo-types.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export interface GeoState {
2424
enabled: boolean;
2525
/** Whether the AI SEO control the Enhancer sits under is on. */
2626
aiSeoEnabled: boolean;
27+
/** Whether Jetpack AI is on site-wide; when off, AI SEO reads off too. */
28+
aiEnabled: boolean;
2729
};
2830
llmsTxt: {
2931
/** Whether llms.txt generation is switched on. */

‎projects/packages/seo/_inc/data/test/fixtures/store-fixtures.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const SEEDED_SETTINGS: SettingsResponse = {
4242
};
4343

4444
export const SEEDED_GEO: GeoState = {
45-
enhancer: { available: true, enabled: false, aiSeoEnabled: true },
45+
enhancer: { available: true, enabled: false, aiSeoEnabled: true, aiEnabled: true },
4646
llmsTxt: { enabled: false, url: 'https://example.com/llms.txt', canServe: true },
4747
crawlers: {
4848
catalog: [

‎projects/packages/seo/_inc/data/test/geo-store.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe( 'geo-store', () => {
1717

1818
it( 'replaces the enhancer state on setEnhancer', () => {
1919
const registry = makeRegistry();
20-
const next = { available: true, enabled: true, aiSeoEnabled: false };
20+
const next = { available: true, enabled: true, aiSeoEnabled: false, aiEnabled: true };
2121
registry.dispatch( geoStore ).setEnhancer( next );
2222
expect( registry.select( geoStore ).getEnhancer() ).toEqual( next );
2323
} );

‎projects/packages/seo/_inc/screens/geo/index.tsx‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,19 @@ const GeoScreen: FC< Props > = ( { form, searchEnginesVisible, onManageVisibilit
451451

452452
const llmsTxtEffectivelyOn = Boolean( searchEnginesVisible && llmsTxt?.enabled );
453453

454+
let enhancerHelp: string | undefined;
455+
if ( ! enhancer.aiSeoEnabled ) {
456+
enhancerHelp = enhancer.aiEnabled
457+
? __(
458+
'AI SEO is turned off for this site, so nothing is generated. Your choice is saved and applies again when AI SEO is turned back on.',
459+
'jetpack-seo'
460+
)
461+
: __(
462+
'Jetpack AI is turned off for this site, so nothing is generated. Your choice is saved and applies again when Jetpack AI is turned back on.',
463+
'jetpack-seo'
464+
);
465+
}
466+
454467
return (
455468
<div className="jetpack-seo-geo">
456469
{ llmsTxt && (
@@ -535,14 +548,7 @@ const GeoScreen: FC< Props > = ( { form, searchEnginesVisible, onManageVisibilit
535548
checked={ enhancer.enabled }
536549
onChange={ setEnhancerEnabled }
537550
disabled={ isSaving || ! enhancer.aiSeoEnabled }
538-
help={
539-
enhancer.aiSeoEnabled
540-
? undefined
541-
: __(
542-
'AI SEO is turned off for this site, so nothing is generated. Your choice is saved and applies again when AI SEO is turned back on.',
543-
'jetpack-seo'
544-
)
545-
}
551+
help={ enhancerHelp }
546552
__nextHasNoMarginBottom
547553
/>
548554
</CollapsibleCard.Content>

‎projects/packages/seo/_inc/screens/geo/test/index.test.tsx‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type { GeoForm } from '../../../data/use-geo';
1414
* @return A form controller for GeoScreen with the enhancer hidden.
1515
*/
1616
const formWith = ( canServe: boolean ): GeoForm => ( {
17-
enhancer: { available: false, enabled: false, aiSeoEnabled: true },
17+
enhancer: { available: false, enabled: false, aiSeoEnabled: true, aiEnabled: true },
1818
llmsTxt: { enabled: true, url: 'https://example.com/llms.txt', canServe },
1919
crawlers: null,
2020
isSaving: false,
@@ -247,7 +247,7 @@ describe( 'GeoScreen (GEO tab) — crawler policy state', () => {
247247
*/
248248
const allModulesForm = (): GeoForm => ( {
249249
...crawlerForm(),
250-
enhancer: { available: true, enabled: false, aiSeoEnabled: true },
250+
enhancer: { available: true, enabled: false, aiSeoEnabled: true, aiEnabled: true },
251251
llmsTxt: { enabled: true, url: 'https://example.com/llms.txt', canServe: true },
252252
} );
253253

@@ -400,12 +400,15 @@ describe( 'GeoScreen (GEO tab) — module titles', () => {
400400
describe( 'GeoScreen (GEO tab) — the AI SEO Enhancer card', () => {
401401
// Same treatment the Traffic page gives it: disabled with an explanation
402402
// rather than hidden, so the saved choice stays visible.
403-
it( 'disables the toggle and explains why while the AI SEO control is off', () => {
403+
it.each( [
404+
[ 'the AI SEO control', true, /AI SEO is turned off for this site/ ],
405+
[ 'Jetpack AI', false, /Jetpack AI is turned off for this site/ ],
406+
] )( 'disables the toggle and explains why while %s is off', ( _, aiEnabled, message ) => {
404407
render(
405408
<GeoScreen
406409
form={ {
407410
...allModulesForm(),
408-
enhancer: { available: true, enabled: true, aiSeoEnabled: false },
411+
enhancer: { available: true, enabled: true, aiSeoEnabled: false, aiEnabled },
409412
} }
410413
searchEnginesVisible
411414
onManageVisibility={ noop }
@@ -415,15 +418,15 @@ describe( 'GeoScreen (GEO tab) — the AI SEO Enhancer card', () => {
415418
expect(
416419
screen.getByRole( 'checkbox', { name: /Automatically generate SEO title/ } )
417420
).toBeDisabled();
418-
expect( screen.getByText( /AI SEO is turned off for this site/ ) ).toBeInTheDocument();
421+
expect( screen.getByText( message ) ).toBeInTheDocument();
419422
} );
420423

421424
it( 'leaves the toggle usable while the AI SEO control is on', () => {
422425
render(
423426
<GeoScreen
424427
form={ {
425428
...allModulesForm(),
426-
enhancer: { available: true, enabled: true, aiSeoEnabled: true },
429+
enhancer: { available: true, enabled: true, aiSeoEnabled: true, aiEnabled: true },
427430
} }
428431
searchEnginesVisible
429432
onManageVisibility={ noop }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: fixed
3+
4+
GEO: Explain that Jetpack AI is turned off when that is why the AI SEO Enhancer cannot generate anything.

‎projects/packages/seo/src/class-dashboard-data.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,8 @@ public static function get_google_verify_data() {
11681168
*
11691169
* `aiSeoEnabled` is reported separately rather than folded into availability:
11701170
* with the control off the card is disabled, not hidden, so the saved choice
1171-
* stays visible — the same treatment the Traffic page gives it.
1171+
* stays visible — the same treatment the Traffic page gives it. `aiEnabled`
1172+
* reports the site-wide Jetpack AI switch so the tab can name which one is off.
11721173
*
11731174
* @return array
11741175
*/
@@ -1180,6 +1181,8 @@ public static function get_geo_data() {
11801181
// does not exist, so the enhancer keeps its pre-control behavior.
11811182
// @phan-suppress-next-line PhanUndeclaredClassMethod -- Jetpack_AI_Settings lives in plugins/jetpack and is guarded by is_callable.
11821183
$ai_seo_on = ! is_callable( array( 'Jetpack_AI_Settings', 'is_ai_seo_enabled' ) ) || \Jetpack_AI_Settings::is_ai_seo_enabled();
1184+
// @phan-suppress-next-line PhanUndeclaredClassMethod -- Jetpack_AI_Settings lives in plugins/jetpack and is guarded by is_callable.
1185+
$ai_on = ! is_callable( array( 'Jetpack_AI_Settings', 'is_ai_enabled' ) ) || \Jetpack_AI_Settings::is_ai_enabled();
11831186

11841187
// Current_Plan comes from the jetpack-plans package (a dependency of this
11851188
// package since the plan-gating work), so it's always available here; the
@@ -1192,6 +1195,7 @@ public static function get_geo_data() {
11921195
'available' => $filter_on && $plan_supports,
11931196
'enabled' => (bool) get_option( 'ai_seo_enhancer_enabled', false ),
11941197
'aiSeoEnabled' => $ai_seo_on,
1198+
'aiEnabled' => $ai_on,
11951199
),
11961200
'llmsTxt' => array(
11971201
'enabled' => Llms_Txt::is_enabled(),

‎projects/packages/seo/tests/php/DashboardDataTest.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ protected function tearDown(): void {
2828
\Jetpack_SEO_Utils::$has_legacy_front_page_meta = false;
2929
\Jetpack_Redux_State_Helper::$site_image = '';
3030
\Jetpack_AI_Settings::$is_ai_seo_enabled = true;
31+
\Jetpack_AI_Settings::$is_ai_enabled = true;
3132
delete_option( 'advanced_seo_title_formats' );
3233
delete_option( 'jetpack_ai_seo_enabled' );
3334
delete_option( 'jetpack_seo_sitemap_enabled' );
@@ -61,6 +62,17 @@ public function test_get_geo_data_reports_the_ai_seo_control_state() {
6162
$this->assertFalse( $off['aiSeoEnabled'] );
6263
}
6364

65+
/**
66+
* The site-wide Jetpack AI switch is reported on its own, so the tab can name it.
67+
*/
68+
public function test_get_geo_data_reports_the_jetpack_ai_switch_state() {
69+
$this->assertTrue( Dashboard_Data::get_geo_data()['enhancer']['aiEnabled'] );
70+
71+
\Jetpack_AI_Settings::$is_ai_enabled = false;
72+
73+
$this->assertFalse( Dashboard_Data::get_geo_data()['enhancer']['aiEnabled'] );
74+
}
75+
6476
/**
6577
* The plan and the feature filter still veto availability outright — those
6678
* are entitlement questions, not a switched-off control.

‎projects/packages/seo/tests/php/stubs/class-jetpack-ai-settings.php‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,28 @@ class Jetpack_AI_Settings {
2020
public static $is_ai_seo_enabled = true;
2121

2222
/**
23-
* Return the configured AI SEO state.
23+
* Whether the site-wide Jetpack AI switch reports on.
24+
*
25+
* @var bool
26+
*/
27+
public static $is_ai_enabled = true;
28+
29+
/**
30+
* Return the configured Jetpack AI state.
31+
*
32+
* @return bool
33+
*/
34+
public static function is_ai_enabled(): bool {
35+
return self::$is_ai_enabled;
36+
}
37+
38+
/**
39+
* Return the configured AI SEO state, off whenever Jetpack AI is, as in the real class.
2440
*
2541
* @return bool
2642
*/
2743
public static function is_ai_seo_enabled(): bool {
28-
return self::$is_ai_seo_enabled;
44+
return self::$is_ai_seo_enabled && self::$is_ai_enabled;
2945
}
3046
}
3147
}

‎projects/packages/seo/tests/routes/geo/stage.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe( 'GEO route stage', () => {
7777

7878
Stage();
7979
const geo = {
80-
enhancer: { available: true, enabled: false, aiSeoEnabled: true },
80+
enhancer: { available: true, enabled: false, aiSeoEnabled: true, aiEnabled: true },
8181
llmsTxt: { enabled: true, url: 'https://example.com/llms.txt', canServe: true },
8282
crawlers: { catalog: [], overrides: {} },
8383
};

0 commit comments

Comments
 (0)