Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tracks Analytics on frontend #185

Merged
merged 87 commits into from
Nov 15, 2024
Merged
Changes from 1 commit
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
b563eab
➕ ADDS: function to check if analytics is enabled
mehmoodak Oct 2, 2024
77477a9
➕ ADDS: track_event function
mehmoodak Oct 2, 2024
c0cbce3
➕ ADDS: events for activation and deactivation
mehmoodak Oct 2, 2024
6f3c13d
➕ ADDS: remotedatablocks_data_sources_stats event
mehmoodak Oct 3, 2024
925d0af
➕ ADDS: remotedatablocks_data_sources events
mehmoodak Oct 3, 2024
8e6e700
🐛 FIX: track prefix
mehmoodak Oct 3, 2024
6be3f30
🔨 REFACTOR: change event name
mehmoodak Oct 3, 2024
d2d4f7b
💫 UPDATE: remotedatablocks_view_data_sources event to only track once…
mehmoodak Oct 3, 2024
7eb1273
💫 UPDATE: event name
mehmoodak Oct 3, 2024
62aba4f
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Oct 7, 2024
234455e
➕ ADDS: remotedatablocks_usage_stats event
mehmoodak Oct 7, 2024
0221044
🔨 REFACTOR: move hooks tracking to Analytics class
mehmoodak Oct 8, 2024
531dbe1
🔨 REFACTOR: class name from Tracks to TracksAnalytics
mehmoodak Oct 9, 2024
99b3877
🔨 REFACTOR: remove is_enabled function from TracksAnalytics
mehmoodak Oct 9, 2024
d1457c7
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Oct 9, 2024
61f51b8
➕ ADDS: tests for get_service method
mehmoodak Oct 9, 2024
ba6957c
➕ ADDS: tests for get_data_source method
mehmoodak Oct 9, 2024
3100fda
move tracks code to functions for easy mocking
mehmoodak Oct 9, 2024
1f9432d
update property $tracks to $instance
mehmoodak Oct 9, 2024
7071a58
update TracksAnalytics to remove static properties and functions
mehmoodak Oct 9, 2024
13c1f1a
➕ ADDS: tests for TracksAnalyticsTest
mehmoodak Oct 9, 2024
89cef98
➕ ADDS: few tests for TracksAnalytics
mehmoodak Oct 9, 2024
311fcfa
🐛 FIX: tests
mehmoodak Oct 9, 2024
6989cda
🐛 FIX: psalm error
mehmoodak Oct 9, 2024
a3a8655
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Oct 10, 2024
0a7f0a9
➕ ADDS: tests for TracksAnalyticsTest
mehmoodak Oct 10, 2024
54c7b56
➖ REMOVE: full stop from comments
mehmoodak Oct 10, 2024
4f7d186
👌 IMPROVE: comments
mehmoodak Oct 10, 2024
d8e09f2
💫 UPDATE: methods to static
mehmoodak Oct 11, 2024
e7f40a1
Remove duplicate from psalm.xml
mehmoodak Oct 15, 2024
c62c3b1
Use nullish coalescing operator
mehmoodak Oct 15, 2024
f0019f2
Add apply filters global
chriszarate Oct 17, 2024
5143f25
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Oct 22, 2024
9263d92
💫 UPDATE: tests to work properly with static methods
mehmoodak Oct 23, 2024
2cc53db
ADDS: @automattic/calypso-analytics package
mehmoodak Oct 28, 2024
83cf6c5
sendTracksEvent util function
mehmoodak Oct 28, 2024
5a00b8a
➕ ADDS: track events for Field Shortcode
mehmoodak Oct 28, 2024
3719154
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Oct 28, 2024
f4b185b
Merge branch 'cafe-942/add-tracks-analytics' into cafe-942/add-tracks…
mehmoodak Oct 28, 2024
24fecd5
💫 UPDATE: sendTracksEvent to not track on local
mehmoodak Oct 28, 2024
b055612
➕ ADDS: track actions on remote data container
mehmoodak Oct 29, 2024
33e95a4
➕ ADDS: track actions for block binding controls
mehmoodak Oct 29, 2024
ef1a26c
➕ ADDS: data source in REMOTE_DATA_BLOCKS
mehmoodak Oct 29, 2024
e806a10
➕ ADDS: track events while adding block
mehmoodak Oct 29, 2024
b926032
➕ ADDS: track event for override
mehmoodak Oct 30, 2024
ee3c4d7
➕ ADDS: track event while associating pattern to block type
mehmoodak Oct 30, 2024
566e209
➕ ADDS: base props for tracks
mehmoodak Oct 30, 2024
6cdc456
➕ ADDS: avoid tracking events on local env
mehmoodak Oct 30, 2024
78c5cd5
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Oct 30, 2024
e6ed105
Merge branch cafe-942/add-tracks-analytics into cafe-942/add-tracks-a…
mehmoodak Oct 30, 2024
a1bbcbc
🐛 FIX: linting
mehmoodak Oct 30, 2024
5ca72ca
💫 UPDATE: variable name
mehmoodak Oct 31, 2024
ee5aafc
👌 IMPROVE: comments
mehmoodak Oct 31, 2024
438b06d
👌 IMPROVE: naming
mehmoodak Oct 31, 2024
53e124e
👌 IMPROVE: additional track props on DataSourceController
mehmoodak Oct 31, 2024
83afe4b
🐛 FIX: tracking of plugin activation and deactivation
mehmoodak Oct 31, 2024
d8c1207
Merge branch cafe-942/add-tracks-analytics into cafe-942/add-tracks-a…
mehmoodak Oct 31, 2024
1b786c4
Add tests for tracks.ts
mehmoodak Nov 1, 2024
0afdfba
fix types
mehmoodak Nov 1, 2024
16dab97
👌 IMPROVE: RDB tracks props
mehmoodak Nov 1, 2024
6862e3e
💫 UPDATE: remove props that are being sent by MU Plugins
mehmoodak Nov 1, 2024
143b701
Merge branch 'cafe-942/add-tracks-analytics' into cafe-942/add-tracks…
mehmoodak Nov 1, 2024
398d3e6
➖ REMOVE: extra data sources from interface and props
mehmoodak Nov 1, 2024
bf6a446
👌 IMPROVE: naming
mehmoodak Nov 1, 2024
497dcb8
➖ REMOVE: extra spacing
mehmoodak Nov 1, 2024
65bb09a
➕ ADDS: tests for remotedatablocks_add_block event
mehmoodak Nov 1, 2024
1574d6f
🐛 FIX: empty data source and improve action names
mehmoodak Nov 1, 2024
0dc8071
track event prop type
mehmoodak Nov 5, 2024
be36ae5
🔨 REFACTOR: move VipTracksBaseProps to tracks.d.ts
mehmoodak Nov 5, 2024
a3e42f9
🔨 REFACTOR: rename get_data_source to get_data_source_type
mehmoodak Nov 5, 2024
bf3289a
🔨 REFACTOR: update action name
mehmoodak Nov 5, 2024
a14dfce
👌 IMPROVE: types of tracks events
mehmoodak Nov 5, 2024
cd8bfc1
🔨 REFACTOR: rename onSaveClick function to onSelectItem
mehmoodak Nov 5, 2024
961d6c1
🔨 REFACTOR: rename tracks event data_source property to data_source_type
mehmoodak Nov 5, 2024
7ce75c6
➖ REMOVE: type checking from linting
mehmoodak Nov 5, 2024
3d1fdb7
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Nov 5, 2024
d35e0fe
Refactor EnvironmentConfig and TracksAnalytics classes
mehmoodak Nov 5, 2024
572f3da
Merge branch 'cafe-942/add-tracks-analytics' into cafe-942/add-tracks…
mehmoodak Nov 5, 2024
82bd6b1
🐛 FIX: undefined wp function errors
mehmoodak Nov 5, 2024
160b93d
Merge branch 'cafe-942/add-tracks-analytics' into cafe-942/add-tracks…
mehmoodak Nov 5, 2024
44a3a22
🔨 REFACTOR: For Tracks remove VIP_TRACKS_BASE_PROPS const and use the…
mehmoodak Nov 5, 2024
7b1513d
🐛 FIX: ignore psalm lint error
mehmoodak Nov 5, 2024
67f7e2d
Merge branch 'cafe-942/add-tracks-analytics' into cafe-942/add-tracks…
mehmoodak Nov 5, 2024
927f08c
Merge branch 'trunk' into cafe-942/add-tracks-analytics
mehmoodak Nov 6, 2024
d1b897a
Merge branch 'cafe-942/add-tracks-analytics' into cafe-942/add-tracks…
mehmoodak Nov 6, 2024
8765d05
Merge branch 'trunk' into cafe-942/add-tracks-analytics-on-js
mehmoodak Nov 15, 2024
8795f5b
💫 UPDATE: revert tsconfig change
mehmoodak Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
➕ ADDS: tests for TracksAnalyticsTest
mehmoodak committed Oct 10, 2024

