wp-graphql/wp-graphql

๐ŸŽฏ Tracking: contract test coverage for WPGraphQL actions and filters

ๅผ€ๆ”พ

#3,929 ๅˆ›ๅปบไบŽ 2026ๅนด6ๆœˆ12ๆ—ฅ

ย (0 ๆก่ฏ„่ฎบ)ย (0 ไธชๅๅบ”)ย (0 ไฝ่ดŸ่ดฃไบบ)PHPย (467 ไธชๆดพ็”Ÿ)github user discovery
effort: highhelp wantedimpact: medscope: testsstatus: actionabletype: chore

ไป“ๅบ“ๆŒ‡ๆ ‡

ๆ˜Ÿๆ ‡
ย (3,780 ไธชๆ˜Ÿๆ ‡)
PR ๅˆๅนถๆŒ‡ๆ ‡
ย (ๅนณๅ‡ๅˆๅนถ 2ๅคฉ 12ๅฐๆ—ถ)ย (30 ๅคฉๅ†…ๅˆๅนถ 114 ไธช PR)

ๆ่ฟฐ

Context

WPGraphQL core defines roughly 219 public hooks (apply_filters / do_action). These are public API, extensions and sites build on them, but most have no test coverage at all. A scan of the codebase (method below) found:

Bucket Count
Hooks defined in core 219
Hook name referenced somewhere in tests/ 59
No test references at all 154
Dynamic hook names (need manual verification) 6

The caveat on the 59: "referenced in tests" only means the hook name appears somewhere in the suite, sometimes that is a real contract test, sometimes it is incidental usage in another test. The 154 are definitive, nothing in the suite touches them.

This matters more with a 3.0 on the horizon. Hooks are where breaking changes hide. A contract test per hook means CI tells us when a filter or action changes behavior, signature, or timing, so we can document it properly instead of finding out from extension authors after release.

This consolidates #330 and #331 (both from 2017). Since those were opened, every hook in the codebase has gained a docblock (enforced by static analysis), and wpgraphql.com publishes action and filter references. Worth being transparent about the state of those reference pages though: they are manually maintained in WordPress (partly as a dogfooding exercise, WordPress is the CMS for wpgraphql.com), there is no automation generating them from the codebase yet, and the content has drifted compared to the code. Automating that generation from the docblocks has been explored locally and deserves its own issue, it is not in scope here. The test coverage goal of #330/#331 is this issue. The naming/consistency audit half of #331 is also out of scope, that is breaking-change territory and should be proposed per-hook against a major release.

Definition of done (per hook)

A wpunit test that hooks the filter/action and asserts the contract: the hook fires when expected, receives the documented args, and (for filters) the returned value affects behavior. See tests/wpunit/FiltersTest.php for the existing pattern. Checking an item off means a dedicated test exists, not just incidental usage.

How the scan was produced

cd plugins/wp-graphql
grep -rhoE "(do_action|apply_filters)\(\s*['\"][^'\"]+['\"]" src/ access-functions.php | \
  sed -E "s/\(\s*/|/; s/['\"]//g" | sort -u | grep -E "\|graph(i)?ql"
# then check each name for presence under tests/

Re-run it after chipping away to refresh the lists.

Untested hooks by subsystem (154)

plugin root (access-functions.php, activation.php, deactivation.php)

  • graphql_get_setting_section_field_value (filter) โ€” access-functions.php:882
  • graphql_get_setting_section_fields (filter) โ€” access-functions.php:863
  • graphql_activate (action) โ€” activation.php:6
  • graphql_deactivate (action) โ€” deactivation.php:16
  • graphql_delete_data (action) โ€” deactivation.php:57

src/Admin/Extensions

  • graphql_get_extensions (filter) โ€” src/Admin/Extensions/Extensions.php:402

src/Admin/GraphiQL

  • graphiql_external_fragments (filter) โ€” src/Admin/GraphiQL/GraphiQL.php:302
  • graphql_render_admin_page (filter) โ€” src/Admin/GraphiQL/GraphiQL.php:266

src/Admin/Settings

  • graphql_setting_field_config (filter) โ€” src/Admin/Settings/SettingsRegistry.php:147
  • graphql_settings_sections (filter) โ€” src/Admin/Settings/SettingsRegistry.php:185
  • graphql_init_settings (action) โ€” src/Admin/Settings/SettingsRegistry.php:178
  • graphql_settings_enqueue_scripts (action) โ€” src/Admin/Settings/SettingsRegistry.php:82
  • graphql_settings_form_bottom (action) โ€” src/Admin/Settings/SettingsRegistry.php:718
  • graphql_settings_form_top (action) โ€” src/Admin/Settings/SettingsRegistry.php:715

