diff --git a/assets/build/css/settings.css b/assets/build/css/settings.css new file mode 100644 index 00000000..ff0f0d82 --- /dev/null +++ b/assets/build/css/settings.css @@ -0,0 +1 @@ +.one-tap-login-screen-row.hidden{display:none}.cookie-expiry-row input[type=number]{-moz-appearance:textfield}.cookie-expiry-row input[type=number]::-webkit-inner-spin-button,.cookie-expiry-row input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.cookie-expiry-row .cookie_expiry_settings_wrapper{align-items:center;display:flex;gap:8px}.cookie-expiry-row .cookie_expiry_settings_wrapper input{flex:0 1 200px}.cookie-expiry-row .human-readable-cookie-expiry{margin:0}.cookie-expiry-row .human-readable-cookie-expiry.hidden{display:none}.cookie-expiry-row .warning{color:red}.cookie-expiry-row .warning.hidden{display:none} diff --git a/assets/build/js/login.asset.php b/assets/build/js/login.asset.php index fb17492b..7055e6b5 100644 --- a/assets/build/js/login.asset.php +++ b/assets/build/js/login.asset.php @@ -1 +1 @@ - array(), 'version' => '57f26158d9cbe8f4'); + array(), 'version' => '7f0bbc0e8908f0a8'); diff --git a/assets/build/js/login.js b/assets/build/js/login.js index 37350ba4..c409a040 100644 --- a/assets/build/js/login.js +++ b/assets/build/js/login.js @@ -1 +1 @@ -(()=>{var o,e={525:()=>{({init:function(){document.addEventListener("DOMContentLoaded",this.onContentLoaded)},onContentLoaded:function(){this.form=document.getElementById("loginform")||document.getElementById("registerform"),document.querySelector(".wp_google_login")&&null===this.form&&(document.cookie="vip-go-cb=1;wp-login-with-google=1;path="+encodeURI(window.location.pathname)+";"),null!==this.form&&(this.googleLoginButton=this.form.querySelector(".wp_google_login"),this.googleLoginButton.classList.remove("hidden"),this.form.append(this.googleLoginButton))}}).init()},879:()=>{}},t={};function n(o){var r=t[o];if(void 0!==r)return r.exports;var i=t[o]={exports:{}};return e[o](i,i.exports,n),i.exports}n.m=e,o=[],n.O=(e,t,r,i)=>{if(!t){var l=1/0;for(s=0;s=i)&&Object.keys(n.O).every((o=>n.O[o](t[d])))?t.splice(d--,1):(a=!1,i0&&o[s-1][2]>i;s--)o[s]=o[s-1];o[s]=[t,r,i]},n.o=(o,e)=>Object.prototype.hasOwnProperty.call(o,e),(()=>{var o={112:0,229:0};n.O.j=e=>0===o[e];var e=(e,t)=>{var r,i,[l,a,d]=t,g=0;if(l.some((e=>0!==o[e]))){for(r in a)n.o(a,r)&&(n.m[r]=a[r]);if(d)var s=d(n)}for(e&&e(t);gn(525)));var r=n.O(void 0,[229],(()=>n(879)));r=n.O(r)})(); \ No newline at end of file +(()=>{var o,e={152:()=>{({init:function(){document.addEventListener("DOMContentLoaded",this.onContentLoaded)},onContentLoaded:function(){this.form=document.getElementById("loginform")||document.getElementById("registerform"),document.querySelector(".wp_google_login")&&null===this.form&&(document.cookie="vip-go-cb=1;wp-login-with-google=1;path="+encodeURI(window.location.pathname)+";"),null!==this.form&&(this.googleLoginButton=this.form.querySelector(".wp_google_login"),this.googleLoginButton.classList.remove("hidden"),this.form.append(this.googleLoginButton))}}).init()},879:()=>{},190:()=>{}},t={};function n(o){var r=t[o];if(void 0!==r)return r.exports;var i=t[o]={exports:{}};return e[o](i,i.exports,n),i.exports}n.m=e,o=[],n.O=(e,t,r,i)=>{if(!t){var l=1/0;for(s=0;s=i)&&Object.keys(n.O).every((o=>n.O[o](t[a])))?t.splice(a--,1):(d=!1,i0&&o[s-1][2]>i;s--)o[s]=o[s-1];o[s]=[t,r,i]},n.o=(o,e)=>Object.prototype.hasOwnProperty.call(o,e),(()=>{var o={112:0,892:0,229:0};n.O.j=e=>0===o[e];var e=(e,t)=>{var r,i,[l,d,a]=t,g=0;if(l.some((e=>0!==o[e]))){for(r in d)n.o(d,r)&&(n.m[r]=d[r]);if(a)var s=a(n)}for(e&&e(t);gn(152))),n.O(void 0,[892,229],(()=>n(879)));var r=n.O(void 0,[892,229],(()=>n(190)));r=n.O(r)})(); \ No newline at end of file diff --git a/assets/build/js/login.min.js b/assets/build/js/login.min.js new file mode 100644 index 00000000..745802e9 --- /dev/null +++ b/assets/build/js/login.min.js @@ -0,0 +1 @@ +(()=>{var o,e={525:()=>{({init:function(){document.addEventListener("DOMContentLoaded",this.onContentLoaded)},onContentLoaded:function(){this.form=document.getElementById("loginform")||document.getElementById("registerform"),document.querySelector(".wp_google_login")&&null===this.form&&(document.cookie="vip-go-cb=1;wp-login-with-google=1;path="+encodeURI(window.location.pathname)+";"),null!==this.form&&(this.googleLoginButton=this.form.querySelector(".wp_google_login"),this.googleLoginButton.classList.remove("hidden"),this.form.append(this.googleLoginButton))}}).init()},879:()=>{}},t={};function n(o){var r=t[o];if(void 0!==r)return r.exports;var i=t[o]={exports:{}};return e[o](i,i.exports,n),i.exports}n.m=e,o=[],n.O=(e,t,r,i)=>{if(!t){var l=1/0;for(s=0;s=i)&&Object.keys(n.O).every((o=>n.O[o](t[d])))?t.splice(d--,1):(a=!1,i0&&o[s-1][2]>i;s--)o[s]=o[s-1];o[s]=[t,r,i]},n.o=(o,e)=>Object.prototype.hasOwnProperty.call(o,e),(()=>{var o={112:0,229:0};n.O.j=e=>0===o[e];var e=(e,t)=>{var r,i,[l,a,d]=t,g=0;if(l.some((e=>0!==o[e]))){for(r in a)n.o(a,r)&&(n.m[r]=a[r]);if(d)var s=d(n)}for(e&&e(t);gn(525)));var r=n.O(void 0,[229],(()=>n(879)));r=n.O(r)})(); diff --git a/assets/build/js/settings.asset.php b/assets/build/js/settings.asset.php new file mode 100644 index 00000000..8660ea10 --- /dev/null +++ b/assets/build/js/settings.asset.php @@ -0,0 +1 @@ + array(), 'version' => 'ce4f6367e4a9bd69'); diff --git a/assets/build/js/settings.js b/assets/build/js/settings.js new file mode 100644 index 00000000..d3bd6fdc --- /dev/null +++ b/assets/build/js/settings.js @@ -0,0 +1 @@ +(()=>{var i=function(){n.init(),e.init()},e={init:function(){this.disallowNonNumbers=this.disallowNonNumbers.bind(this),this.addEventListeners=this.addEventListeners.bind(this),this.updateCookieWarning=this.updateCookieWarning.bind(this),this.cookieExpiryRow=document.querySelector(".cookie-expiry-row"),this.cookieExpiryRow&&(this.cookieExpiry=this.cookieExpiryRow.querySelector("#cookie-expiry"),this.cookieExpiry&&(this.cookieExpiryWarning=this.cookieExpiryRow.querySelector(".warning"),this.addEventListeners()))},addEventListeners:function(){this.cookieExpiry.addEventListener("input",this.updateCookieWarning),this.cookieExpiry.addEventListener("keypress",this.disallowNonNumbers)},disallowNonNumbers:function(i){1!==i.key.length||i.key.match(/[0-9]/)||i.preventDefault()},updateCookieWarning:function(){this.cookieExpiryWarning&&(parseInt(this.cookieExpiry.value)>14?this.cookieExpiryWarning.classList.remove("hidden"):this.cookieExpiryWarning.classList.add("hidden"))}},n={init:function(){this.setInitialState=this.setInitialState.bind(this),this.addEventListeners=this.addEventListeners.bind(this),this.toggleOneTapLoginScreenRow=this.toggleOneTapLoginScreenRow.bind(this),this.oneTapLoginCheckBox=document.querySelector("#one-tap-login"),this.oneTapLoginCheckBox&&(this.oneTapLoginScreenRow=document.querySelector(".one-tap-login-screen-row"),this.oneTapLoginScreenRow&&(this.addEventListeners(),this.setInitialState()))},addEventListeners:function(){this.oneTapLoginCheckBox.addEventListener("change",this.toggleOneTapLoginScreenRow)},setInitialState:function(){this.oneTapLoginCheckBox.checked?this.oneTapLoginScreenRow.classList.remove("hidden"):this.oneTapLoginScreenRow.classList.add("hidden")},toggleOneTapLoginScreenRow:function(){this.oneTapLoginScreenRow.classList.toggle("hidden")}};document.addEventListener("DOMContentLoaded",(function(){i()}))})(); \ No newline at end of file diff --git a/assets/mix-manifest.json b/assets/mix-manifest.json index 0a0abb81..14d634b4 100644 --- a/assets/mix-manifest.json +++ b/assets/mix-manifest.json @@ -1,8 +1,11 @@ { "/build/js/login.js": "/build/js/login.js", "/build/js/login.asset.php": "/build/js/login.asset.php", + "/build/js/settings.js": "/build/js/settings.js", + "/build/js/settings.asset.php": "/build/js/settings.asset.php", "/build/js/block-button.js": "/build/js/block-button.js", "/build/js/block-button.asset.php": "/build/js/block-button.asset.php", + "/build/css/settings.css": "/build/css/settings.css", "/build/css/login.css": "/build/css/login.css", "/build/images/google_light.png": "/build/images/google_light.png", "/build/js/onetap.js": "/build/js/onetap.js", diff --git a/assets/src/js/settings.js b/assets/src/js/settings.js new file mode 100644 index 00000000..0f95c65b --- /dev/null +++ b/assets/src/js/settings.js @@ -0,0 +1,159 @@ +/** + * JS for Settings page. + * + * @package login-with-google + */ + +/** + * Settings page JS. + */ +const WpGoogleSettings = { + + /** + * Init method. + * + * @return void + */ + init() { + OneTapLoginSettings.init(); + LoginCookieExpiry.init(); + }, +} + +const LoginCookieExpiry = { + /** + * Init method. + * + * @return void + */ + init() { + this.disallowNonNumbers = this.disallowNonNumbers.bind( this ); + this.addEventListeners = this.addEventListeners.bind( this ); + this.updateCookieWarning = this.updateCookieWarning.bind( this ); + + this.cookieExpiryRow = document.querySelector( '.cookie-expiry-row' ); + if ( ! this.cookieExpiryRow ) { + return; + } + + this.cookieExpiry = this.cookieExpiryRow.querySelector( '#cookie-expiry' ); + + if ( ! this.cookieExpiry ) { + return; + } + + this.cookieExpiryWarning = this.cookieExpiryRow.querySelector( '.warning' ); + + this.addEventListeners(); + }, + + /** + * Add event listeners. + * + * @return void + */ + addEventListeners() { + this.cookieExpiry.addEventListener( 'input', this.updateCookieWarning ); + this.cookieExpiry.addEventListener( 'keypress', this.disallowNonNumbers ); + }, + + /** + * Disallow anything else than numbers. + * + * @param {object} event + */ + disallowNonNumbers( event ) { + if ( event.key.length === 1 && ! event.key.match( /[0-9]/ ) ) { + event.preventDefault(); + } + }, + + /** + * Update cookie expiry warning. + * + * @return void + */ + updateCookieWarning() { + + if ( ! this.cookieExpiryWarning ) { + return; + } + + const days = parseInt( this.cookieExpiry.value ); + + if ( ( days > 14 ) ) { + this.cookieExpiryWarning.classList.remove( 'hidden' ); + } else { + this.cookieExpiryWarning.classList.add( 'hidden' ); + } + }, +} + +/** + * One Tap Login Settings. + */ +const OneTapLoginSettings = { + + /** + * Init method. + * + * @return void + */ + init() { + + this.setInitialState = this.setInitialState.bind( this ); + this.addEventListeners = this.addEventListeners.bind( this ); + this.toggleOneTapLoginScreenRow = this.toggleOneTapLoginScreenRow.bind( this ); + + this.oneTapLoginCheckBox = document.querySelector( '#one-tap-login' ); + + if ( ! this.oneTapLoginCheckBox ) { + return; + } + + this.oneTapLoginScreenRow = document.querySelector( '.one-tap-login-screen-row' ); + + if ( ! this.oneTapLoginScreenRow ) { + return; + } + + this.addEventListeners(); + this.setInitialState(); + + }, + + /** + * Add event listeners. + * + * @return void + */ + addEventListeners() { + this.oneTapLoginCheckBox.addEventListener( 'change', this.toggleOneTapLoginScreenRow ); + }, + + /** + * Set initial state. + * + * @return void + */ + setInitialState() { + if ( this.oneTapLoginCheckBox.checked ) { + this.oneTapLoginScreenRow.classList.remove( 'hidden' ); + } else { + this.oneTapLoginScreenRow.classList.add( 'hidden' ); + } + }, + + /** + * Toggle one tap login screen row. + * + * @return void + */ + toggleOneTapLoginScreenRow() { + this.oneTapLoginScreenRow.classList.toggle( 'hidden' ); + } +} + +document.addEventListener( 'DOMContentLoaded', () => { + WpGoogleSettings.init(); +} ); diff --git a/assets/src/scss/settings.scss b/assets/src/scss/settings.scss new file mode 100644 index 00000000..cb5ea670 --- /dev/null +++ b/assets/src/scss/settings.scss @@ -0,0 +1,42 @@ +.one-tap-login-screen-row.hidden { + display: none; +} + +.cookie-expiry-row { + + input[type=number] { + -moz-appearance: textfield; + + &::-webkit-inner-spin-button, + &::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } + } + + .cookie_expiry_settings_wrapper { + display: flex; + gap: 8px; + align-items: center; + + input { + flex: 0 1 200px; + } + } + + .human-readable-cookie-expiry { + margin: 0; + + &.hidden { + display: none; + } + } + + .warning { + color: #f00; + + &.hidden { + display: none; + } + } +} diff --git a/assets/webpack.mix.js b/assets/webpack.mix.js index 3234a6b3..5ee02db7 100644 --- a/assets/webpack.mix.js +++ b/assets/webpack.mix.js @@ -15,5 +15,7 @@ mix.copy( 'src/images', 'build/images' ) .copy( 'src/js/onetap.js', 'build/js' ) .minify( 'build/js/onetap.js' ) .js( 'src/js/login.js', 'build/js' ) + .js( 'src/js/settings.js', 'build/js' ) .block( 'src/js/block-button.js', 'build/js' ) - .sass( 'src/scss/login.scss', 'build/css' ); + .sass( 'src/scss/login.scss', 'build/css' ) + .sass( 'src/scss/settings.scss', 'build/css' ); diff --git a/src/Modules/Assets.php b/src/Modules/Assets.php index 6aaaf8b3..eaf3e1a9 100644 --- a/src/Modules/Assets.php +++ b/src/Modules/Assets.php @@ -30,6 +30,27 @@ class Assets implements ModuleInterface { */ const LOGIN_BUTTON_STYLE_HANDLE = 'login-with-google'; + /** + * Handle for Settings Page style. + * + * @var string + */ + const SETTINGS_PAGE_STYLE_HANDLE = 'login-with-google-settings'; + + /** + * Handle for Settings Page assets. + * + * @var string + */ + const SETTINGS_PAGE_SCRIPT_HANDLE = 'login-with-google-settings-script'; + + /** + * Settings page hook suffix. + * + * @var string + */ + const SETTINGS_PAGE_HOOK_SUFFIX = 'settings_page_login-with-google'; + /** * Module name. * @@ -46,6 +67,48 @@ public function name(): string { */ public function init(): void { add_action( 'login_enqueue_scripts', [ $this, 'enqueue_login_styles' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_styles' ] ); + } + + /** + * Enqueue styles for admin. + * + * @param string $hook_suffix Current page hook. + * + * @return void + */ + public function enqueue_admin_styles( string $hook_suffix ): void { + if ( 'settings_page_login-with-google' !== $hook_suffix ) { + return; + } + + if ( ! wp_style_is( self::SETTINGS_PAGE_STYLE_HANDLE, 'registered' ) ) { + $this->register_style( self::SETTINGS_PAGE_STYLE_HANDLE, 'build/css/settings.css' ); + } + + wp_enqueue_style( self::SETTINGS_PAGE_STYLE_HANDLE ); + } + + /** + * Enqueue scripts and styles for admin. + * + * @param string $hook_suffix Current page hook. + * + * @return void + */ + public function enqueue_admin_scripts( string $hook_suffix ): void { + if ( 'settings_page_login-with-google' !== $hook_suffix ) { + return; + } + + $filename = ( defined( 'WP_SCRIPT_DEBUG' ) && true === WP_SCRIPT_DEBUG ) ? 'settings.min.js' : 'settings.js'; + + if ( ! wp_script_is( self::SETTINGS_PAGE_SCRIPT_HANDLE, 'registered' ) ) { + $this->register_script( self::SETTINGS_PAGE_SCRIPT_HANDLE, 'build/js/' . $filename ); + } + + wp_enqueue_script( self::SETTINGS_PAGE_SCRIPT_HANDLE ); } /** diff --git a/src/Modules/OneTapLogin.php b/src/Modules/OneTapLogin.php index 9f455008..25090317 100644 --- a/src/Modules/OneTapLogin.php +++ b/src/Modules/OneTapLogin.php @@ -212,6 +212,10 @@ public function authenticate(): void { } $wp_user = $this->authenticator->authenticate( $user ); + + // Adding filter to modify cookie expiry for one tap login. + add_filter( 'auth_cookie_expiration', [ $this->settings, 'modify_cookie_expiry' ] ); $this->authenticator->set_auth_cookies( $wp_user ); + remove_filter( 'auth_cookie_expiration', [ $this->settings, 'modify_cookie_expiry' ] ); } } diff --git a/src/Modules/Settings.php b/src/Modules/Settings.php index f63e1637..a6d51447 100644 --- a/src/Modules/Settings.php +++ b/src/Modules/Settings.php @@ -14,6 +14,12 @@ use RtCamp\GoogleLogin\Interfaces\Module as ModuleInterface; +use RtCamp\GoogleLogin\Utils\Helper; + +use stdClass; + +use function RtCamp\GoogleLogin\plugin; + /** * Class Settings. * @@ -23,6 +29,7 @@ * @property bool|null registration_enabled * @property bool|null one_tap_login * @property string one_tap_login_screen + * @property int|null cookie_expiry * * @package RtCamp\GoogleLogin\Modules */ @@ -47,6 +54,7 @@ class Settings implements ModuleInterface { 'WP_GOOGLE_LOGIN_WHITELIST_DOMAINS' => 'whitelisted_domains', 'WP_GOOGLE_ONE_TAP_LOGIN' => 'one_tap_login', 'WP_GOOGLE_ONE_TAP_LOGIN_SCREEN' => 'one_tap_login_screen', + 'WP_GOOGLE_COOKIE_EXPIRY' => 'cookie_expiry', ]; /** @@ -82,6 +90,37 @@ public function init(): void { $this->options = get_option( 'wp_google_login_settings', [] ); add_action( 'admin_init', [ $this, 'register_settings' ] ); add_action( 'admin_menu', [ $this, 'settings_page' ] ); + add_filter( 'auth_cookie_expiration', [ $this, 'modify_cookie_expiry' ] ); + } + + /** + * Modify cookie expiry. + * + * @param int $expiration Current cookie expiry. + * + * @return int + */ + public function modify_cookie_expiry( int $expiration ): int { + $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); + + if ( ! $state ) { + return $expiration; + } + + $state = base64_decode( $state ); + $state = $state ? json_decode( $state ) : null; + + if ( ! ( $state instanceof stdClass ) || empty( $state->provider ) || 'google' !== $state->provider ) { + return $expiration; + } + + if ( ! is_numeric( $this->cookie_expiry ) ) { + return $expiration; + } + + $days = intval( $this->cookie_expiry ); + + return $days * DAY_IN_SECONDS; } /** @@ -133,7 +172,7 @@ function () { [ $this, 'one_tap_login' ], 'login-with-google', 'wp_google_login_section', - [ 'label_for' => 'one-tap-login' ] + [ 'label_for' => 'one-tap-login', ] ); add_settings_field( @@ -142,7 +181,10 @@ function () { [ $this, 'one_tap_login_screens' ], 'login-with-google', 'wp_google_login_section', - [ 'label_for' => 'one-tap-login-screen' ] + [ + 'label_for' => 'one-tap-login-screen', + 'class' => 'one-tap-login-screen-row', + ] ); add_settings_field( @@ -153,6 +195,60 @@ function () { 'wp_google_login_section', [ 'label_for' => 'whitelisted-domains' ] ); + + add_settings_field( + 'wp_google_cookie_expiry', + __( 'Auto logout after', 'login-with-google' ), + [ $this, 'cookie_expiry_field' ], + 'login-with-google', + 'wp_google_login_section', + [ + 'label_for' => 'cookie-expiry', + 'class' => 'cookie-expiry-row', + ] + ); + } + + /** + * Get Warning classes. + * + * @param int $days Days. + * + * @return string + */ + private function get_warning_classes( int $days ): string { + $warning_classes = 'warning'; + if ( empty( $days ) || $days < 14 ) { + $warning_classes .= ' hidden'; + } + + return $warning_classes; + } + + /** + * Render cookie expiry field. + * + * @return void + */ + public function cookie_expiry_field(): void { + + $days = ! is_numeric( $this->cookie_expiry ) ? '' : intval( $this->cookie_expiry ); + $warning_classes = $this->get_warning_classes( intval( $days ) ); + + ?> + +

+ +

+ +

+ +

+ - - { + test('should be able to set cookie expiry field', async ({ page, admin }) => { + await admin.visitAdminPage('/') + + await page.hover('role=link[name="Settings"i]') + + await page.click('role=link[name="Login with Google"i]') + + expect(page.locator("form[action='options.php'] h2")).toHaveText( + 'Log in with Google Settings' + ) + + await page.type( + '#cookie-expiry', + '320' + ) + + await page.waitForTimeout(1000) + + await page.click('#submit') + }) +}) diff --git a/tests/php/Unit/Modules/AssetsTest.php b/tests/php/Unit/Modules/AssetsTest.php index 4342eaca..f78de9b3 100644 --- a/tests/php/Unit/Modules/AssetsTest.php +++ b/tests/php/Unit/Modules/AssetsTest.php @@ -45,7 +45,23 @@ public function testInit() { 'login_enqueue_scripts', [ $this->testee, - 'enqueue_login_styles' + 'enqueue_login_styles', + ] + ); + + WP_Mock::expectActionAdded( + 'admin_enqueue_scripts', + [ + $this->testee, + 'enqueue_admin_scripts', + ] + ); + + WP_Mock::expectActionAdded( + 'admin_enqueue_scripts', + [ + $this->testee, + 'enqueue_admin_styles', ] ); @@ -89,6 +105,162 @@ function () { $this->assertConditionsMet(); } + /** + * @covers ::register_admin_styles + */ + public function testEnqueueAdminStylesWithStyleRegistered() { + $this->wpMockFunction( + 'wp_style_is', + [ + $this->testee::SETTINGS_PAGE_STYLE_HANDLE, + 'registered', + ], + 1, + true, + ); + + $this->wpMockFunction( + 'wp_enqueue_style', + [ + $this->testee::SETTINGS_PAGE_STYLE_HANDLE, + ], + ); + + $this->testee->enqueue_admin_styles( $this->testee::SETTINGS_PAGE_HOOK_SUFFIX ); + $this->assertConditionsMet(); + + } + + /** + * @covers ::register_admin_styles + */ + public function testEnqueueAdminStylesWithStyleNotRegistered() { + $this->wpMockFunction( + 'RtCamp\GoogleLogin\plugin', + [], + 2, + function () { + return (object) [ + 'url' => 'https://example.com/', + 'assets_dir' => 'https://example.com/assets', + ]; + } + ); + + $this->wpMockFunction( + 'wp_style_is', + [ + $this->testee::SETTINGS_PAGE_STYLE_HANDLE, + 'registered', + ], + 1, + false, + ); + + $this->wpMockFunction( + 'wp_register_style', + [ + $this->testee::SETTINGS_PAGE_STYLE_HANDLE, + 'https://example.com/assets/build/css/settings.css', + [], + false, + true, + ], + 1, + true, + ); + + $this->wpMockFunction( + 'wp_enqueue_style', + [ + $this->testee::SETTINGS_PAGE_STYLE_HANDLE, + ], + 1, + true, + ); + + $this->testee->enqueue_admin_styles( $this->testee::SETTINGS_PAGE_HOOK_SUFFIX ); + $this->assertConditionsMet(); + + } + + /** + * @covers ::enqueue_admin_scripts + */ + public function testEnqueueAdminScriptsWithScriptRegistered() { + $this->wpMockFunction( + 'wp_script_is', + [ + $this->testee::SETTINGS_PAGE_SCRIPT_HANDLE, + 'registered', + ], + 1, + true, + ); + + $this->wpMockFunction( + 'wp_enqueue_script', + [ + $this->testee::SETTINGS_PAGE_SCRIPT_HANDLE, + ], + ); + + $this->testee->enqueue_admin_scripts( $this->testee::SETTINGS_PAGE_HOOK_SUFFIX ); + $this->assertConditionsMet(); + } + + /** + * @covers ::enqueue_admin_scripts + */ + public function testEnqueueAdminScriptsWithScriptNotRegistered() { + $this->wpMockFunction( + 'RtCamp\GoogleLogin\plugin', + [], + 2, + function () { + return (object) [ + 'url' => 'https://example.com/', + 'assets_dir' => 'https://example.com/assets', + ]; + } + ); + + $this->wpMockFunction( + 'wp_script_is', + [ + $this->testee::SETTINGS_PAGE_SCRIPT_HANDLE, + 'registered', + ], + 1, + false, + ); + + $this->wpMockFunction( + 'wp_register_script', + [ + $this->testee::SETTINGS_PAGE_SCRIPT_HANDLE, + 'https://example.com/assets/build/js/settings.js', + [], + false, + true, + ], + 1, + true, + ); + + $this->wpMockFunction( + 'wp_enqueue_script', + [ + $this->testee::SETTINGS_PAGE_SCRIPT_HANDLE, + ], + 1, + true, + ); + + $this->testee->enqueue_admin_scripts( $this->testee::SETTINGS_PAGE_HOOK_SUFFIX ); + $this->assertConditionsMet(); + } + /** * @covers ::register_script */ @@ -111,7 +283,7 @@ function () { 'login-with-google', 'https://example.com/assets/js/login.js', [ - 'some-other-script' + 'some-other-script', ], false, true, @@ -124,7 +296,7 @@ function () { 'login-with-google', 'js/login.js', [ - 'some-other-script' + 'some-other-script', ] ); diff --git a/tests/php/Unit/Modules/SettingsTest.php b/tests/php/Unit/Modules/SettingsTest.php index bc2616e1..6383284d 100644 --- a/tests/php/Unit/Modules/SettingsTest.php +++ b/tests/php/Unit/Modules/SettingsTest.php @@ -7,6 +7,8 @@ namespace RtCamp\GoogleLogin\Tests\Unit\Modules; +use Mockery; +use RtCamp\GoogleLogin\Utils\Helper; use WP_Mock; use RtCamp\GoogleLogin\Interfaces\Module as ModuleInterface; use RtCamp\GoogleLogin\Tests\TestCase; @@ -63,11 +65,11 @@ public function testGetWithProper() { 'get_option', [ 'wp_google_login_settings', - [] + [], ], 1, [ - 'client_id' => 'cid' + 'client_id' => 'cid', ] ); @@ -84,7 +86,7 @@ public function testInit() { 'get_option', [ 'wp_google_login_settings', - [] + [], ], 1, [] @@ -93,10 +95,146 @@ public function testInit() { WP_Mock::expectActionAdded( 'admin_init', [ $this->testee, 'register_settings' ] ); WP_Mock::expectActionAdded( 'admin_menu', [ $this->testee, 'settings_page' ] ); + WP_Mock::expectFilterAdded( 'auth_cookie_expiration', [ $this->testee, 'modify_cookie_expiry' ] ); + $this->testee->init(); $this->assertConditionsMet(); } + /** + * @covers ::modify_cookie_expiry + */ + public function testModifyCookieExpiry() { + $state = 'eyJwcm92aWRlciI6Imdvb2dsZSIsIm5vbmNlIjoidGVzdG5vbmNlIn0='; + + $helperMock = Mockery::mock( 'alias:' . Helper::class ); + $helperMock->expects( 'filter_input' )->once()->withArgs( + [ + INPUT_GET, + 'state', + FILTER_SANITIZE_FULL_SPECIAL_CHARS, + ] + )->andReturn( $state ); + + $default_expiration = 14; + $this->testee->cookie_expiry = 230; + + $value_to_match = $this->testee->cookie_expiry * DAY_IN_SECONDS; + + $return = $this->testee->modify_cookie_expiry( $default_expiration ); + $this->assertEquals( $value_to_match, $return ); + } + + /** + * @covers ::modify_cookie_expiry + */ + public function testModifyCookieExpiryWithOtherProvider() { + $state = 'eyJwcm92aWRlciI6InNvbWUtb3RoZXIiLCJub25jZSI6InRlc3Rub25jZSJ9'; + + $helperMock = Mockery::mock( 'alias:' . Helper::class ); + $helperMock->expects( 'filter_input' )->once()->withArgs( + [ + INPUT_GET, + 'state', + FILTER_SANITIZE_FULL_SPECIAL_CHARS, + ] + )->andReturn( $state ); + + $default_expiration = 14; + $this->testee->cookie_expiry = 230; + + $return = $this->testee->modify_cookie_expiry( $default_expiration ); + $this->assertEquals( $default_expiration, $return ); + } + + /** + * @covers ::get_warning_classes + */ + public function testGetWarningClassesForDaysMoreThen14() { + $expected_class = 'warning'; + + list( $object, $method ) = $this->buildTesteeMethodMock( + Testee::class, + [], + 'get_warning_classes', + [] + ); + + $return = $method->invokeArgs( $object, [ 15 ] ); + + $this->assertStringNotContainsString( 'hidden', $return ); + } + + /** + * @covers ::get_warning_classes + */ + public function testGetWarningClassesForDaysLessThen14() { + list( $object, $method ) = $this->buildTesteeMethodMock( + Testee::class, + [], + 'get_warning_classes', + [] + ); + + $return = $method->invokeArgs( $object, [ 13 ] ); + + $this->assertStringContainsString( 'hidden', $return ); + } + + /** + * @covers ::modify_cookie_expiry + */ + public function testModifyCookieExpiryStateWithNoProvider() { + $state = 'eyJub25jZSI6InRlc3Rub25jZSJ9'; + + $helperMock = Mockery::mock( 'alias:' . Helper::class ); + $helperMock->expects( 'filter_input' )->once()->withArgs( + [ + INPUT_GET, + 'state', + FILTER_SANITIZE_FULL_SPECIAL_CHARS, + ] + )->andReturn( $state ); + + $default_expiration = 14; + $this->testee->cookie_expiry = 230; + + $return = $this->testee->modify_cookie_expiry( $default_expiration ); + $this->assertEquals( $default_expiration, $return ); + } + + /** + * @covers ::modify_cookie_expiry + */ + public function testModifyCookieExpiryWithNonNumericValue() { + $state = 'eyJwcm92aWRlciI6Imdvb2dsZSIsIm5vbmNlIjoidGVzdG5vbmNlIn0='; + + $helperMock = Mockery::mock( 'alias:' . Helper::class ); + $helperMock->expects( 'filter_input' )->once()->withArgs( + [ + INPUT_GET, + 'state', + FILTER_SANITIZE_FULL_SPECIAL_CHARS, + ] + )->andReturn( $state ); + + $default_expiration = 14; + $this->testee->cookie_expiry = 'abc'; + + $return = $this->testee->modify_cookie_expiry( $default_expiration ); + $this->assertEquals( $default_expiration, $return ); + + } + + /** + * @covers ::cookie_expiry_field + */ + public function testCookieExpiryField() { + $this->setOutputCallback(function() {}); + $this->testee->cookie_expiry_field(); + $this->assertConditionsMet(); + } + /** * @covers ::register_settings */ @@ -105,7 +243,7 @@ public function testRegisterSettings() { 'register_setting', [ 'wp_google_login', - 'wp_google_login_settings' + 'wp_google_login_settings', ], 1, true @@ -117,7 +255,7 @@ public function testRegisterSettings() { 'wp_google_login_section', 'Log in with Google Settings', \Closure::class, - 'login-with-google' + 'login-with-google', ], 1 ); @@ -133,7 +271,7 @@ public function testRegisterSettings() { \WP_Mock\Functions::type( 'string' ), \WP_Mock\Functions::type( 'array' ), ], - 'times' => 6 + 'times' => 7, ] ); @@ -154,7 +292,7 @@ public function testSettingsPage() { 'login-with-google', [ $this->testee, - 'output' + 'output', ], ] ); @@ -203,7 +341,7 @@ public function testClientIdField() { 'esc_html__', [ 'Create oAuth Client ID and Client Secret at', - 'login-with-google' + 'login-with-google', ], 2, ); @@ -216,7 +354,7 @@ public function testClientIdField() { esc_html__( 'Create oAuth Client ID and Client Secret at', 'login-with-google' ), 'https://console.developers.google.com/apis/dashboard', 'console.developers.google.com' - ) + ), ], 1, ); @@ -235,7 +373,7 @@ public function testUserRegistration() { $this->wpMockFunction( 'checked', [ - 'yes' + 'yes', ], 1, ); @@ -244,7 +382,7 @@ public function testUserRegistration() { 'esc_html_e', [ 'Create a new user account if it does not exist already', - 'login-with-google' + 'login-with-google', ], 1, ); @@ -287,7 +425,7 @@ public function testWhitelistedDomains() { $this->wpMockFunction( 'esc_html', [ - __( 'Add each domain comma separated', 'login-with-google' ) + __( 'Add each domain comma separated', 'login-with-google' ), ], 1, ); @@ -305,7 +443,7 @@ public function testClientSecretField() { $this->wpMockFunction( 'esc_attr', [ - 'cis' + 'cis', ], 1 ); diff --git a/tests/php/bootstrap.php b/tests/php/bootstrap.php index 112893e5..90e48ed9 100644 --- a/tests/php/bootstrap.php +++ b/tests/php/bootstrap.php @@ -26,3 +26,27 @@ if ( ! defined( 'GH_PLUGIN_DIR' ) ) { define( 'GH_PLUGIN_DIR', dirname( __DIR__, 2 ) ); } + +if ( ! defined( 'MINUTE_IN_SECONDS' ) ) { + define( 'MINUTE_IN_SECONDS', 60 ); +} + +if ( ! defined( 'HOUR_IN_SECONDS' ) ) { + define( 'HOUR_IN_SECONDS', 60 * MINUTE_IN_SECONDS ); +} + +if ( ! defined( 'DAY_IN_SECONDS' ) ) { + define( 'DAY_IN_SECONDS', 24 * HOUR_IN_SECONDS ); +} + +if ( ! defined( 'WEEK_IN_SECONDS' ) ) { + define( 'WEEK_IN_SECONDS', 7 * DAY_IN_SECONDS ); +} + +if ( ! defined( 'MONTH_IN_SECONDS' ) ) { + define( 'MONTH_IN_SECONDS', 30 * DAY_IN_SECONDS ); +} + +if ( ! defined( 'YEAR_IN_SECONDS' ) ) { + define( 'YEAR_IN_SECONDS', 365 * DAY_IN_SECONDS ); +}