mercredi 1 juin 2016

jetpack returns mobile on desktop

I'm writing a device detection script for WordPress and unit testing it. I can successfully detect mobile and tablet, but all user agent string return as mobile for desktop user agents.

Here is the device detection script:

 /**
 * Is Device - Adapter classes for JetPack device handling
 */
class IsDevice {

private $JUAI = '';

public function __construct( $juai = '' ) {
    if ( ! function_exists('jetpack_is_mobile') ) {
        return;
    }

    if ( ! empty( $juai ) ) {
        $this->JUAI = $juai;
    }

    if( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
        // Set a default user-agent so Jetpack_User_Agent_Info()
        // can be instantiated for use by get_device_type()
        $this->set_ua( 'mozilla/5.0 (android; tablet; rv:14.0) gecko/14.0 firefox/14.0' );
    }
    return 1;
}

public function get_device() {
    if( function_exists('jetpack_is_mobile') ) {
        return $this->get_device_type();
    }
    return ;
}

/**
 * Set User Agent - For Unit testing set the user agent
 * @param   string $ua - user agent string set for unit testing
 */
public function set_ua( $ua ) {
    $_SERVER['HTTP_USER_AGENT'] = $ua;
}

public function get_ua() {
    return $_SERVER['HTTP_USER_AGENT'];
}

private function jetpack_exists() {
    if ( function_exists('jetpack_is_mobile') ) {
        return true;
    }
}

private function mobile_check() {
    /*
     * Query the VIP available cached jetpack_is_mobile() function to see if mobile
     */
    if ( $this->jetpack_exists() ) {
        var_dump( jetpack_is_mobile() ) . $this->get_ua(). "\n";
        if ( true === jetpack_is_mobile() ) {
            return true;
        }
        return;
    }
    return;
}

private function tablet_check() {
    // jetpack_is_mobile will return FALSE for tablets; so we first check if it is false, then we run it 
    // against the is_tablet() method which calls internal tablet methods; if this returns tablet = true 
    // then we return tablet. As a final fail safe there are many generic tablet user_agents that are returned 
    // as mobiles or desktops, so the basic preg_match checks for a match of 'tablet' in the user agent string 
    // if it finds it then it will return 'tablet'

    if ( ! empty( $this->JUAI ) ) {
        if ( 
            true == $this->JUAI ->is_tablet() || 
            preg_match('/tablet/i', $this->get_ua() )
        ) {
            return true;
        }
        return;
    } else if ( class_exists('Jetpack_User_Agent_Info') ) {
        if( 
            Jetpack_User_Agent_Info::is_tablet() == TRUE || 
            preg_match('/tablet/i', $this->get_ua() ) 
        ) {
            return true;
        }
        return;
    }
    return;
}

protected function get_device_type() {
    if ( function_exists('jetpack_is_mobile') ) {

        if ( true == $this->tablet_check() ) {
            return 'tablet';
        }

        if ( true == $this->mobile_check() ) {
            return 'mobile';
        }

        // When reaching here, if jetpack_is_mobile returns FALSE and it is not a tablet then it is a desktop
        // So return 'desktop'
        // 
        return 'desktop';
    }
    // JetPack is not available so return null
    return;
}

}

And here are the unit tests:

require_once( dirname(__FILE__) . '/../../classes/IsDevice.php');

