Add integration tests for The Events Calendar #31

Merged
linos merged 17 commits from improve_tests into main 2024-09-24 16:24:32 +02:00
10 changed files with 335 additions and 55 deletions

View file

@ -4,6 +4,7 @@ on:
push:
branches:
- main
- improve_tests
pull_request:
env:
@ -36,7 +37,7 @@ jobs:
path: |
${{ env.WP_CORE_DIR }}
${{ env.WP_TESTS_DIR }}
key: cache-wordpress-1
key: cache-wordpress-3
- name: Cache Composer
id: cache-composer-phpunit
@ -67,11 +68,11 @@ jobs:
- name: Setup Test Environment
if: steps.cache-wordpress.outputs.cache-hit != 'true'
run: bash tests/install-wp-tests.sh wordpress_test root root 127.0.0.1 6.6 false false false false
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 6.6 false false false false
- name: Initialize WordPress test database
if: steps.cache-wordpress.outputs.cache-hit != 'false'
run: bash tests/install-wp-tests.sh wordpress_test root root 127.0.0.1 6.6 false true true true
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 6.6 false true true true
- name: Run PHPUnit
run: cd /workspace/Event-Federation/wordpress-activitypub-event-extensions/ && ./vendor/bin/phpunit

View file

@ -11,7 +11,18 @@ RUN apk update \
RUN docker-php-ext-install mysqli
# Install Composer
# Install Xdebug
RUN apk add --no-cache $PHPIZE_DEPS \
&& apk add --update linux-headers \
&& pecl install xdebug \
&& docker-php-ext-enable xdebug \
&& apk del --purge $PHPIZE_DEPS \
&& echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo "xdebug.idekey=VSCODE" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
# Install Composer
RUN EXPECTED_CHECKSUM=$(curl -s https://composer.github.io/installer.sig) \
&& curl https://getcomposer.org/installer -o composer-setup.php \
&& ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" \

View file

@ -15,6 +15,16 @@ SKIP_WP_INSTALL=${7-false}
SKIP_PLUGINS_INSTALL=${8-false}
SKIP_TEST_SUITE_INSTALL=${9-false}
# Initialize the plugin list
PLUGINS=""
# Parse optional --plugins argument
while [[ "$#" -gt 0 ]]; do
case $1 in
--plugins) PLUGINS="$2"; shift ;;
esac
shift
done
TMPDIR=${TMPDIR-/tmp}
TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
@ -182,23 +192,50 @@ install_db() {
if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ]
then
echo "Reinstalling will delete the existing test database ($DB_NAME)"
read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB
recreate_db $DELETE_EXISTING_DB
recreate_db yes
else
create_db
fi
}
install_wp_plugin() {
PLUGIN_NAME=$1
mkdir -p "$WP_CORE_DIR/wp-content/plugins/"
if [ -d "$WP_CORE_DIR/wp-content/plugins/$PLUGIN_NAME" ]; then
return;
fi
# Get the latest tag.
LATEST_TAG=$(svn log https://plugins.svn.wordpress.org/$PLUGIN_NAME/tags --limit 1 | awk 'NR == 4 { print $4 }')
PLUGIN_FILE="$PLUGIN_NAME.$LATEST_TAG.zip"
URL="https://downloads.wordpress.org/plugin/$PLUGIN_FILE"
# Check if the plugin file already exists
if ! test -f "$TMPDIR/$PLUGIN_FILE"; then
download $URL "$TMPDIR/$PLUGIN_FILE"
fi
# Unzip the plugin into the WordPress must-use plugins directory
unzip -q -o "$TMPDIR/$PLUGIN_FILE" -d "$WP_CORE_DIR/wp-content/plugins/"
}
install_wp_plugins() {
if [ "$SKIP_PLUGINS_INSTALL" = "true" ]; then
echo "Skipping WordPress plugin installation."
return 0
fi
ACTIVITYPUB_FILE="activitypub.3.2.5.zip"
if ! test -f $TMPDIR/$ACTIVITYPUB_FILE; then
download https://downloads.wordpress.org/plugin/$ACTIVITYPUB_FILE $TMPDIR/$ACTIVITYPUB_FILE
fi
unzip -o $TMPDIR/$ACTIVITYPUB_FILE -d $WP_CORE_DIR/wp-content/plugins/
# Always install the ActivityPub plugin.
install_wp_plugin activitypub
install_wp_plugin the-events-calendar
# Install additional plugins.
# if [[ -n "$PLUGINS" ]]; then
# IFS=',' read -ra PLUGIN_ARRAY <<< "$PLUGINS"
# for plugin in "${PLUGIN_ARRAY[@]}"; do
# install_wp_plugin "$plugin"
# done
# fi
}
install_wp

View file

@ -43,7 +43,7 @@
],
"test": [
"composer install",
"tests/install-wp-tests.sh wordpress-test root wordpress-test test-db latest true",
"bin/install-wp-tests.sh wordpress-test root wordpress-test test-db latest true",
"phpunit"
]
}