src/Admin

  • graphql_add_admin_notice (filter) โ€” src/Admin/AdminNotices.php:161
  • graphql_admin_notices_is_allowed_admin_page (filter) โ€” src/Admin/AdminNotices.php:387
  • graphql_enable_graphiql (filter) โ€” src/Admin/Admin.php:50
  • graphql_show_admin (filter) โ€” src/Admin/Admin.php:49
  • graphql_admin_notices_init (action) โ€” src/Admin/AdminNotices.php:99
  • graphql_admin_notices_render_notice (action) โ€” src/Admin/AdminNotices.php:309
  • graphql_admin_notices_render_notices (action) โ€” src/Admin/AdminNotices.php:309

src/Data/Connection

  • graphql_comment_connection_args (filter) โ€” src/Data/Connection/CommentConnectionResolver.php:238
  • graphql_comment_connection_query_args (filter) โ€” src/Data/Connection/CommentConnectionResolver.php:135
  • graphql_connection_amount_requested (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:629
  • graphql_connection_args (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:225
  • graphql_connection_edge (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:1014
  • graphql_connection_edges (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:1014
  • graphql_connection_ids (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:1102
  • graphql_connection_loader_name (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:559
  • graphql_connection_nodes (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:1004
  • graphql_connection_page_info (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:812
  • graphql_connection_pre_get_query (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:716
  • graphql_connection_pre_should_execute (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:887
  • graphql_connection_query_class (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:683
  • graphql_connection_should_execute (filter) โ€” src/Data/Connection/AbstractConnectionResolver.php:1065
  • graphql_map_input_fields_to_get_terms (filter) โ€” src/Data/Connection/TermObjectConnectionResolver.php:234
  • graphql_map_input_fields_to_wp_comment_query (filter) โ€” src/Data/Connection/CommentConnectionResolver.php:294
  • graphql_menu_item_connection_args (filter) โ€” src/Data/Connection/MenuItemConnectionResolver.php:107
  • graphql_post_object_connection_args (filter) โ€” src/Data/Connection/PostObjectConnectionResolver.php:588
  • graphql_term_object_connection_args (filter) โ€” src/Data/Connection/TermObjectConnectionResolver.php:282
  • graphql_term_object_connection_query_args (filter) โ€” src/Data/Connection/TermObjectConnectionResolver.php:140

src/Data/Cursor

  • graphql_post_object_cursor_meta_key (filter) โ€” src/Data/Cursor/PostObjectCursor.php:250
  • graphql_pre_comment_cursor_node (filter) โ€” src/Data/Cursor/CommentObjectCursor.php:64
  • graphql_pre_post_cursor_node (filter) โ€” src/Data/Cursor/PostObjectCursor.php:66
  • graphql_pre_term_cursor_node (filter) โ€” src/Data/Cursor/TermObjectCursor.php:48
  • graphql_pre_user_cursor_node (filter) โ€” src/Data/Cursor/UserCursor.php:83

src/Data/Loader

  • graphql_dataloader_get_model (filter) โ€” src/Data/Loader/AbstractDataLoader.php:377
  • graphql_dataloader_pre_get_model (filter) โ€” src/Data/Loader/AbstractDataLoader.php:354

src/Data

  • graphql_allowed_hosts (filter) โ€” src/Data/NodeResolver.php:386
  • graphql_allowed_settings_by_group (filter) โ€” src/Data/DataSource.php:388
  • graphql_comment_insert_post_args (filter) โ€” src/Data/CommentMutation.php:110
  • graphql_media_item_insert_post_args (filter) โ€” src/Data/MediaItemMutation.php:103
  • graphql_post_object_insert_post_args (filter) โ€” src/Data/PostObjectMutation.php:112
  • graphql_post_object_mutation_set_edit_lock (filter) โ€” src/Data/PostObjectMutation.php:150
  • graphql_post_object_mutations_allow_term_creation (filter) โ€” src/Data/PostObjectMutation.php:282
  • graphql_post_resolve_uri (filter) โ€” src/Data/NodeResolver.php:336
  • graphql_pre_resolve_uri (filter) โ€” src/Data/NodeResolver.php:123
  • graphql_pre_wp_comments_query_cursor_pagination_support (filter) โ€” src/Data/Config.php:465
  • graphql_pre_wp_query_cursor_pagination_stability (filter) โ€” src/Data/Config.php:184
  • graphql_pre_wp_query_cursor_pagination_support (filter) โ€” src/Data/Config.php:235
  • graphql_pre_wp_term_query_cursor_pagination_support (filter) โ€” src/Data/Config.php:390
  • graphql_pre_wp_user_query_cursor_pagination_stability (filter) โ€” src/Data/Config.php:287
  • graphql_pre_wp_user_query_cursor_pagination_support (filter) โ€” src/Data/Config.php:338
  • graphql_resolve_node_type (filter) โ€” src/Data/DataSource.php:552
  • graphql_resolve_uri_query_class (filter) โ€” src/Data/NodeResolver.php:170
  • graphql_resolve_uri (filter) โ€” src/Data/NodeResolver.php:170
  • graphql_term_object_insert_term_args (filter) โ€” src/Data/TermObjectMutation.php:108
  • graphql_user_insert_post_args (filter) โ€” src/Data/UserMutation.php:233
  • graphql_user_mutation_input_fields (filter) โ€” src/Data/UserMutation.php:134
  • graphql_comment_object_mutation_update_additional_data (action) โ€” src/Data/CommentMutation.php:134
  • graphql_media_item_mutation_update_additional_data (action) โ€” src/Data/MediaItemMutation.php:141
  • graphql_post_object_mutation_set_object_terms (action) โ€” src/Data/PostObjectMutation.php:241
  • graphql_user_object_mutation_update_additional_data (action) โ€” src/Data/UserMutation.php:266

src/Experimental

  • graphql_experimental_features_cap (filter) โ€” src/Experimental/ExperimentRegistry.php:112
  • graphql_experiments_loaded (action) โ€” src/Experimental/ExperimentRegistry.php:326

src/Model

  • graphql_allowed_fields_on_restricted_type (filter) โ€” src/Model/Model.php:321
  • graphql_html_entity_decoding_enabled (filter) โ€” src/Model/Model.php:524
  • graphql_model_field_capability (filter) โ€” src/Model/Model.php:450
  • graphql_model_prepare_fields (filter) โ€” src/Model/Model.php:499
  • graphql_object_visibility (filter) โ€” src/Model/Model.php:276
  • graphql_restricted_data_cap (filter) โ€” src/Model/Model.php:215
  • graphql_return_field_from_model (filter) โ€” src/Model/Model.php:420
  • graphql_after_return_field_from_model (action) โ€” src/Model/Model.php:373

src/Mutation

  • graphql_media_item_create_allowed_protocols (filter) โ€” src/Mutation/MediaItemCreate.php:230
  • graphql_post_object_create_default_post_status (filter) โ€” src/Mutation/PostObjectCreate.php:289
  • graphql_post_object_create_should_set_intended_post_status (filter) โ€” src/Mutation/PostObjectCreate.php:370
  • graphql_insert_post_object (action) โ€” src/Mutation/PostObjectUpdate.php:197
  • graphql_insert_term (action) โ€” src/Mutation/TermObjectCreate.php:197
  • graphql_update_term (action) โ€” src/Mutation/TermObjectUpdate.php:165

src/Registry

  • graphql_excluded_connections (filter) โ€” src/Registry/TypeRegistry.php:1840
  • graphql_excluded_mutations (filter) โ€” src/Registry/TypeRegistry.php:1866
  • graphql_excluded_types (filter) โ€” src/Registry/TypeRegistry.php:1813
  • graphql_introspection_keys (filter) โ€” src/Registry/TypeRegistry.php:916
  • graphql_type_registry (filter) โ€” src/Registry/TypeRegistry.php:286
  • graphql_register_initial_types (action) โ€” src/Registry/TypeRegistry.php:305

src/Server/ValidationRules

  • graphql_pre_restrict_endpoint (filter) โ€” src/Server/ValidationRules/RequireAuthentication.php:31
  • graphql_require_authentication_allowed_fields (filter) โ€” src/Server/ValidationRules/RequireAuthentication.php:71

src/Type

  • graphql_custom_scalar_config (filter) โ€” src/Type/WPScalar.php:51
  • graphql_interface_fields (filter) โ€” src/Type/WPInterfaceType.php:117
  • graphql_interface_resolve_type (filter) โ€” src/Type/WPInterfaceType.php:69
  • graphql_mutation_input (filter) โ€” src/Type/WPMutationType.php:216
  • graphql_mutation_payload (filter) โ€” src/Type/WPMutationType.php:246
  • graphql_pre_mutate_and_get_payload (filter) โ€” src/Type/WPMutationType.php:230
  • graphql_type_name (filter) โ€” src/Type/WPInterfaceType.php:50
  • graphql_union_possible_types (filter) โ€” src/Type/WPUnionType.php:68
  • graphql_union_resolve_type (filter) โ€” src/Type/WPUnionType.php:65
  • graphql_wp_interface_type_config (filter) โ€” src/Type/WPInterfaceType.php:78
  • graphql_wp_mutation_type_config (filter) โ€” src/Type/WPMutationType.php:88
  • graphql_wp_union_type_config (filter) โ€” src/Type/WPUnionType.php:77
  • graphql_mutation_response (action) โ€” src/Type/WPMutationType.php:259
  • graphql_wp_mutation_type (action) โ€” src/Type/WPMutationType.php:88
  • graphql_wp_union_type (action) โ€” src/Type/WPUnionType.php:77

src/Utils

  • graphql_cache_collection_get_list_types (filter) โ€” src/Utils/QueryAnalyzer.php:527
  • graphql_cache_collection_get_query_models (filter) โ€” src/Utils/QueryAnalyzer.php:698
  • graphql_cache_collection_get_query_types (filter) โ€” src/Utils/QueryAnalyzer.php:610
  • graphql_debug_log_entry (filter) โ€” src/Utils/DebugLog.php:101
  • graphql_debug_log (filter) โ€” src/Utils/DebugLog.php:42
  • graphql_debug_logs_enabled (filter) โ€” src/Utils/DebugLog.php:42
  • graphql_field_definition (filter) โ€” src/Utils/InstrumentSchema.php:69
  • graphql_field_resolver_auth_error_message (filter) โ€” src/Utils/InstrumentSchema.php:223
  • graphql_pre_query_analyzer_get_list_types (filter) โ€” src/Utils/QueryAnalyzer.php:444
  • graphql_pre_query_analyzer_get_models (filter) โ€” src/Utils/QueryAnalyzer.php:631
  • graphql_pre_query_analyzer_get_query_types (filter) โ€” src/Utils/QueryAnalyzer.php:548
  • graphql_query_analyzer_get_headers (filter) โ€” src/Utils/QueryAnalyzer.php:862
  • graphql_query_analyzer_get_runtime_nodes (filter) โ€” src/Utils/QueryAnalyzer.php:329
  • graphql_query_analyzer_header_length_limit (filter) โ€” src/Utils/QueryAnalyzer.php:241
  • graphql_query_analyzer_runtime_node (filter) โ€” src/Utils/QueryAnalyzer.php:723
  • graphql_query_id_hash_algorithm (filter) โ€” src/Utils/Utils.php:23
  • graphql_query_log_entry (filter) โ€” src/Utils/QueryLog.php:202
  • graphql_should_analyze_queries (filter) โ€” src/Utils/QueryAnalyzer.php:161
  • graphql_should_analyze_query (filter) โ€” src/Utils/QueryAnalyzer.php:200
  • graphql_should_show_query_analyzer_in_extensions (filter) โ€” src/Utils/QueryAnalyzer.php:887
  • graphql_tracing_response (filter) โ€” src/Utils/QueryLog.php:164
  • graphql_user_can_see_query_logs (filter) โ€” src/Utils/QueryLog.php:90
  • graphql_user_can_see_trace_data (filter) โ€” src/Utils/Tracing.php:357
  • graphql_determine_graphql_keys (action) โ€” src/Utils/QueryAnalyzer.php:298
  • graphql_get_debug_log (action) โ€” src/Utils/DebugLog.php:119

src root (Router, Request, WPGraphQL, AppContext, WPSchema)

  • graphql_cookie_auth_require_nonce (filter) โ€” src/Router.php:721
  • graphql_data_loader_classes (filter) โ€” src/AppContext.php:197
  • graphql_invalid_content_type_status_code (filter) โ€” src/Request.php:811
  • graphql_is_batch_queries_enabled (filter) โ€” src/Request.php:875
  • graphql_is_graphql_http_request (filter) โ€” src/Router.php:205
  • graphql_is_valid_http_content_type (filter) โ€” src/Request.php:779
  • graphql_response_headers_to_send (filter) โ€” src/Router.php:369
  • graphql_response_status_code (filter) โ€” src/Router.php:622
  • graphql_schema_config (filter) โ€” src/WPSchema.php:52
  • graphql_send_header (filter) โ€” src/Router.php:271
  • graphql_validation_rules (filter) โ€” src/Request.php:214
  • graphql_after_execute (action) โ€” src/Request.php:443
  • graphql_do_update_routine (action) โ€” src/WPGraphQL.php:498
  • graphql_execute_batch_queries (action) โ€” src/Request.php:321
  • graphql_get_schema (action) โ€” src/WPGraphQL.php:909
  • graphql_get_type_registry (action) โ€” src/WPGraphQL.php:960
  • graphql_server_config (action) โ€” src/Request.php:910

Dynamic hook names (6), verify per-variant coverage manually

  • graphql_insert_{$post_type_object->name} (action) โ€” src/Data/PostObjectMutation.php
  • graphql_insert_{$taxonomy->name} (action) โ€” src/Mutation/TermObjectCreate.php
  • graphql_update_{$taxonomy->name} (action) โ€” src/Mutation/TermObjectUpdate.php
  • graphql_{$lc_type_name}_fields (filter) โ€” src/Type/WPInputObjectType.php
  • graphql_{$uc_type_name}_fields (filter) โ€” src/Type/WPInputObjectType.php
  • graphql_wp_connection_{$this->from_type}_from_field_name (filter) โ€” src/Type/WPConnectionType.php

Hooks with existing test references (59), verify these are real contract tests over time

  • graphql_ (filter)
  • graphql_allowed_setting_groups (filter)
  • graphql_app_context_config (filter)
  • graphql_authentication_error_status_code (filter)
  • graphql_authentication_errors (filter)
  • graphql_connection (filter)
  • graphql_connection_default_query_amount (filter)
  • graphql_connection_is_valid_model (filter)
  • graphql_connection_max_query_amount (filter)
  • graphql_connection_query (filter)
  • graphql_connection_query_args (filter)
  • graphql_data_is_private (filter)
  • graphql_debug_enabled (filter)
  • graphql_endpoint (filter)
  • graphql_enum_values (filter)
  • graphql_experimental_features_override (filter)
  • graphql_experiments_registered_classes (filter)
  • graphql_get_dependents (filter)
  • graphql_get_possible_dependents (filter)
  • graphql_get_type (filter)
  • graphql_input_fields (filter)
  • graphql_map_input_fields_to_wp_query (filter)
  • graphql_map_input_fields_to_wp_user_query (filter)
  • graphql_object_fields (filter)
  • graphql_post_entities_allowed_post_types (filter)
  • graphql_post_object_connection_query_args (filter)
  • graphql_pre_format_name (filter)
  • graphql_pre_is_graphql_http_request (filter)
  • graphql_pre_model_data_is_private (filter)
  • graphql_pre_resolve_field (filter)
  • graphql_pre_resolve_menu_item_connected_node (filter)
  • graphql_pre_return_field_from_model (filter)
  • graphql_request_data (filter)
  • graphql_request_results (filter)
  • graphql_resolve_field (filter)
  • graphql_resolve_revision_meta_from_parent (filter)
  • graphql_root_value (filter)
  • graphql_schema (filter)
  • graphql_send_nocache_headers (filter)
  • graphql_term_entities_allowed_taxonomies (filter)
  • graphql_type_interfaces (filter)
  • graphql_wp_connection_type_config (filter)
  • graphql_wp_object_type_config (filter)
  • graphql_after_resolve_field (action)
  • graphql_before_execute (action)
  • graphql_before_resolve_field (action)
  • graphql_execute (action)
  • graphql_experiments_registered (action)
  • graphql_init (action)
  • graphql_post_object_mutation_update_additional_data (action)
  • graphql_process_http_request (action)
  • graphql_process_http_request_response (action)
  • graphql_register_settings (action)
  • graphql_register_types (action)
  • graphql_register_types_late (action)
  • graphql_response_set_headers (action)
  • graphql_return_response (action)
  • graphql_wp_connection_type (action)
  • graphql_wp_object_type (action)

่ดก็Œฎ่€…ๆŒ‡ๅ—