class IsDeviceTest extends WP_UnitTestCase {
public $IsDevice;

function setUp() {
    #setup code
    parent::setUp();

    require_once( dirname(__FILE__) . '/class.jetpack-user-agent.php' );
    $this->IsDevice = new IsDevice( new Jetpack_User_Agent_Info() );
}

/**
 * Create the data set for the data provider
 * Inner array pairs are user agent ( $ua ) and $expected value
 * @return collections array
 */
function mobile_user_agent_data_provider() {
    return array( 
            array( 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3', 'mobile' ),
            array( 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A366 Safari/600.1.4', 'mobile' ), 
            array( 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3', 'mobile' ),
            array( 'Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36', 'mobile' )
    );
}

/**
 * Create the data set for the data provider
 * Inner array pairs are user agent ( $ua ) and $expected value
 * @return collections array
 */
function tablet_user_agent_data_provider() {
    return array( 
            array( 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3; Tablet PC 2.0)', 'tablet' ),
            array( 'Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1', 'tablet' ), 
            array( 'Mozilla/5.0 (Linux; U; Android 4.2.2; nl-nl; GT-P5210 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30', 'tablet' ),
            array( 'Mozilla/5.0 (Linux; U; Android 4.2.2; en-gb; SM-T311 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30', 'tablet' ),
            array( 'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10', 'tablet' ),
            array( 'Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X; en-us) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3', 'tablet' )
    );
}

/**
 * Create the data set for the data provider
 * Inner array pairs are user agent ( $ua ) and $expected value
 * @return collections array
 */
function desktop_user_agent_data_provider() {
    return array( 
            array( 'Mozilla/5.0 (X11; Linux i686; rv:30.0) Gecko/20100101 Firefox/30.0', 'desktop' ),
    );
}

/**
 * @dataProvider mobile_user_agent_data_provider
 * pass the $ua and $expected values from the data providers data set
 */
function testGetDeviceTypeIsMobile( $ua, $expected ) {
    // mobile user agent 
    $this->IsDevice->set_ua( $ua );
    $this->assertEquals( $expected, $this->IsDevice->get_device() );
}

/**
 * @dataProvider tablet_user_agent_data_provider
 * pass the $ua and $expected values from the data providers data set
 */
function testGetDeviceTypeIsTablet( $ua, $expected ) {
    // tablet user agent 
    $this->IsDevice->set_ua( $ua );
    $this->assertEquals( $expected, $this->IsDevice->get_device() );
}

/**
 * @dataProvider desktop_user_agent_data_provider
 * pass the $ua and $expected values from the data providers data set
 */
function testGetDeviceTypeIsDesktop( $ua, $expected ) {

    // desktop user agent 
    $this->IsDevice->set_ua( $ua );
    $this->assertEquals( $expected, $this->IsDevice->get_device() );
}

function tearDown() {
    # tear down code
    parent::tearDown();
}

}

The passed in $juai is an instantiated Jetpack_User_Agent_info() class. This is required because the stripped down version of WP_UnitTestCase does not have have Jetpack installed; therefore in the test file's setUp() method the class.jetpack-user-agent.php is loaded and then passed into IsDevice( $juai ).

When the tests are run the lone desktop_user_agent_data_provider string ( which is a desktop user agent is jetpack_is_mobile() matched as true. I tried over 50 desktop user agent strings but they all return as 'mobile'. Here are the results of running the tests:

Configuration read from /tests/phpunit.xml

.bool(true) .bool(true) .bool(true) .bool(true) ......Fbool(true) ...........................................S........ 63 / 251 ( 25%) .......S....................................................... 126 / 251 ( 50%) S.............................................................. 189 / 251 ( 75%) ..............................................................

Time: 7.44 seconds, Memory: 29.25Mb

There was 1 failure:

1) IsDeviceTest::testGetDeviceTypeIsDesktop with data set #0 ('Mozilla/5.0 (X11; Linux i686; rv:30.0) Gecko/20100101 Firefox/30.0', 'desktop') Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'desktop' +'mobile'

/tests/custom_tests/test-is-device.php:85

FAILURES!
Tests: 251, Assertions: 786, Failures: 1, Skipped: 3.

I know that people have had this same trouble with Supercache enabled, however my WordPress bare bones unit test library has no caching enabled. As the tests are run on the command line a cache would not be a problem.

Can you spot a logical error in this implementation or do you have suggestions on how this could be fixed?

Regards, Steve

Aucun commentaire:

Enregistrer un commentaire