View file

@ -4,6 +4,20 @@ version: '3'
# Install docker and docker compose and than just run:
# docker compose up
# To live debug in VSCode add this launch configuration to your .vscode/launch.json.
# It assumes that the WordPress root-dir is your workspace root.
#
# {
# "name": "Listen for PHPUnit",
# "type": "php",
# "request": "launch",
# "port": 9003,
# "pathMappings": {
# "/app/": "${workspaceRoot}/wp-content/plugins/activitypub-event-extensions/",
# "/tmp/wordpress/": "${workspaceRoot}/"
# },
# },
services:
test-db:
image: mariadb
@ -29,3 +43,5 @@ services:
volumes:
- .:/app
command: ["composer", "run-script", "test"]
extra_hosts:
- "host.docker.internal:host-gateway"

View file

@ -145,25 +145,52 @@ final class The_Events_Calendar extends Event {
* @return Place|array The place/venue if one is set.
*/
public function get_location(): Place|null {
if ( empty( $this->wp_object->venues ) || ! empty( $this->wp_object->venues[0] ) ) {
// Get short handle for the venues.
$venues = $this->wp_object->venues;
// Get first venue. We currently only support a single venue.
if ( $venues instanceof \Tribe\Events\Collections\Lazy_Post_Collection ) {
$venue = $venues->first();
} elseif ( empty( $this->wp_object->venues ) || ! empty( $this->wp_object->venues[0] ) ) {
return null;
} else {
$venue = $venues[0];
}
if ( ! $venue ) {
return null;
}
// We currently only support a single venue.
$event_venue = $this->wp_object->venues[0];
$address = array(
'addressCountry' => $event_venue->country,
'addressLocality' => $event_venue->city,
'addressRegion' => $event_venue->province,
'postalCode' => $event_venue->zip,
'streetAddress' => $event_venue->address,
'type' => 'PostalAddress',
);
// Set the address.
$address = array();
if ( ! empty( $venue->country ) ) {
$address['addressCountry'] = $venue->country;
}
if ( ! empty( $venue->city ) ) {
$address['addressLocality'] = $venue->city;
}
if ( ! empty( $venue->province ) ) {
$address['addressRegion'] = $venue->province;
}
if ( ! empty( $venue->zip ) ) {
$address['postalCode'] = $venue->zip;
}
if ( ! empty( $venue->address ) ) {
$address['streetAddress'] = $venue->address;
}
$address['type'] = 'PostalAddress';
$location = new Place();
$location->set_address( $address );
$location->set_id( $event_venue->permalink );
$location->set_name( $event_venue->post_name );
if ( count( $address ) > 1 ) {
$location->set_address( $address );
}
$location->set_id( $venue->permalink );
$location->set_name( $venue->post_title );
return $location;
}

View file

@ -59,14 +59,15 @@ class Setup {
* @since 1.0.0
*/
protected function __construct() {
$this->activitypub_plugin_is_active = is_plugin_active( 'activitypub/activitypub.php' );
$this->activitypub_plugin_is_active = defined( 'ACTIVITYPUB_PLUGIN_VERSION' ) ||
is_plugin_active( 'activitypub/activitypub.php' );
// BeforeFirstRelease: decide whether we want to do anything at all when ActivityPub plugin is note active.
// if ( ! $this->activitypub_plugin_is_active ) {
// deactivate_plugins( ACTIVITYPUB_EVENT_EXTENSIONS_PLUGIN_FILE );
// return;
// }.
$this->active_event_plugins = self::detect_active_event_plugins();
$this->activitypub_plugin_version = get_file_data( WP_PLUGIN_DIR . '/activitypub/activitypub.php', array( 'Version' ) )[0];
$this->activitypub_plugin_version = self::get_activitypub_plugin_version();
$this->setup_hooks();
}
@ -95,6 +96,19 @@ class Setup {
return self::$instance;
}
/**
* LooksUp the current version of the ActivityPub.
*
* @return string The semantic Version.
*/
private static function get_activitypub_plugin_version(): string {
if ( defined( 'ACTIVITYPUB_PLUGIN_VERSION' ) ) {
return constant( 'ACTIVITYPUB_PLUGIN_VERSION' );
}
$version = get_file_data( WP_PLUGIN_DIR . '/activitypub/activitypub.php', array( 'Version' ) )[0];
return $version ?? '0.0.0';
}
/**
* Getter function for the active event plugins.
*
@ -166,7 +180,7 @@ class Setup {
);
// Check if the minimum required version of the ActivityPub plugin is installed.
if ( version_compare( $this->activitypub_plugin_version, ACTIVITYPUB_EVENT_EXTENSIONS_ACTIVITYPUB_PLUGIN_MIN_VERSION ) ) {
if ( ! version_compare( $this->activitypub_plugin_version, ACTIVITYPUB_EVENT_EXTENSIONS_ACTIVITYPUB_PLUGIN_MIN_VERSION ) ) {
return;
}
@ -206,7 +220,7 @@ class Setup {
// The ActivityPub plugin is not active.
add_action( 'admin_notices', array( 'Activitypub_Event_Extensions\Admin\General_Admin_Notices', 'activitypub_plugin_not_enabled' ), 10, 1 );
}
if ( version_compare( $this->activitypub_plugin_version, ACTIVITYPUB_EVENT_EXTENSIONS_ACTIVITYPUB_PLUGIN_MIN_VERSION ) ) {
if ( ! version_compare( $this->activitypub_plugin_version, ACTIVITYPUB_EVENT_EXTENSIONS_ACTIVITYPUB_PLUGIN_MIN_VERSION ) ) {
// The ActivityPub plugin is too old.
add_action( 'admin_notices', array( 'Activitypub_Event_Extensions\Admin\General_Admin_Notices', 'activitypub_plugin_version_too_old' ), 10, 1 );
}
@ -234,9 +248,9 @@ class Setup {
// Get the transformer for a specific event plugins event-post type.
foreach ( $this->active_event_plugins as $event_plugin ) {
if ( $wp_object->post_type === $event_plugin->get_post_type() ) {
$transformer_class = $event_plugin->get_activitypub_event_transformer_class();
$transformer_class = $event_plugin::get_activitypub_event_transformer_class();
if ( class_exists( $transformer_class ) ) {
return new $transformer_class( $wp_object, $event_plugin->get_event_category_taxonomy() );
return new $transformer_class( $wp_object, $event_plugin::get_event_category_taxonomy() );
}
}
}
@ -256,8 +270,8 @@ class Setup {
// If someone installs this plugin, we simply enable ActivityPub support for all currently active event post types.
$activitypub_supported_post_types = get_option( 'activitypub_support_post_types', array() );
foreach ( $this->active_event_plugins as $event_plugin ) {
if ( ! in_array( $event_plugin['post_type'], $activitypub_supported_post_types, true ) ) {
$activitypub_supported_post_types[] = $event_plugin['post_type'];
if ( ! in_array( $event_plugin->get_post_type(), $activitypub_supported_post_types, true ) ) {
$activitypub_supported_post_types[] = $event_plugin->get_post_type();
}
}
update_option( 'activitypub_support_post_types', $activitypub_supported_post_types );

View file

@ -26,9 +26,21 @@ if ( ! file_exists( "{$_tests_dir}/includes/functions.php" ) ) {
require_once "{$_tests_dir}/includes/functions.php";
/**
* Manually load the plugin being tested.
* Manually load the plugin being tested and its integrations.
*/
function _manually_load_plugin() {
$plugin_dir = ABSPATH . '/wp-content/plugins/';
require_once $plugin_dir . 'activitypub/activitypub.php';
$event_plugin = 'the-events-calendar';
switch ( $event_plugin ) {
case 'the-events-calendar':
$plugin_file = 'the-events-calendar/the-events-calendar.php';
require_once $plugin_dir . $plugin_file;
$current = get_option( 'active_plugins', array() );
$current[] = $plugin_file;
sort( $current );
update_option( 'active_plugins', $current );
}
require dirname( __DIR__ ) . '/activitypub-event-extensions.php';
}

View file

@ -0,0 +1,182 @@
<?php
/**
* Class SampleTest
*
* @package Activitypub_Event_Extensions
*/
/**
* Sample test case.
*/
class Test_The_Events_Calendar extends WP_UnitTestCase {
/**
* Mockup events of certain complexity.
*/
public const MOCKUP_VENUS = array(
'minimal_venue' => array(
'venue' => 'Minimal Venue',
'status' => 'publish',
),
'complex_venue' => array(
'venue' => 'Complex Venue',
'status' => 'publish',
'show_map' => false,
'show_map_link' => false,
'address' => 'Venue address',
'city' => 'Venue city',
'country' => 'Venue country',
'province' => 'Venue province',
'state' => 'Venue state',
'stateprovince' => 'Venue stateprovince',
'zip' => 'Venue zip',
'phone' => 'Venue phone',
'website' => 'http://venue.com',
),
);
public const MOCKUP_EVENTS = array(
'minimal_event' => array(
'title' => 'My Event',
'content' => 'Come to my event. Let\'s connect!',
'start_date' => '+10 days 15:00:00',
'duration' => HOUR_IN_SECONDS,
'status' => 'publish',
),
'complex_event' => array(
'title' => 'My Event',
'content' => 'Come to my event. Let\'s connect!',
'start_date' => '+10 days 15:00:00',
'duration' => HOUR_IN_SECONDS,
'status' => 'publish',
),
);
/**
* Override the setup function, so that tests don't run if the Events Calendar is not active.
*/
public function set_up() {
parent::set_up();
if ( ! class_exists( '\Tribe__Events__Main' ) ) {
self::markTestSkipped( 'The Events Calendar plugin is not active.' );
}
// Make sure that ActivityPub support is enabled for The Events Calendar.
$aec = \Activitypub_Event_Extensions\Setup::get_instance();
$aec->activate_activitypub_support_for_active_event_plugins();
// Delete all posts afterwards.
_delete_all_posts();
}
/**
* Test that the right transformer gets applied.
*/
public function test_transformer_class() {
// We only test for one event plugin being active at the same time,
// even though we support multiple onces in theory.
// But testing all combinations is beyond scope.
$active_event_plugins = \Activitypub_Event_Extensions\Setup::get_instance()->get_active_event_plugins();
$this->assertEquals( 1, count( $active_event_plugins ) );
// Enable ActivityPub support for the event plugin.
$this->assertContains( 'tribe_events', get_option( 'activitypub_support_post_types' ) );
// Create a The Events Calendar Event without content.
$wp_object = tribe_events()
->set_args( self::MOCKUP_EVENTS['minimal_event'] )
->create();
// Call the transformer Factory.
$transformer = \Activitypub\Transformer\Factory::get_transformer( $wp_object );
// Check that we got the right transformer.
$this->assertInstanceOf( \Activitypub_Event_Extensions\Activitypub\Transformer\The_Events_Calendar::class, $transformer );
}
/**
* Test transformation of minimal event without venue.
*/
public function test_transform_of_minimal_event_without_venue() {
// Create a The Events Calendar Event.
$wp_object = tribe_events()
->set_args( self::MOCKUP_EVENTS['minimal_event'] )
->create();
// Call the transformer.
$event_array = \Activitypub\Transformer\Factory::get_transformer( $wp_object )->to_object()->to_array();
// Check that the event ActivityStreams representation contains everything as expected.
$this->assertEquals( 'Event', $event_array['type'] );
$this->assertEquals( 'My Event', $event_array['name'] );
$this->assertEquals( '', $event_array['content'] );
$this->assertEquals( gmdate( 'Y-m-d', strtotime( '+10 days 15:00:00' ) ) . 'T15:00:00Z', $event_array['startTime'] );
$this->assertEquals( gmdate( 'Y-m-d', strtotime( '+10 days 16:00:00' ) ) . 'T16:00:00Z', $event_array['endTime'] );
$this->assertTrue( $event_array['commentsEnabled'] );
$this->assertEquals( 'allow_all', $event_array['repliesModerationOption'] );
$this->assertEquals( 'free', $event_array['joinMode'] );
$this->assertArrayNotHasKey( 'location', $event_array );
$this->assertEquals( 'MEETING', $event_array['category'] );
}
/**
* Test transformation of event with mapped category.
*/
public function test_transform_event_with_mapped_categories() {
// Create category.
$category_id_music = wp_insert_term( 'Music', Tribe__Events__Main::TAXONOMY, array( 'slug' => 'music' ) );
$category_id_theatre = wp_insert_term( 'Theatre', Tribe__Events__Main::TAXONOMY, array( 'slug' => 'theatre' ) );
// Set default mapping for event categories.
update_option( 'activitypub_event_extensions_default_event_category', 'MUSIC' );
// Set an override for the category with the slug theatre.
update_option( 'activitypub_event_extensions_event_category_mappings', array( 'theatre' => 'THEATRE' ) );
// Create a The Events Calendar event with the music category.
$wp_object = tribe_events()
->set_args( self::MOCKUP_EVENTS['minimal_event'] )
->create();
// Set the post term music to the event.
wp_set_post_terms( $wp_object->ID, $category_id_music['term_id'], Tribe__Events__Main::TAXONOMY );
// Call the transformer.
$event_array = \Activitypub\Transformer\Factory::get_transformer( $wp_object )->to_object()->to_array();
// See if the default category mapping is applied.
$this->assertEquals( 'MUSIC', $event_array['category'] );
// Set the post term theatre to the event.
wp_set_post_terms( $wp_object->ID, $category_id_theatre['term_id'], Tribe__Events__Main::TAXONOMY );
// Call the transformer.
$event_array = \Activitypub\Transformer\Factory::get_transformer( $wp_object )->to_object()->to_array();
// See if the default category mapping is applied.
$this->assertEquals( 'THEATRE', $event_array['category'] );
}
/**
* Test transformation of minimal event with minimal venue.
*/
public function test_transform_of_minimal_event_with_venue() {
// Create Venue.
$venue = tribe_venues()->set_args( self::MOCKUP_VENUS['minimal_venue'] )->create();
// Create a The Events Calendar Event.
$wp_object = tribe_events()
->set_args( self::MOCKUP_EVENTS['complex_event'] )
->set( 'venue', $venue->ID )
->create();
// Call the transformer.
$event_array = \Activitypub\Transformer\Factory::get_transformer( $wp_object )->to_object()->to_array();
// Check that the event ActivityStreams representation contains everything as expected.
$this->assertEquals( 'Event', $event_array['type'] );
$this->assertEquals( 'My Event', $event_array['name'] );
$this->assertEquals( '', $event_array['content'] );
$this->assertEquals( gmdate( 'Y-m-d', strtotime( '+10 days 15:00:00' ) ) . 'T15:00:00Z', $event_array['startTime'] );
$this->assertEquals( gmdate( 'Y-m-d', strtotime( '+10 days 16:00:00' ) ) . 'T16:00:00Z', $event_array['endTime'] );
$this->assertEquals( gmdate( 'Y-m-d', strtotime( '+10 days 16:00:00' ) ) . 'T16:00:00Z', $event_array['commentsEnabled'] );
$this->assertEquals( gmdate( 'Y-m-d', strtotime( '+10 days 16:00:00' ) ) . 'T16:00:00Z', $event_array['endTime'] );
$this->assertArrayHasKey( 'location', $event_array );
$this->assertEquals( 'Place', $event_array['location']['type'] );
$this->assertEquals( self::MOCKUP_VENUS['minimal_venue']['venue'], $event_array['location']['name'] );
}
}

View file

@ -1,20 +0,0 @@
<?php
/**
* Class SampleTest
*
* @package Activitypub_Event_Extensions
*/
/**
* Sample test case.
*/
class Test_Sample extends WP_UnitTestCase {
/**
* A single example test.
*/
public function test_sample() {
// Replace this with some actual testing code.
$this->assertTrue( true );
}
}