Verified

This commit was signed with the committer’s verified signature.
mehmoodak Mehmood Ahmad
commit 0a7f0a95b9059e4ced4b18da745f3e57c1dc41d0
85 changes: 47 additions & 38 deletions inc/Analytics/TracksAnalytics.php
Original file line number Diff line number Diff line change
@@ -41,29 +41,14 @@ public function __construct() {
}
}

public function have_tracks_library(): bool {
return class_exists( 'Automattic\VIP\Telemetry\Tracks' );
}

public function is_enabled_via_filter(): bool {
return apply_filters( 'remote_data_blocks_enable_tracks_analytics', false ) ?? false;
}

/**
* Returns the Tracks library class.
*/
public function get_tracks_library(): ?string {
if ( ! $this->have_tracks_library() ) {
return null;
private function get_hosting_provider(): string {
if ( $this->is_wpvip_site() ) {
return 'wpvip';
}

/** @psalm-suppress UndefinedClass */
return Tracks::class;
return 'other';
}

/**
* Setup tracking via hooks.
*/
public function setup_tracking_via_hooks(): void {
// WordPress Dashboard Hooks.
add_action( 'activated_plugin', [ $this, 'track_plugin_activation' ] );
@@ -77,7 +62,7 @@ public function setup_tracking_via_hooks(): void {
* @param string $plugin_path Path of the plugin that was activated.
*/
public function track_plugin_activation( string $plugin_path ): void {
if ( plugin_basename( __FILE__ ) !== $plugin_path ) {
if ( ! $this->is_remote_data_blocks_plugin( $plugin_path ) ) {
return;
}

@@ -90,7 +75,7 @@ public function track_plugin_activation( string $plugin_path ): void {
* @param string $plugin_path Path of the plugin that was deactivated.
*/
public function track_plugin_deactivation( string $plugin_path ): void {
if ( plugin_basename( __FILE__ ) !== $plugin_path ) {
if ( ! $this->is_remote_data_blocks_plugin( $plugin_path ) ) {
return;
}

@@ -103,22 +88,27 @@ public function track_plugin_deactivation( string $plugin_path ): void {
* @param int $post_id Post ID.
* @param \WP_Post $post Post object.
*/
public function track_remote_data_blocks_usage( int $post_id, \WP_Post $post ): void {
public function track_remote_data_blocks_usage( int $post_id, object $post ): void {
// Ensure this is not an auto-save or revision.
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
if ( ! $this->should_track_blocks_usage( $post_id ) ) {
return;
}

$post_status = $post->post_status;
if ( 'publish' !== $post_status ) {
return;
}

// Regular expression to match all remote data blocks present in the post content.
$reg_exp = '/<!--\s{1}wp:remote-data-blocks\/([^\s]+)\s/';
preg_match_all( $reg_exp, $post->post_content, $matches );
if ( count( $matches ) === 0 ) {
if ( count( $matches[1] ) === 0 ) {
return;
}

// Get data source and track usage.
$track_props = [
'post_status' => $post->post_status,
'post_status' => $post_status,
'post_type' => $post->post_type,
];
foreach ( $matches[1] as $match ) {
@@ -143,37 +133,56 @@ public function track_remote_data_blocks_usage( int $post_id, \WP_Post $post ):
*
* @param string $event_name The name of the event.
* @param array $props The properties to send with the event.
*
* @return bool True if the event was recorded, false otherwise.
*/
public function record_event( string $event_name, array $props ): void {
public function record_event( string $event_name, array $props ): bool {
if ( ! isset( $this->instance ) ) {
return;
return false;
}

$this->instance->record_event( $event_name, $props );
return true;
}

public function have_tracks_library(): bool {
return class_exists( 'Automattic\VIP\Telemetry\Tracks' );
}

public function is_enabled_via_filter(): bool {
return apply_filters( 'remote_data_blocks_enable_tracks_analytics', false ) ?? false;
}

/**
* Check if the site is a WPVIP site.
* Check if the plugin is Remote Data Blocks.
*/
public function is_remote_data_blocks_plugin( string $plugin_path ): bool {
return plugin_basename( __FILE__ ) === $plugin_path;
}

public function get_tracks_library(): ?string {
if ( ! $this->have_tracks_library() ) {
return null;
}

/** @psalm-suppress UndefinedClass */
return Tracks::class;
}

public function is_wpvip_site(): bool {
return defined( 'WPCOM_IS_VIP_ENV' ) && constant( 'WPCOM_IS_VIP_ENV' ) === true
&& defined( 'WPCOM_SANDBOXED' ) && constant( 'WPCOM_SANDBOXED' ) === false;
}

/**
* Get the hosting provider.
*/
public function get_hosting_provider(): string {
if ( $this->is_wpvip_site() ) {
return 'wpvip';
public function should_track_blocks_usage( int $post_id ): bool {
// Ensure post is not an auto-save or revision.
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
return false;
}

return 'other';
return true;
}

/**
* Returns the tracks instance.
*/
public function get_instance(): ?object {
return isset( $this->instance ) ? $this->instance : null;
}
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
<stubs>
<file name="vendor/php-stubs/wordpress-globals/wordpress-globals.php"/>
<file name="vendor/php-stubs/wordpress-stubs/wordpress-stubs.php"/>
<file name="vendor/php-stubs/wordpress-stubs/wordpress-stubs.php"/>
<file name="remote-data-blocks.php"/>
</stubs>
</psalm>
163 changes: 148 additions & 15 deletions tests/inc/Analytics/TracksAnalyticsTest.php
Original file line number Diff line number Diff line change
@@ -5,13 +5,15 @@
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use RemoteDataBlocks\Analytics\TracksAnalytics;
use RemoteDataBlocks\Config\QueryContext\HttpQueryContext;
use RemoteDataBlocks\Editor\BlockManagement\ConfigStore;
use RemoteDataBlocks\ExampleApi\Queries\ExampleApiDataSource;
use RemoteDataBlocks\Integrations\Shopify\ShopifyDataSource;

// Define a dummy class if it doesn't exist
// Define a mock class for Tracks.
if ( ! class_exists( 'Automattic\VIP\Telemetry\Tracks' ) ) {
class MockTracks {
public function __construct( $prefix, $global_props ) {}

public function record_event( $name, $props ) {}
public function record_event( $_name, $_props ) {}
}
}

@@ -23,31 +25,162 @@ public function testInitDoesNotSetTracksIfLibraryIsNotPresent(): void {
}

public function testInitDoesNotSetTracksIfTrackingIsNotEnabled(): void {
/**
* @var TracksAnalytics|MockObject
*/
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->disableOriginalConstructor()->onlyMethods( [ 'have_tracks_library' ] )->getMock();
$mock->method( 'have_tracks_library' )->willReturn( true );

$mock->__construct();

$this->assertEquals( true, $mock->have_tracks_library() );
$this->assertEquals( null, $mock->get_instance() );
}

public function testInitDoesSetTracksIfTrackingIsEnabled(): void {
/**
* @var TracksAnalytics|MockObject
*/
$mock = $this->getMockBuilder( TracksAnalytics::class )->disableOriginalConstructor()->onlyMethods( [ 'have_tracks_library', 'is_enabled_via_filter', 'get_tracks_library' ] )->getMock();
public function testInitDoesSetTracksIfTrackingIsEnabledViaFilter(): void {
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->disableOriginalConstructor()->onlyMethods( [ 'have_tracks_library', 'is_enabled_via_filter', 'get_tracks_library', 'setup_tracking_via_hooks' ] )->getMock();
$mock->method( 'have_tracks_library' )->willReturn( true );
$mock->method( 'is_enabled_via_filter' )->willReturn( true );
$mock->method( 'get_tracks_library' )->willReturn( MockTracks::class );
$mock->expects( $this->once() )->method( 'setup_tracking_via_hooks' )->with();

$mock->__construct();

$this->assertEquals( true, $mock->have_tracks_library() );
$this->assertEquals( true, $mock->is_enabled_via_filter() );
$this->assertInstanceOf( MockTracks::class, $mock->get_instance() );
}

public function testInitDoesSetTracksIfTrackingIsEnabledOnVipSite(): void {
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->disableOriginalConstructor()->onlyMethods( [ 'have_tracks_library', 'is_wpvip_site', 'get_tracks_library', 'setup_tracking_via_hooks' ] )->getMock();
$mock->method( 'have_tracks_library' )->willReturn( true );
$mock->method( 'is_wpvip_site' )->willReturn( true );
$mock->method( 'get_tracks_library' )->willReturn( MockTracks::class );
$mock->expects( $this->exactly( 1 ) )->method( 'setup_tracking_via_hooks' )->with();

$mock->__construct();

$this->assertInstanceOf( MockTracks::class, $mock->get_instance() );
}

public function testGetHostingProviderReturnsWpvip(): void {
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->disableOriginalConstructor()->onlyMethods( [ 'is_wpvip_site' ] )->getMock();
$mock->method( 'is_wpvip_site' )->willReturn( true );

$method = get_private_method( TracksAnalytics::class, 'get_hosting_provider' );
$result = $method->invoke( $mock );

$this->assertEquals( 'wpvip', $result );
}

public function testGetHostingProviderReturnsOther(): void {
$obj = new TracksAnalytics();

$method = get_private_method( TracksAnalytics::class, 'get_hosting_provider' );
$result = $method->invoke( $obj );

$this->assertEquals( 'other', $result );
}

public function testTrackPluginActivationDoesNotRecordEventIfPluginIsNotRDB(): void {
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->onlyMethods( [ 'is_remote_data_blocks_plugin', 'record_event' ] )->getMock();
$mock->method( 'is_remote_data_blocks_plugin' )->with( 'plugin_path' )->willReturn( false );

$mock->expects( $this->exactly( 0 ) )->method( 'record_event' );
$mock->track_plugin_activation( 'plugin_path' );
}

public function testTrackPluginActivationDoesRecordEventIfPluginIsRDB(): void {
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->onlyMethods( [ 'is_remote_data_blocks_plugin', 'record_event' ] )->getMock();
$mock->method( 'is_remote_data_blocks_plugin' )->with( 'plugin_path' )->willReturn( true );

$mock->expects( $this->exactly( 1 ) )->method( 'record_event' )->with( 'remotedatablocks_plugin_toggle', [ 'action' => 'activate' ] );
$mock->track_plugin_activation( 'plugin_path' );
}

public function testTrackPluginDeactivationDoesNotRecordEventIfPluginIsNotRDB(): void {
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->onlyMethods( [ 'is_remote_data_blocks_plugin', 'record_event' ] )->getMock();
$mock->method( 'is_remote_data_blocks_plugin' )->with( 'plugin_path' )->willReturn( false );

$mock->expects( $this->exactly( 0 ) )->method( 'record_event' );
$mock->track_plugin_deactivation( 'plugin_path' );
}

public function testTrackPluginDeactivationDoesRecordEventIfPluginIsRDB(): void {
/** @var TracksAnalytics|MockObject */
$mock = $this->getMockBuilder( TracksAnalytics::class )->onlyMethods( [ 'is_remote_data_blocks_plugin', 'record_event' ] )->getMock();
$mock->method( 'is_remote_data_blocks_plugin' )->with( 'plugin_path' )->willReturn( true );

$mock->expects( $this->exactly( 1 ) )->method( 'record_event' )->with( 'remotedatablocks_plugin_toggle', [ 'action' => 'deactivate' ] );
$mock->track_plugin_deactivation( 'plugin_path' );
}

public function testRecordEventDoesNothingIfInstanceIsNotSet(): void {
/** @var TracksAnalytics|MockObject */
$obj = new TracksAnalytics();

$result = $obj->record_event( 'name', [] );

$this->assertEquals( false, $result );
}

public function testRecordEventTracksTheEventIfInstanceIsSet(): void {
$mock_tracks = $this->getMockBuilder( MockTracks::class )->onlyMethods( [ 'record_event' ] )->getMock();
$mock_tracks->expects( $this->exactly( 1 ) )->method( 'record_event' )->with( 'event_name', [ 'event_props' ] );
/** @var TracksAnalytics|MockObject */
$obj = new TracksAnalytics();
set_private_property( TracksAnalytics::class, $obj, 'instance', $mock_tracks );

$result = $obj->record_event( 'event_name', [ 'event_props' ] );

$this->assertEquals( true, $result );
}

public function testTrackRemoteDataBlocksUsageDoesNotTrackEventIfUsageShouldNotBeTracked(): void {
$mock = $this->getMockBuilder( TracksAnalytics::class )->onlyMethods( [ 'should_track_blocks_usage', 'record_event' ] )->getMock();
$mock->method( 'should_track_blocks_usage' )->willReturn( false );

$mock->expects( $this->exactly( 0 ) )->method( 'record_event' );
$mock->track_remote_data_blocks_usage( 1, (object) [] );
}

public function testTrackRemoteDataBlocksUsageDoesNotTrackEventIfPostStatusIsNotPublish(): void {
$mock = $this->getMockBuilder( TracksAnalytics::class )->onlyMethods( [ 'should_track_blocks_usage', 'record_event' ] )->getMock();
$mock->method( 'should_track_blocks_usage' )->willReturn( true );

$mock->expects( $this->exactly( 0 ) )->method( 'record_event' );
$mock->track_remote_data_blocks_usage( 1, (object) [ 'post_status' => 'draft' ] );
}

public function testTrackRemoteDataBlocksUsageDoesNotTrackEventIfPostContentHaveNoRemoteBlocks(): void {
$mock = $this->getMockBuilder( TracksAnalytics::class )->onlyMethods( [ 'should_track_blocks_usage', 'record_event' ] )->getMock();
$mock->method( 'should_track_blocks_usage' )->willReturn( true );
// Setup data sources.
ConfigStore::init();
ConfigStore::set_configuration( 'remote-data-blocks/shopify-vip-store', [
'queries' => [ new HttpQueryContext( ShopifyDataSource::create( 'access_token', 'name' ) ) ],
] );
ConfigStore::set_configuration( 'remote-data-blocks/conference-event', [
'queries' => [
new HttpQueryContext( ExampleApiDataSource::from_array( [
'slug' => 'example-api',
'service' => 'example_api',
] ) ),
],
] );

$mock->expects( $this->exactly( 1 ) )->method( 'record_event' )->with( 'remotedatablocks_blocks_usage_stats', [
'post_status' => 'publish',
'post_type' => 'post',
'shopify_data_source_count' => 2,
'remote_data_blocks_total_count' => 3,
'example_api_data_source_count' => 1,
] );
$mock->track_remote_data_blocks_usage( 1, (object) [
'post_type' => 'post',
'post_status' => 'publish',
'post_content' => '<!-- wp:remote-data-blocks/shopify-vip-store {"remoteData":{"blockName":"remote-data-blocks/shopify-vip-store","isCollection":false,"metadata":{"last_updated":{"name":"Last updated","type":"string","value":"2024-10-07 14:43:51"},"total_count":{"name":"Total count","type":"number","value":1}},"queryInput":{"id":"gid://shopify/Product/8642689958112"},"resultId":"d9583b2d-b79f-4af7-8adc-a723c53d7f67","results":[{"description":"Our floating shelf is the perfect way to store all your Tonal accessories. Its sleek, versatile design makes this an easy fit with any style room. Available in Coffee Oak (seen here), as well as Matte Black and Light Aged Ash.\nMade in the U.S.","title":"Tonal Accessories Shelf (Coffee Oak)","image_url":"https://cdn.shopify.com/s/files/1/0680/3456/0224/files/Coffee-Oak-with-products.webp?v=1721748682","image_alt_text":"","price":"$272.99","variant_id":"gid://shopify/ProductVariant/46081522368736"}]}} --> <div class="wp-block-remote-data-blocks-shopify-vip-store rdb-container"><!-- wp:group {"metadata":{"categories":["Remote Data Blocks"],"patternName":"remote-data-blocks/shopify-vip-store/pattern","name":"Shopify (vip-store) Data"},"layout":{"type":"constrained"}} --> <div class="wp-block-group"><!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"width":"33%"} --> <div class="wp-block-column" style="flex-basis:33%"><!-- wp:image {"metadata":{"bindings":{"alt":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"image_alt_text"}},"url":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"image_url"}}},"name":"Image URL"}} --> <figure class="wp-block-image"><img src="https://cdn.shopify.com/s/files/1/0680/3456/0224/files/Coffee-Oak-with-products.webp?v=1721748682" alt=""/></figure> <!-- /wp:image --></div> <!-- /wp:column --> <!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"title"}}},"name":"Title"}} --> <h2 class="wp-block-heading">Tonal Accessories Shelf (Coffee Oak)</h2> <!-- /wp:heading --> <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"description"}}},"name":"Product description"}} --> <p>Our floating shelf is the perfect way to store all your Tonal accessories. Its sleek, versatile design makes this an easy fit with any style room. Available in Coffee Oak (seen here), as well as Matte Black and Light Aged Ash. Made in the U.S.</p> <!-- /wp:paragraph --> <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"price"}}},"name":"Item price"}} --> <p>$272.99</p> <!-- /wp:paragraph --></div> <!-- /wp:column --></div> <!-- /wp:columns --></div> <!-- /wp:group --></div> <!-- /wp:remote-data-blocks/shopify-vip-store --> <!-- wp:remote-data-blocks/shopify-vip-store {"remoteData":{"blockName":"remote-data-blocks/shopify-vip-store","isCollection":false,"metadata":{"last_updated":{"name":"Last updated","type":"string","value":"2024-10-07 14:44:01"},"total_count":{"name":"Total count","type":"number","value":1}},"queryInput":{"id":"gid://shopify/Product/8642627207392"},"resultId":"ea7d7dae-4888-42c5-b64c-2b69ccbb958e","results":[{"description":"No detail is too small. Tonal’s proprietary T-Locks let you swap out Tonal accessories with a quick push and twist to lock everything in place.","title":"T-Locks (Pack of 4)","image_url":"https://cdn.shopify.com/s/files/1/0680/3456/0224/files/T-Locks.webp?v=1721746444","image_alt_text":"","price":"$42.99","variant_id":"gid://shopify/ProductVariant/46081259307232"}]}} --> <div class="wp-block-remote-data-blocks-shopify-vip-store rdb-container"><!-- wp:group {"metadata":{"categories":["Remote Data Blocks"],"patternName":"remote-data-blocks/shopify-vip-store/pattern","name":"Shopify (vip-store) Data"},"layout":{"type":"constrained"}} --> <div class="wp-block-group"><!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column {"width":"33%"} --> <div class="wp-block-column" style="flex-basis:33%"><!-- wp:image {"metadata":{"bindings":{"alt":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"image_alt_text"}},"url":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"image_url"}}},"name":"Image URL"}} --> <figure class="wp-block-image"><img src="https://cdn.shopify.com/s/files/1/0680/3456/0224/files/T-Locks.webp?v=1721746444" alt=""/></figure> <!-- /wp:image --></div> <!-- /wp:column --> <!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"title"}}},"name":"Title"}} --> <h2 class="wp-block-heading">T-Locks (Pack of 4)</h2> <!-- /wp:heading --> <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"description"}}},"name":"Product description"}} --> <p>No detail is too small. Tonal’s proprietary T-Locks let you swap out Tonal accessories with a quick push and twist to lock everything in place.</p> <!-- /wp:paragraph --> <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/shopify-vip-store","field":"price"}}},"name":"Item price"}} --> <p>$42.99</p> <!-- /wp:paragraph --></div> <!-- /wp:column --></div> <!-- /wp:columns --></div> <!-- /wp:group --></div> <!-- /wp:remote-data-blocks/shopify-vip-store --> <!-- wp:remote-data-blocks/conference-event {"remoteData":{"blockName":"remote-data-blocks/conference-event","isCollection":false,"metadata":{"last_updated":{"name":"Last updated","type":"string","value":"2024-10-07 14:44:10"},"total_count":{"name":"Total count","type":"number","value":1}},"queryInput":{"record_id":"rec1eYD2JFtevz39u"},"resultId":"268c2a0f-2322-4197-b915-e3cede1d1c83","results":[{"id":"rec1eYD2JFtevz39u","title":"Break","location":"Pearl room","type":"Panel"}]}} --> <div class="wp-block-remote-data-blocks-conference-event rdb-container"><!-- wp:heading {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/conference-event","field":"title"}}},"name":"Title"}} --> <h2 class="wp-block-heading">Break</h2> <!-- /wp:heading --> <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/conference-event","field":"location"}}},"name":"Location"}} --> <p>Pearl room</p> <!-- /wp:paragraph --> <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"remote-data/binding","args":{"block":"remote-data-blocks/conference-event","field":"type"}}},"name":"Type"}} --> <p>Panel</p> <!-- /wp:paragraph --></div> <!-- /wp:remote-data-blocks/conference-event -->',
] );
}
}
1 change: 1 addition & 0 deletions tests/inc/bootstrap.php
Original file line number Diff line number Diff line change
@@ -10,3 +10,4 @@
require_once __DIR__ . '/../../inc/Integrations/constants.php';
require_once __DIR__ . '/../../functions.php';
require_once __DIR__ . '/stubs.php';
require_once __DIR__ . '/test-utils.php';
61 changes: 61 additions & 0 deletions tests/inc/test-utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php declare(strict_types = 1);

/**
* Gets private property of a class.
*
* @param class-string $class_name Name of the class.
* @param string $property_name Name of the property.
* @return ReflectionProperty
*/
function get_private_property( string $class_name, string $property_name ): ReflectionProperty {
$reflector = new ReflectionClass( $class_name );
$property = $reflector->getProperty( $property_name );

/**
* @psalm-suppress UnusedMethodCall
*/
$property->setAccessible( true );

return $property;
}

/**
* Overrides the value of a private property on a given object. This is
* useful when mocking the internals of a class.
*
* Note that the property will no longer be private after setAccessible is
* called.
*
* @param class-string $class_name The fully qualified class name, including namespace.
* @param object $object_instance The object instance on which to set the value.
* @param string $property_name The name of the private property to override.
* @param mixed $value The value to set.
*/
function set_private_property(
string $class_name,
$object_instance,
string $property_name,
$value
): void {
$property = get_private_property( $class_name, $property_name );
$property->setValue( $object_instance, $value );
}

/**
* Gets private method of a class.
*
* @param class-string $class_name Name of the class.
* @param string $method Name of the method.
* @return ReflectionMethod
*/
function get_private_method( string $class_name, string $method ): ReflectionMethod {
$reflector = new ReflectionClass( $class_name );
$method = $reflector->getMethod( $method );

/**
* @psalm-suppress UnusedMethodCall
*/
$method->setAccessible( true );

return $method;
}