diff --git a/.distignore b/.distignore new file mode 100644 index 0000000..58601de --- /dev/null +++ b/.distignore @@ -0,0 +1,28 @@ +# Files and folders excluded from the distributable plugin zip. +# Patterns are rsync --exclude-from compatible. + +.git/ +.github/ +.gitignore +.gitattributes +.distignore +.vscode/ +.editorconfig +.DS_Store + +node_modules/ +tests/ +coverage/ +docs/ + +phpcs.xml +phpunit.xml.dist +vitest.config.js +webpack.config.js +tsconfig.json +package-lock.json +composer.lock + +CHANGELOG.md +fix-step2.php +fix-tour2.php diff --git a/.github/workflows/manually-build-zip.yml b/.github/workflows/manually-build-zip.yml new file mode 100644 index 0000000..d5f6b71 --- /dev/null +++ b/.github/workflows/manually-build-zip.yml @@ -0,0 +1,61 @@ +name: Manually build plugin zip + +on: + workflow_dispatch: + inputs: + tag: + description: "Existing tag/release to attach the zip to (e.g. 0.5.0)" + required: true + +permissions: + contents: write + +env: + PLUGIN_SLUG: admin-coach-tours + PLUGIN_ZIP: admin-coach-tours.zip + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout tag + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.tag }} + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install JS dependencies and build assets + run: | + npm ci + npm run build + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: composer + + - name: Install production Composer dependencies + run: composer install --no-dev --optimize-autoloader --no-interaction + + - name: Verify updater dependency is bundled + run: | + test -d vendor/yahnis-elsts/plugin-update-checker \ + || { echo "Missing yahnis-elsts/plugin-update-checker in vendor/"; exit 1; } + + - name: Build plugin zip + run: | + mkdir -p "/tmp/${PLUGIN_SLUG}" + rsync -a --exclude-from=.distignore --exclude='.git' ./ "/tmp/${PLUGIN_SLUG}/" + ( cd /tmp && zip -rq "${GITHUB_WORKSPACE}/${PLUGIN_ZIP}" "${PLUGIN_SLUG}" ) + + - name: Attach zip to release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.event.inputs.tag }} + files: ${{ env.PLUGIN_ZIP }} diff --git a/.github/workflows/on-release-add.zip.yml b/.github/workflows/on-release-add.zip.yml new file mode 100644 index 0000000..c071edc --- /dev/null +++ b/.github/workflows/on-release-add.zip.yml @@ -0,0 +1,55 @@ +name: Build and attach release zip + +on: + release: + types: [published] + +permissions: + contents: write + +env: + PLUGIN_SLUG: admin-coach-tours + PLUGIN_ZIP: admin-coach-tours.zip + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install JS dependencies and build assets + run: | + npm ci + npm run build + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: composer + + - name: Install production Composer dependencies + run: composer install --no-dev --optimize-autoloader --no-interaction + + - name: Verify updater dependency is bundled + run: | + test -d vendor/yahnis-elsts/plugin-update-checker \ + || { echo "Missing yahnis-elsts/plugin-update-checker in vendor/"; exit 1; } + + - name: Build plugin zip + run: | + mkdir -p "/tmp/${PLUGIN_SLUG}" + rsync -a --exclude-from=.distignore --exclude='.git' ./ "/tmp/${PLUGIN_SLUG}/" + ( cd /tmp && zip -rq "${GITHUB_WORKSPACE}/${PLUGIN_ZIP}" "${PLUGIN_SLUG}" ) + + - name: Attach zip to release + uses: softprops/action-gh-release@v2 + with: + files: ${{ env.PLUGIN_ZIP }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c19d73..1f112b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.0] - 2026-07-24 + +### Changed + +- Reworked the AI layer to use the WordPress 7 AI Connector (`wp_get_connectors` / `wp_ai_client_prompt`); removed the bundled OpenAI/Azure/Anthropic providers and API-key encryption. **Now requires WordPress 7.0+ and at least one configured AI provider connector.** +- Refactored the AI tour-generation flow into deep modules (`TourRequest`, `TourGenerator`, `TourSchema`); the REST controller is now a thin adapter. +- Slimmed the settings page to connector status, an enable toggle, and an optional provider/model override. +- Reordered task categories (Text first) and expanded the task list: added Paragraph, plus Code, Separator, Details, Audio, File, Group, Spacer, and Embed-from-URL. + +### Added + +- Self-updates from GitHub releases via the WordPress Plugin GitHub Updater, plus GitHub Actions workflows to build and attach the release zip. +- Coach panel shows the `/command` as a header above the instruction. +- Refocus the current block when a tour closes. +- Finish the tour by clicking the highlighted block on the last step. +- Hide the launcher when the editor is in code editor mode. +- Stop the tour with a clear message when a different block than expected is inserted. + +### Fixed + +- `generate-tour` REST route rejected `null` `editorContext`/`failureContext` (400 `rest_invalid_param`). +- `wpBlock: "selected"` now falls back to the last-selected block so multi-step tours don't fail on confirmation steps. +- Removed the arrow "Next" button that let users skip ahead before a block existed. +- Tour copy no longer uses positional words ("below"/"above"). + ## [0.4.1] - 2026-02-03 ### Added diff --git a/README.md b/README.md index a3f613c..6c113af 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Admin Coach Tours [](https://github.com/soderlind/admin-coach-tours) -[](https://wordpress.org) +[](https://wordpress.org) [](https://php.net) [](https://www.gnu.org/licenses/gpl-2.0.html) @@ -16,7 +16,7 @@ Admin Coach Tours helps WordPress users learn the block editor through AI-genera ## Features - **AI-Generated Tours** — On-demand tutorials created by AI based on your request -- **12 Predefined Tasks** — Common tasks like adding images, videos, headings, and more +- **Ready-made Task Library** — 20+ common tasks like adding images, videos, headings, paragraphs, and more - **Freeform Questions** — Ask anything about the block editor - **Interactive Overlay** — Visual highlighting guides you through each step - **Smart Block Targeting** — Accurately identifies and highlights the correct elements @@ -24,28 +24,43 @@ Admin Coach Tours helps WordPress users learn the block editor through AI-genera ## Requirements -- WordPress 6.8+ +- WordPress 7.0+ - PHP 8.3+ -- sodium extension (for API key encryption) -- AI provider API key (OpenAI, Azure OpenAI, or Anthropic) +- At least one WordPress AI provider connector configured ## Installation -1. Upload `admin-coach-tours` to `/wp-content/plugins/` -2. Activate the plugin -3. Go to **Tools → Coach Tours** to configure AI +### From a release zip (recommended) + +1. Download `admin-coach-tours.zip` from the [latest release](https://github.com/soderlind/admin-coach-tours/releases/latest). +2. In wp-admin, go to **Plugins → Add New → Upload Plugin**, choose the zip, and click **Install Now**. +3. Activate the plugin. +4. Go to **Tools → Coach Tours** to enable AI. + +### From source + +```bash +git clone https://github.com/soderlind/admin-coach-tours.git +cd admin-coach-tours +composer install --no-dev +npm ci && npm run build +``` + +Copy the folder into `wp-content/plugins/` and activate it. + +### Updates + +The plugin updates itself from GitHub releases — new versions show up under **Plugins** and **Dashboard → Updates** like any other plugin (checked roughly every 6 hours). ## Setup ### Configure AI Provider -1. Navigate to **Tours → Settings** -2. Enable AI Features -3. Select your provider: - - **OpenAI** — Add your API key - - **Azure OpenAI** — Add your API key and endpoint URL - - **Anthropic** — Add your API key -4. Save settings +1. Configure at least one WordPress AI provider connector +2. Navigate to **Tools → Coach Tours** +3. Enable AI Features +4. Optionally choose a preferred provider and model override +5. Save settings ## Usage @@ -58,10 +73,10 @@ Admin Coach Tours helps WordPress users learn the block editor through AI-genera **Common Tasks:** | Category | Tasks | |----------|-------| -| Media | Add image, Add video, Create gallery, Add cover | -| Text | Add heading, Create list, Add quote, Create table | -| Design | Add button, Create columns | -| Embed | Embed YouTube | +| Text | Add paragraph, heading, list, quote, table; format text; code, separator, details | +| Media | Add image, video, gallery, cover, audio, file | +| Design | Add button, columns, group, spacer | +| Embed | Embed YouTube, embed from URL | **Or Ask a Question:** Type any question about the block editor and press Enter. @@ -69,11 +84,6 @@ Type any question about the block editor and press Enter. 4. Follow the highlighted steps to complete the task 5. Each step auto-advances when you perform the action -### Tour Controls - -- **Previous/Next** — Navigate between steps -- **Skip** — Skip a step you already know -- **Stop** — Exit the tour at any time ## How It Works @@ -150,7 +160,7 @@ npm run lint # JavaScript (ESLint) ## Security -- API keys encrypted with libsodium +- API keys are owned by the WordPress AI connector (not stored by this plugin) - All endpoints require authentication - Capability checks on all operations - Input sanitization and output escaping @@ -162,5 +172,5 @@ GPL v2 or later — see [LICENSE](LICENSE) for details. ## Credits - Built with [@wordpress/scripts](https://www.npmjs.com/package/@wordpress/scripts) and [@wordpress/data](https://www.npmjs.com/package/@wordpress/data) -- AI integration supports OpenAI, Azure OpenAI, and Anthropic +- AI generation via the WordPress 7 AI Connector (`wp_get_connectors` / `wp_ai_client_prompt`) - RAG knowledge base includes pedagogical content from [Learn WordPress](https://learn.wordpress.org/learning-pathway/user/) courses diff --git a/admin-coach-tours.php b/admin-coach-tours.php index 77d4755..4055139 100644 --- a/admin-coach-tours.php +++ b/admin-coach-tours.php @@ -11,8 +11,8 @@ * Plugin Name: Admin Coach Tours * Plugin URI: https://github.com/soderlind/admin-coach-tours * Description: Interactive guided tours for WordPress admin, enabling educators to create step-by-step tutorials and pupils to learn with guided overlays. - * Version: 0.4.1 - * Requires at least: 6.8 + * Version: 0.5.0 + * Requires at least: 7.0 * Requires PHP: 8.3 * Author: Per Soderlind * Author URI: https://github.com/soderlind @@ -32,7 +32,7 @@ /** * Plugin version. */ -const VERSION = '0.4.1'; +const VERSION = '0.5.0'; /** * Plugin slug. @@ -57,7 +57,7 @@ /** * Minimum WordPress version. */ -const MIN_WP_VERSION = '6.8'; +const MIN_WP_VERSION = '7.0'; /** * Minimum PHP version. @@ -100,11 +100,6 @@ function check_requirements(): bool { ); } - // Check sodium extension. - if ( ! function_exists( 'sodium_crypto_secretbox' ) ) { - $errors[] = __( 'Admin Coach Tours requires the PHP Sodium extension for secure API key storage.', 'admin-coach-tours' ); - } - if ( ! empty( $errors ) ) { add_action( 'admin_notices', @@ -141,6 +136,18 @@ function init(): void { dirname( PLUGIN_BASENAME ) . '/languages' ); + // Self-updates from GitHub releases. + if ( class_exists( \Soderlind\WordPress\GitHubUpdater::class ) ) { + \Soderlind\WordPress\GitHubUpdater::init( + github_url: 'https://github.com/soderlind/admin-coach-tours', + plugin_file: __FILE__, + plugin_slug: 'admin-coach-tours', + name_regex: '/admin-coach-tours\.zip/', + branch: 'main', + check_period: 6, + ); + } + // Initialize components. $initializers = [ Cpt\ToursCpt::class, @@ -160,6 +167,52 @@ function init(): void { // Enqueue admin assets (for settings page). add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\enqueue_admin_assets' ); + + // One-time cleanup of legacy AI options after upgrading to the WP AI connector. + add_action( 'admin_init', __NAMESPACE__ . '\\maybe_upgrade' ); +} + +/** + * Run one-time upgrade routines when the stored version differs from the current one. + * + * Removes legacy provider API-key options that are now managed by the + * WordPress AI connector (wp_get_connectors) instead of this plugin. + * + * @return void + */ +function maybe_upgrade(): void { + $stored = get_option( 'act_version', '' ); + + if ( VERSION === $stored ) { + return; + } + + // Legacy options that stored encrypted API keys and per-provider config. + $legacy_options = [ + 'act_ai_provider', + 'act_ai_openai_api_key', + 'act_ai_openai_model', + 'act_ai_azure_api_key', + 'act_ai_azure_endpoint', + 'act_ai_azure_deployment', + 'act_ai_azure_model', + 'act_ai_anthropic_api_key', + 'act_ai_anthropic_model', + 'act_ai_api_key', + 'act_ai_endpoint', + 'act_encryption_key', + ]; + + // Preserve act_ai_provider only if it now holds a connector ID (repurposed). + // A fresh install has no legacy encrypted keys, so removing the others is safe. + foreach ( $legacy_options as $option ) { + if ( 'act_ai_provider' === $option ) { + continue; + } + delete_option( $option ); + } + + update_option( 'act_version', VERSION ); } /** diff --git a/assets/js/pupil/CoachPanel.jsx b/assets/js/pupil/CoachPanel.jsx index 5f1a5a1..b31c15c 100644 --- a/assets/js/pupil/CoachPanel.jsx +++ b/assets/js/pupil/CoachPanel.jsx @@ -7,7 +7,7 @@ * @since 0.1.0 */ -import { useState, useEffect, useCallback, useRef } from '@wordpress/element'; +import { useState, useEffect, useCallback, useRef, useMemo } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { Button, @@ -16,7 +16,7 @@ import { FlexItem, FlexBlock, } from '@wordpress/components'; -import { arrowRight, close } from '@wordpress/icons'; +import { close } from '@wordpress/icons'; /** * @typedef {import('../types/step.js').Step} Step @@ -66,6 +66,17 @@ export default function CoachPanel( { const isLastStep = stepIndex === totalSteps - 1; const isManualCompletion = step?.completion?.type === 'manual'; + // Detect a "/command" in the instruction so it can be surfaced as a header. + const slashCommand = useMemo( () => { + const text = ( step?.content || '' ).replace( /<[^>]*>/g, ' ' ); + const match = text.match( /(?:^|\s)(\/[a-z][a-z0-9-]*)/i ); + return match ? match[ 1 ] : null; + }, [ step?.content ] ); + + // Only steps the user must confirm show a footer control; auto-advancing + // steps have none (removing the old "next" arrow that skipped ahead). + const hasControl = isManualCompletion || isLastStep; + /** * Position panel relative to target element. */ @@ -284,6 +295,43 @@ export default function CoachPanel( { return ( <> + { slashCommand && ( +
+ { slashCommand }
+
+ ' . esc_html__( - 'Configure AI providers for generating step drafts and completion suggestions.', + 'AI features use the WordPress AI connector. Configure at least one AI provider connector in WordPress, then enable AI features below.', 'admin-coach-tours' ) . '
'; $ai_manager = AiManager::get_instance(); + $connectors = $ai_manager->get_configured_connectors(); - if ( ! $ai_manager->is_available() ) { + if ( empty( $connectors ) ) { echo ''; - esc_html_e( 'No AI provider is currently configured. Add an API key below to enable AI features.', 'admin-coach-tours' ); + esc_html_e( 'No configured AI provider connector was detected. AI features are unavailable until a connector is set up.', 'admin-coach-tours' ); echo '
'; + printf( + /* translators: %s: comma-separated list of configured provider labels. */ + esc_html__( 'Configured AI providers: %s', 'admin-coach-tours' ), + esc_html( implode( ', ', $connectors ) ) + ); + echo '
- +
'; - - if ( 'select' === $type && ! empty( $options ) ) { - echo ''; - } else { - $input_type = 'password' === $type ? 'password' : 'text'; - - if ( $is_sensitive ) { - // Show a masked field with option to reveal/update. - printf( - '', - esc_attr( $input_type ), - esc_attr( $name ), - esc_attr( $name ), - ! empty( $value ) ? esc_attr__( 'Leave empty to keep current', 'admin-coach-tours' ) : '' - ); - - if ( ! empty( $value ) ) { - echo ''; - } - } else { - printf( - '', - esc_attr( $input_type ), - esc_attr( $name ), - esc_attr( $name ), - esc_attr( $display_value ) - ); - } - } - - if ( ! empty( $desc ) ) { - echo '' . esc_html( $desc ) . '
'; - } - - echo ''; + esc_html_e( 'Optional model ID to use (e.g., a specific model name). Leave empty to use the provider default.', 'admin-coach-tours' ); + echo '
'; } } diff --git a/readme.txt b/readme.txt index ab8f60b..a1065f6 100644 --- a/readme.txt +++ b/readme.txt @@ -1,10 +1,10 @@ === Admin Coach Tours === Contributors: PerS Tags: gutenberg, block editor, tutorial, guided tour, ai, learning -Requires at least: 6.8 +Requires at least: 7.0 Tested up to: 6.9 Requires PHP: 8.3 -Stable tag: 0.4.1 +Stable tag: 0.5.0 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -17,7 +17,7 @@ Admin Coach Tours helps WordPress users learn the block editor through AI-genera = Features = * **AI-Generated Tours** — On-demand tutorials created by AI based on your request -* **12 Predefined Tasks** — Common tasks like adding images, videos, headings, and more +* **Ready-made Task Library** — 20+ common tasks like adding images, videos, headings, paragraphs, and more * **Freeform Questions** — Ask anything about the block editor * **Interactive Overlay** — Visual highlighting guides you through each step * **Smart Block Targeting** — Accurately identifies and highlights the correct elements @@ -26,48 +26,67 @@ Admin Coach Tours helps WordPress users learn the block editor through AI-genera = Predefined Tasks = -**Media:** -* Add an image -* Add a video -* Create a gallery -* Add a cover image - **Text & Content:** +* Add a paragraph * Add a heading * Create a list * Add a quote * Create a table -* Add a button +* Format text (bold, italic, links) +* Add a code block +* Add a separator +* Add a details/accordion + +**Media:** +* Add an image +* Add a video +* Create a gallery +* Add a cover image +* Add audio +* Add a file download **Design & Layout:** +* Add a button * Create columns -* Add a group block +* Group blocks in a container +* Add spacing **Embeds:** * Embed a YouTube video +* Embed from a URL = Requirements = -* WordPress 6.8 or later +* WordPress 7.0 or later * PHP 8.3 or later -* sodium extension (for API key encryption) -* AI provider API key (OpenAI, Azure OpenAI, or Anthropic) +* At least one WordPress AI provider connector configured == Installation == -1. Upload the `admin-coach-tours` folder to `/wp-content/plugins/` -2. Activate the plugin through the 'Plugins' menu in WordPress -3. Go to **Tools → Coach Tours** to configure your AI provider += From a release zip (recommended) = + +1. Download `admin-coach-tours.zip` from the latest GitHub release: https://github.com/soderlind/admin-coach-tours/releases/latest +2. In wp-admin, go to Plugins > Add New > Upload Plugin, choose the zip, and click Install Now. +3. Activate the plugin. +4. Go to **Tools → Coach Tours** to enable AI features. + += From source = + +1. `git clone https://github.com/soderlind/admin-coach-tours.git` +2. `composer install --no-dev` and `npm ci && npm run build` +3. Copy the folder into `wp-content/plugins/` and activate it. + += Updates = + +The plugin updates itself from GitHub releases — new versions appear under Plugins and Dashboard > Updates like any other plugin. = Configure AI Provider = -1. Navigate to **Tools → Coach Tours** -2. Enable AI Features -3. Select your provider: - * **OpenAI** — Add your API key - * **Azure OpenAI** — Add endpoint URL, API key, and deployment name - * **Anthropic** — Add your API key -4. Save settings +1. Configure at least one WordPress AI provider connector +2. Navigate to **Tools → Coach Tours** +3. Enable AI Features +4. Optionally choose a preferred provider and model override +5. Save settings == Usage == @@ -92,14 +111,11 @@ Admin Coach Tours helps WordPress users learn the block editor through AI-genera = What AI providers are supported? = -Admin Coach Tours supports: -* OpenAI (GPT-4 and GPT-4o models) -* Azure OpenAI -* Anthropic (Claude models) +Admin Coach Tours uses the WordPress AI connector, so it works with whatever AI provider connectors you have configured in WordPress (for example, an Azure AI Foundry or OpenAI connector). Configure at least one connector, then enable AI features in the plugin. = Are API keys stored securely? = -Yes, all API keys are encrypted using PHP's sodium extension before being stored in the database. +This plugin does not store API keys. Keys are managed by the WordPress AI connector you configure; the plugin only calls the connector. = Do I need to create my own tours? = @@ -122,6 +138,22 @@ Yes, the plugin can be activated network-wide or on individual sites. == Changelog == += 0.5.0 = +* Changed: Reworked the AI layer to use the WordPress 7 AI Connector (wp_get_connectors / wp_ai_client_prompt); removed bundled providers and API-key encryption. Now requires WordPress 7.0+ and at least one configured AI provider connector. +* Changed: Refactored the AI tour-generation flow into deep modules; the REST controller is now a thin adapter. +* Changed: Slimmed the settings page to connector status, an enable toggle, and an optional provider/model override. +* Changed: Reordered task categories (Text first) and expanded the task list (Paragraph, Code, Separator, Details, Audio, File, Group, Spacer, Embed-from-URL). +* Added: Coach panel shows the "/command" as a header above the instruction. +* Added: Self-updates from GitHub releases via the WordPress Plugin GitHub Updater, plus release-zip build workflows. +* Added: Refocus the current block when a tour closes. +* Added: Finish the tour by clicking the highlighted block on the last step. +* Added: Hide the launcher when the editor is in code editor mode. +* Added: Stop the tour with a clear message when a different block than expected is inserted. +* Fixed: generate-tour REST route rejected null editorContext/failureContext. +* Fixed: wpBlock "selected" now falls back to the last-selected block so multi-step tours don't fail on confirmation steps. +* Fixed: Removed the arrow "Next" button that let users skip ahead before a block existed. +* Fixed: Tour copy no longer uses positional words ("below"/"above"). + = 0.4.0 = * Added: Localized AI responses - tours are now generated in the user's WordPress language * Added: Language detection using WordPress format_code_lang() for accurate language names diff --git a/tests/php/AiControllerTest.php b/tests/php/AiControllerTest.php index 5413567..80cec85 100644 --- a/tests/php/AiControllerTest.php +++ b/tests/php/AiControllerTest.php @@ -2,6 +2,11 @@ /** * Test AI Controller. * + * The controller is now a thin HTTP adapter; sanitization of editor/failure + * context lives in TourRequest and cache/orchestration in TourGenerator, each + * tested in their own files. This covers the element-context sanitization the + * controller still owns, plus the endpoint surface. + * * @package AdminCoachTours */ @@ -28,8 +33,6 @@ protected function setUp(): void { Functions\stubTranslationFunctions(); Functions\stubEscapeFunctions(); - - // Mock apply_filters to return the first argument. Functions\when( 'apply_filters' )->returnArg(); } @@ -42,48 +45,17 @@ protected function tearDown(): void { } /** - * Test generate_draft returns error when AI not available. + * Test the REST endpoint surface exists. */ - public function test_generate_draft_returns_error_when_ai_unavailable(): void { - // Create mock request. - $request = $this->createMock( \WP_REST_Request::class); - $request->method( 'get_param' ) - ->willReturnCallback( - function ( $param ) { - if ( 'elementContext' === $param ) { - return [ - 'tagName' => 'button', - 'role' => 'button', - ]; - } - return null; - } - ); - - // Mock AiManager to return not available. - $ai_manager = $this->getMockBuilder( \stdClass::class) - ->addMethods( [ 'is_available' ] ) - ->getMock(); - $ai_manager->method( 'is_available' )->willReturn( false ); - - // This test demonstrates the expected behavior. - // In a real scenario, we'd mock the singleton properly. - $this->assertTrue( true ); - } - - /** - * Test get_status returns proper structure. - */ - public function test_get_status_returns_structure(): void { - Functions\when( 'rest_ensure_response' )->returnArg(); - - // This would need proper mocking of AiManager. - // For now, verify the method exists and is callable. + public function test_endpoint_methods_exist(): void { + $this->assertTrue( method_exists( AiController::class, 'generate_draft' ) ); $this->assertTrue( method_exists( AiController::class, 'get_status' ) ); + $this->assertTrue( method_exists( AiController::class, 'get_tasks' ) ); + $this->assertTrue( method_exists( AiController::class, 'generate_tour' ) ); } /** - * Test sanitization of element context. + * Test sanitization of element context strips unsafe values. */ public function test_element_context_sanitization(): void { Functions\when( 'sanitize_key' )->alias( @@ -102,7 +74,6 @@ function ( $str ) { } ); - // Use reflection to test private sanitize method. $reflection = new \ReflectionClass( AiController::class); $method = $reflection->getMethod( 'sanitize_element_context' ); $method->setAccessible( true ); @@ -121,52 +92,6 @@ function ( $str ) { $this->assertStringNotContainsString( '