diff --git a/.github/workflows/pull_request.yml b/.github/workflows/phpcs_on_pull_request.yml similarity index 92% rename from .github/workflows/pull_request.yml rename to .github/workflows/phpcs_on_pull_request.yml index afbb492..8bb240a 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/phpcs_on_pull_request.yml @@ -11,6 +11,7 @@ jobs: - name: Run PHPCS inspection uses: docker://rtcamp/action-phpcs-code-review:v2.0.0 env: + SKIP_FOLDERS: "tests,.github" VAULT_ADDR: ${{ secrets.VAULT_ADDR }} VAULT_TOKEN: ${{ secrets.VAULT_TOKEN }} with: diff --git a/.github/workflows/phpunit_on_pull_request.yml b/.github/workflows/phpunit_on_pull_request.yml new file mode 100644 index 0000000..1e0af67 --- /dev/null +++ b/.github/workflows/phpunit_on_pull_request.yml @@ -0,0 +1,12 @@ +on: pull_request +name: PHPUnit +jobs: + runPHPCSInspection: + name: Run PHPUnit test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Run PHPUnit test + uses: docker://rtcamp/action-run-phpunit:v1.0.0 diff --git a/.gitignore b/.gitignore index b95261b..f83903a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ *.swp vendor node_modules -composer.lock .idea .DS_Store .vscode diff --git a/README.md b/README.md index 3c204fb..fbad3d4 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,15 @@ Once you've decided to commit the time to seeing your pull request through, plea 1. You need at least one approval and once your pull request has passed code review and tests, it will be merged into `develop` and be in the pipeline for the next release. +## Unit testing + +- Setup local unit test environment by running the below script from terminal with appropriate values. + - `./bin/install-wp-tests.sh [db-host] [wp-version] [skip-database-creation]` + + - E.g `./bin/install-wp-tests.sh amp_admanager_test root root localhost` +- Run `phpunit` in terminal from cloned project directory to run all test cases. +- Run `phpunit ./tests/classes/test-class.php` with file path to run specific tests. + ## Change Log ### v1.0.0 (27-09-2019) diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh new file mode 100755 index 0000000..5ceac4b --- /dev/null +++ b/bin/install-wp-tests.sh @@ -0,0 +1,155 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" + exit 1 +fi + +DB_NAME=$1 +DB_USER=$2 +DB_PASS=$3 +DB_HOST=${4-localhost} +WP_VERSION=${5-latest} +SKIP_DB_CREATE=${6-false} + +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} + +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then + WP_BRANCH=${WP_VERSION%\-*} + WP_TESTS_TAG="branches/$WP_BRANCH" + +elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then + WP_TESTS_TAG="branches/$WP_VERSION" +elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + WP_TESTS_TAG="tags/${WP_VERSION%??}" + else + WP_TESTS_TAG="tags/$WP_VERSION" + fi +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi +set -ex + +install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-nightly + download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip + unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ + mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + fi + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_test_suite() { + # portable in-place argument for both GNU sed and Mac OSX sed + if [[ $(uname -s) == 'Darwin' ]]; then + local ioption='-i.bak' + else + local ioption='-i' + fi + + # set up testing suite if it doesn't yet exist + if [ ! -d $WP_TESTS_DIR ]; then + # set up testing suite + mkdir -p $WP_TESTS_DIR + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data + fi + + if [ ! -f wp-tests-config.php ]; then + download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + # remove all forward slashes in the end + WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php + fi + +} + +install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_wp +install_test_suite +install_db diff --git a/classes/class-amp-admanager.php b/classes/class-amp-admanager.php index 622f6fa..0064611 100644 --- a/classes/class-amp-admanager.php +++ b/classes/class-amp-admanager.php @@ -60,10 +60,14 @@ public static function get_dfp_ad_targeting_data( $attr ) { if ( is_author() ) { + // todo: Check if username is available in queried object. + /** + * Ignoring code coverage for below lines because username member is not available in queried object. + */ if ( ! empty( $queried->data->username ) ) { - $dfp_ad_data['authorPage'] = $queried->data->username; + $dfp_ad_data['authorPage'] = $queried->data->username; // @codeCoverageIgnore } elseif ( ! empty( $queried->username ) ) { - $dfp_ad_data['authorPage'] = $queried->username; + $dfp_ad_data['authorPage'] = $queried->username; // @codeCoverageIgnore } } @@ -116,12 +120,34 @@ public static function get_dfp_ad_targeting_data( $attr ) { $final_ad_data = []; $final_ad_data['targeting'] = $dfp_ad_data; + if ( ! empty( $attr['custom-targeting'] ) ) { + + // Separate out all key values in array. + $custom_targeting = explode( ',', trim( $attr['custom-targeting'] ) ); + + if ( ! empty( $custom_targeting ) ) { + + foreach ( $custom_targeting as $value ) { + + // Separate out individual targeting key values as $key => $value pair. + $new_key_value = explode( ':', trim( $value ) ); + + if ( ! empty( $new_key_value ) ) { + $attr['targeting'][ trim( $new_key_value[0] ) ] = trim( $new_key_value[1] ); + } + } + } + } + if ( ! empty( $attr['targeting'] ) ) { $final_ad_data['targeting'] = array_unique( array_merge( $dfp_ad_data, $attr['targeting'] ) ); } /** - * amp_dfp_targeting_data filter to customize targeting variable. + * Filters the targeting attribute for the AMP AD. + * + * @param array $targeting An array of targeting attribute data. + * @param array $attr An array of get_ads() attributes. */ $final_ad_data['targeting'] = apply_filters( 'amp_dfp_targeting_data', $final_ad_data['targeting'], $attr ); @@ -288,9 +314,9 @@ private static function filter_breakpoints( $sizes ) { if ( 728 <= (int) $width ) { $breakpoints = self::set_max_height_and_width( 'desktop', $breakpoints, $width, $height ); } elseif ( 300 <= (int) $width && 600 >= (int) $width ) { - $breakpoints = self::set_max_height_and_width( 'tablet', $breakpoints, $width, $height ); + $breakpoints = self::set_max_height_and_width( 'tablet', $breakpoints, $width, $height ); if ( 350 >= (int) $width ) { - $breakpoints = self::set_max_height_and_width( 'mobile', $breakpoints, $width, $height ); + $breakpoints = self::set_max_height_and_width( 'mobile', $breakpoints, $width, $height ); } } } diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5316364 --- /dev/null +++ b/composer.json @@ -0,0 +1,31 @@ +{ + "name": "amp-admanager", + "description": "Amp Admanager Plugin", + "type": "wordpress-plugin", + "license": "proprietary", + "authors": [ + { + "name": "rtCamp", + "homepage": "https://rtcamp.com" + } + ], + "repositories":[ + { + "type":"composer", + "url":"https://wpackagist.org" + } + ], + "require": { + "composer/installers": "^1.6" + }, + "require-dev": { + "wpackagist-plugin/amp":"1.4.2" + }, + "extra": { + "installer-paths": { + "../amp/": [ + "wpackagist-plugin/amp" + ] + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..2eb2451 --- /dev/null +++ b/composer.lock @@ -0,0 +1,161 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "71c362461e9ae58cb24b6e2573619cd6", + "packages": [ + { + "name": "composer/installers", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "141b272484481432cda342727a427dc1e206bfa0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/141b272484481432cda342727a427dc1e206bfa0", + "reference": "141b272484481432cda342727a427dc1e206bfa0", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "replace": { + "roundcube/plugin-installer": "*", + "shama/baton": "*" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpunit/phpunit": "^4.8.36" + }, + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Craft", + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Thelia", + "Whmcs", + "WolfCMS", + "agl", + "aimeos", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "joomla", + "known", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "mediawiki", + "modulework", + "modx", + "moodle", + "osclass", + "phpbb", + "piwik", + "ppi", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "symfony", + "typo3", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "time": "2019-08-12T15:00:31+00:00" + } + ], + "packages-dev": [ + { + "name": "wpackagist-plugin/amp", + "version": "1.4.2", + "source": { + "type": "svn", + "url": "https://plugins.svn.wordpress.org/amp/", + "reference": "tags/1.4.2" + }, + "dist": { + "type": "zip", + "url": "https://downloads.wordpress.org/plugin/amp.1.4.2.zip", + "reference": null, + "shasum": null + }, + "require": { + "composer/installers": "~1.0" + }, + "type": "wordpress-plugin", + "homepage": "https://wordpress.org/plugins/amp/" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..dc66119 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,24 @@ + + + WordPress Coding Standards + + + + + + + + + + + + + + + + tests/* + .github/ + */dev-lib/* + */node_modules/* + */vendor/* + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..0bf7dc4 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,25 @@ + + + + ./tests/ + + + + + ./classes + + ./tests + + + + + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..4a1f6db --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,34 @@ +_instance = new Admin(); + } + + /** + * Tests class construct. + * + * @covers \AMP_AdManager\Admin::__construct + * + * @throws \ReflectionException Throws reflection exception. + * + * @return void + */ + public function test_construct() { + // Set demo data for settings. + update_option( 'amp-admanager-menu-settings', array( + 'dfp-network-id' => '123456789', + 'load-amp-resources' => '1', + ) ); + Utility::invoke_method( $this->_instance, '__construct' ); + $this->assertEquals( 10, has_action( 'admin_menu', [ $this->_instance, 'amp_admanager_menu' ] ) ); + $this->assertEquals( 10, has_action( 'admin_init', [ $this->_instance, 'amp_admanager_menu_init' ] ) ); + } + + /** + * Tests `amp_admanager_menu` function. + * + * @covers \AMP_AdManager\Admin::amp_admanager_menu + * + * @return void + */ + public function test_amp_admanager_menu() { + $current_user = get_current_user_id(); + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); + $this->_instance->amp_admanager_menu(); + $admin_page_url = home_url() . '/wp-admin/admin.php?page=amp-admanager-menu'; + $this->assertEquals( + $admin_page_url, + menu_page_url( 'amp-admanager-menu', false ), 'AMP AdManager Settings Page was not created' + ); + wp_set_current_user( $current_user ); + } + + /** + * Tests `amp_admanager_menu_init` function. + * + * @covers \AMP_AdManager\Admin::amp_admanager_menu_init + * + * @return void + */ + public function test_amp_admanager_menu_init() { + global $new_whitelist_options, $wp_settings_sections, $wp_settings_fields; + + $this->_instance->amp_admanager_menu_init(); + + $this->assertarrayHasKey( + 'amp-admanager-menu', + $new_whitelist_options, + 'Option Group amp-admanager-menu has not been created' + ); + + $settings = $new_whitelist_options['amp-admanager-menu']; + + $this->assertCount( 1, $settings, 'The Settings Group amp-admanager-menu 1 setting' ); + + $this->assertContains( 'amp-admanager-menu-settings', + $settings, + sprintf( 'Setting "%1$s" has not been created', 'amp-admanager-menu-settings' ) + ); + + $this->assertarrayHasKey( + 'amp-admanager-menu-page', + $wp_settings_sections, + 'amp-admanager-menu-page setting section has not been created' + ); + + $this->assertarrayHasKey( + 'amp-admanager-menu-page', + $wp_settings_fields, + 'amp-admanager-menu-page settings field has not been created' + ); + + $this->assertarrayHasKey( + 'amp-admanager-general-settings', + $wp_settings_fields['amp-admanager-menu-page'], + 'amp-admanager-menu-page types section settings fields has not been created' + ); + + $this->assertCount( + 2, + $wp_settings_fields['amp-admanager-menu-page']['amp-admanager-general-settings'], + 'There are less than 3 sections in the amp-admanager-menu-page settings field' + ); + + $settings_fields = array( + 'dfp-network-id', + 'load-amp-resources', + ); + + foreach ( $settings_fields as $setting_field ) { + $this->assertarrayHasKey( + $setting_field, + $wp_settings_fields['amp-admanager-menu-page']['amp-admanager-general-settings'] + ); + } + } + + /** + * Tests `amp_admanager_menu_html` function. + * + * @covers \AMP_AdManager\Admin::amp_admanager_menu_html + * + * @return void + */ + public function test_amp_admanager_menu_html() { + // Test access of user with privileges. + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); + $amp_ad_manager_settings_page = Utility::buffer_and_return( [ $this->_instance, 'amp_admanager_menu_html' ] ); + $this->assertContains( 'Global Settings', $amp_ad_manager_settings_page ); + + // Test access of user without privileges. + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'author' ) ) ); + $amp_ad_manager_settings_page = Utility::buffer_and_return( [ $this->_instance, 'amp_admanager_menu_html' ] ); + $this->assertContains( 'You do not have sufficient permissions to access this page.', $amp_ad_manager_settings_page ); + } + + /** + * Tests `get_checkbox_field` function. + * + * @covers \AMP_AdManager\Admin::get_checkbox_field + * + * @return void + */ + public function test_get_checkbox_field() { + $amp_ad_manager_checkbox_field = Utility::buffer_and_return( [ $this->_instance, 'get_checkbox_field' ] ); + $this->assertContains( '_instance, 'get_text_field' ] ); + $this->assertContains( '_instance = new AMP_AdManager(); + } + + /** + * Tests class constructor. + * + * @covers ::__construct + */ + public function test_construct() { + + Utility::invoke_method( $this->_instance, '__construct' ); + + $hooks = [ + [ + 'type' => 'action', + 'name' => 'wp_head', + 'priority' => 0, + 'function' => 'load_amp_resources', + ], + ]; + + // Check if hooks loaded. + foreach ( $hooks as $hook ) { + + $this->assertEquals( + $hook['priority'], + call_user_func( + sprintf( 'has_%s', $hook['type'] ), + $hook['name'], + [ + $this->_instance, + $hook['function'], + ] + ), + sprintf( 'AMP_AdManager::__construct() failed to register %1$s "%2$s" to %3$s()', $hook['type'], $hook['name'], $hook['function'] ) + ); + } + + } + + /** + * Tests `get_dfp_ad_targeting_data` function. + * + * @covers ::get_dfp_ad_targeting_data + * + * @return void + */ + public function test_get_dfp_ad_targeting_data() { + + if ( ! empty( $GLOBALS['wp_query'] ) ) { + $old_wp_query = $GLOBALS['wp_query']; + } + + $post_id = $this->factory->post->create( [ 'post_type' => 'post' ] ); + + $conditions = [ 'is_home' => true ]; + Utility::mock_wp_query( + [ + 'post_type' => 'post', + 'posts_per_page' => 1, + ], + $conditions + ); + + $attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '336x280', + 'targeting' => [ + 'contentType' => '', + 'siteDomain' => 'example.com', + 'adId' => 'AMP_ADTest', + ], + ]; + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $expected_output = [ + 'targeting' => [ + 'contentType' => '', + 'siteDomain' => 'example.com', + 'adId' => 'AMP_ADTest', + ], + ]; + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertEquals( $expected_output, $output ); + + // Test case for is_single() condition. + $conditions = [ + 'is_home' => false, + 'is_single' => true, + 'is_singular' => true, + ]; + Utility::mock_wp_query( + [ + 'post_type' => 'post', + 'posts_per_page' => 1, + ], + $conditions + ); + + $attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '336x280', + ]; + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'postCategories', $output['targeting'] ); + $this->assertArrayHasKey( 'postName', $output['targeting'] ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'post' ); + + // Test case for is_page() condition. + $this->factory->post->create( [ 'post_type' => 'page' ] ); + $conditions = [ + 'is_home' => false, + 'is_page' => true, + 'is_singular' => true, + ]; + Utility::mock_wp_query( + [ + 'post_type' => 'page', + 'posts_per_page' => 1, + ], + $conditions + ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'postName', $output['targeting'] ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'page' ); + + // Test case for is_archive() condition. + $conditions = [ 'is_archive' => true ]; + Utility::mock_wp_query( [ 'post_type' => 'page' ], $conditions ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'listingpage' ); + + // Test case for is_category() condition. + $term = $this->factory->category->create_and_get( + [ + 'name' => 'Parent', + 'slug' => 'parent', + 'parent' => 0, + ] + ); + $conditions = [ 'is_category' => true ]; + Utility::mock_wp_query( + [ + 'post_type' => 'page', + 'category_name' => 'Parent', + 'cat' => $term->term_id, + ], + $conditions + ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'categoryPage', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'listingpage' ); + $this->assertEquals( $output['targeting']['categoryPage'], 'parent' ); + + // Test case for is_author() condition. + $user_id = $this->factory->user->create( [ 'user_login' => 'testuser' ] ); + $conditions = [ + 'is_author' => true, + 'is_home' => false, + 'is_archive' => true, + ]; + Utility::mock_wp_query( + [ + 'post_type' => 'page', + 'author' => $user_id, + ], + $conditions + ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'listingpage' ); + + // Restore global wp_query. + if ( ! empty( $old_wp_query ) ) { + $GLOBALS['wp_query'] = $old_wp_query; + } + + $attr = [ + 'ad-unit' => 'Test_Ad', + 'custom-targeting' => 'siteDomain:test.com,test:test2', + ]; + + $expected = [ + 'targeting' => + [ + 'contentType' => '', + 'siteDomain' => 'test.com', + 'adId' => 'Test_Ad', + 'test' => 'test2', + ], + ]; + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertEquals( $expected, $output ); + } + + /** + * Tests `get_amp_ad` function. + * + * @covers ::get_amp_ad + * + * @return void + */ + public function test_get_amp_ad() { + + $expected_output = ''; + $attr = [ + 'ad-unit' => 'AMP_ADTest', + 'max' => 799, + 'min' => 500, + 'width' => 336, + 'height' => 280, + 'sizes' => '336x280', + ]; + $output = Utility::invoke_method( $this->_instance, 'get_amp_ad', [ $attr ] ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Test case for blank attributes. + $output = Utility::invoke_method( $this->_instance, 'get_amp_ad', [ [] ] ); + $this->assertEmpty( $output ); + + } + + /** + * Tests `get_ads` function. + * + * @covers ::get_ads + * + * @return void + */ + public function test_get_ads() { + + // Mobile Ads. + $expected_output = ''; + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'mobile-sizes' => '300x250', + 'layout' => 'fixed', + ]; + $output = AMP_AdManager::get_ads( $ad_attr ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Tablet Ads. + $expected_output = ''; + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'tablet-sizes' => '336x280', + 'layout' => 'fixed', + ]; + $output = AMP_AdManager::get_ads( $ad_attr ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Desktop ads. + $expected_output = ''; + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '970x250', + ]; + $output = AMP_AdManager::get_ads( $ad_attr ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Custom targeting. + $expected_output = ''; + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '970x250', + 'custom-targeting' => 'siteDomain:test.com,test:test2', + ]; + $output = AMP_AdManager::get_ads( $ad_attr ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Test echo. + $output_echo = Utility::buffer_and_return( 'AMP_AdManager\AMP_AdManager::get_ads', [ $ad_attr, true ] ); + + $this->assertNotEmpty( $output_echo ); + $this->assertEquals( $expected_output, $output_echo ); + + } + + /** + * Tests `filter_breakpoints` function. + * + * @covers ::filter_breakpoints + * + * @return void + */ + public function test_filter_breakpoints() { + + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '320x50', + 'layout' => 'fixed', + ]; + + $output = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $expected_output = [ + 'mobile' => [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50' ], + ], + 'tablet' => [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50' ], + ], + 'desktop' => [], + ]; + + // Check mobile sizes data. + $this->assertEquals( $expected_output, $output ); + + // Test for desktop size ad. + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '728x90', + 'layout' => 'fixed', + ]; + + $output = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $this->assertNotEmpty( $output ); + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $expected_output = [ + 'mobile' => [], + 'tablet' => [], + 'desktop' => [ + 'width' => '728', + 'height' => '90', + 'sizes' => [ '728x90' ], + ], + ]; + + // Check mobile sizes data. + $this->assertEquals( $expected_output, $output ); + + } + + /** + * Tests `set_max_height_and_width` function. + * + * @covers ::set_max_height_and_width + * + * @return void + */ + public function test_set_max_height_and_width() { + + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '320x50', + 'layout' => 'fixed', + ]; + $breakpoint = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $output = Utility::invoke_method( $this->_instance, 'set_max_height_and_width', [ 'mobile', $breakpoint, 300, 50 ] ); + + $this->assertNotEmpty( $output ); + + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $mobile_output = [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50', '300x50' ], + ]; + + // Check mobile sizes data. + $this->assertEquals( $mobile_output, $output['mobile'] ); + + } + + /** + * Tests `set_custom_sizes` function. + * + * @covers ::set_custom_sizes + * + * @return void + */ + public function test_set_custom_sizes() { + + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'desktop-sizes' => '970x250', + 'tablet-sizes' => '336x280', + 'mobile-sizes' => '300x250', + 'custom-targeting' => 'adPosition:1', + 'layout' => 'fixed', + ]; + $breakpoint = []; + + $output = Utility::invoke_method( $this->_instance, 'set_custom_sizes', [ $ad_attr, $breakpoint ] ); + + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $desktop_output = [ + 'width' => '970', + 'height' => '250', + 'sizes' => [ '970x250' ], + ]; + + // Check output data. + $this->assertEquals( $desktop_output, $output['desktop'] ); + + // Case with breakpoints. + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '320x50', + 'layout' => 'fixed', + ]; + $breakpoint = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $output = Utility::invoke_method( $this->_instance, 'set_custom_sizes', [ $ad_attr, $breakpoint ] ); + + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $mobile_output = [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50' ], + ]; + + // Check output data. + $this->assertEquals( $mobile_output, $output['mobile'] ); + + } + + /** + * Tests `get_slot_media_query` function. + * + * @covers ::get_slot_media_query + * + * @return void + */ + public function test_get_slot_media_query() { + + $expected_output = '(min-width: 200px) and (max-width: 50px)'; + $output = AMP_AdManager::get_slot_media_query( 200, 50 ); + + $this->assertEquals( $expected_output, $output ); + + $expected_output = '(min-width: 200px)'; + $output = AMP_AdManager::get_slot_media_query( 200, 0 ); + + $this->assertEquals( $expected_output, $output ); + + } + + /** + * Tests `load_amp_resources` function. + * + * @covers ::load_amp_resources + * + * @return void + */ + public function test_load_amp_resources() { + + $expected = ''; + + update_option( + 'amp-admanager-menu-settings', + [ + 'load-amp-resources' => '1', + 'dfp-network-id' => '', + ] + ); + + // Update settings after updating option. + AMP_AdManager::$amp_settings = get_option( 'amp-admanager-menu-settings' ); + $output = Utility::buffer_and_return( [ $this->_instance, 'load_amp_resources' ] ); + + $this->assertContains( $expected, $output ); + $this->assertContains( '