diff --git a/core/.eslintrc.json b/core/.eslintrc.json index f4b9ff39fb65086fb41566059484603e9e335b96..5a982db5a266c016b3f45da642b731bfc6969905 100644 --- a/core/.eslintrc.json +++ b/core/.eslintrc.json @@ -15,6 +15,7 @@ "drupalTranslations": true, "jQuery": true, "_": true, + "Cookies": true, "Backbone": true, "Modernizr": true, "Popper": true, diff --git a/core/assets/vendor/jquery.cookie/jquery.cookie.min.js b/core/assets/vendor/jquery.cookie/jquery.cookie.min.js deleted file mode 100644 index c0f19d8a3b4097a2f5dfbb731c248d9d8bdbded5..0000000000000000000000000000000000000000 --- a/core/assets/vendor/jquery.cookie/jquery.cookie.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jquery.cookie v1.4.1 | MIT */ -!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}}); \ No newline at end of file diff --git a/core/assets/vendor/js-cookie/js.cookie.min.js b/core/assets/vendor/js-cookie/js.cookie.min.js new file mode 100644 index 0000000000000000000000000000000000000000..f5f4c36c17146c4d1fb7346fca83f196de695e8b --- /dev/null +++ b/core/assets/vendor/js-cookie/js.cookie.min.js @@ -0,0 +1,3 @@ +/*! js-cookie v2.2.1 | MIT */ + +!function(a){var b;if("function"==typeof define&&define.amd&&(define(a),b=!0),"object"==typeof exports&&(module.exports=a(),b=!0),!b){var c=window.Cookies,d=window.Cookies=a();d.noConflict=function(){return window.Cookies=c,d}}}(function(){function a(){for(var a=0,b={};a<arguments.length;a++){var c=arguments[a];for(var d in c)b[d]=c[d]}return b}function b(a){return a.replace(/(%[0-9A-Z]{2})+/g,decodeURIComponent)}function c(d){function e(){}function f(b,c,f){if("undefined"!=typeof document){f=a({path:"/"},e.defaults,f),"number"==typeof f.expires&&(f.expires=new Date(1*new Date+864e5*f.expires)),f.expires=f.expires?f.expires.toUTCString():"";try{var g=JSON.stringify(c);/^[\{\[]/.test(g)&&(c=g)}catch(j){}c=d.write?d.write(c,b):encodeURIComponent(c+"").replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),b=encodeURIComponent(b+"").replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent).replace(/[\(\)]/g,escape);var h="";for(var i in f)f[i]&&(h+="; "+i,!0!==f[i]&&(h+="="+f[i].split(";")[0]));return document.cookie=b+"="+c+h}}function g(a,c){if("undefined"!=typeof document){for(var e={},f=document.cookie?document.cookie.split("; "):[],g=0;g<f.length;g++){var h=f[g].split("="),i=h.slice(1).join("=");c||'"'!==i.charAt(0)||(i=i.slice(1,-1));try{var j=b(h[0]);if(i=(d.read||d)(i,j)||b(i),c)try{i=JSON.parse(i)}catch(k){}if(e[j]=i,a===j)break}catch(k){}}return a?e[a]:e}}return e.set=f,e.get=function(a){return g(a,!1)},e.getJSON=function(a){return g(a,!0)},e.remove=function(b,c){f(b,"",a(c,{expires:-1}))},e.defaults={},e.withConverter=c,e}return c(function(){})}); \ No newline at end of file diff --git a/core/core.libraries.yml b/core/core.libraries.yml index b632a426e05a890bcd4f1c67952be24d441718cd..51ac98b7aba9a74990a33e0cf62fbd236873becb 100644 --- a/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -210,7 +210,6 @@ drupal.form: - core/jquery - core/drupal - core/drupal.debounce - - core/jquery.cookie - core/jquery.once drupal.machine-name: @@ -275,7 +274,6 @@ drupal.tabledrag: - core/drupal - core/drupalSettings - core/jquery.once - - core/jquery.cookie drupal.tableheader: version: VERSION @@ -351,16 +349,14 @@ jquery: assets/vendor/jquery/jquery.min.js: { minified: true, weight: -20 } jquery.cookie: - remote: https://github.com/carhartl/jquery-cookie - version: "v1.4.1" - license: - name: MIT - url: https://github.com/carhartl/jquery-cookie/blob/v1.4.1/MIT-LICENSE.txt - gpl-compatible: true + version: VERSION js: - assets/vendor/jquery.cookie/jquery.cookie.min.js: { minified: true } + misc/jquery.cookie.shim.js: {} dependencies: - core/jquery + - core/drupal + - core/js-cookie + deprecated: The %library_id% asset library is deprecated in Drupal 9.0.0 and will be removed in Drupal 10.0.0. Use the core/js-cookie library instead. See https://www.drupal.org/node/3104677 jquery.farbtastic: remote: https://github.com/mattfarina/farbtastic @@ -406,7 +402,6 @@ jquery.joyride: assets/vendor/jquery-joyride/jquery.joyride-2.1.js: { } dependencies: - core/jquery - - core/jquery.cookie jquery.once: remote: https://github.com/RobLoach/jquery-once @@ -661,3 +656,15 @@ drupal.dialog.off_canvas: - core/drupal.announce - core/drupal.dialog - core/drupal.dialog.ajax + +js-cookie: + remote: https://github.com/js-cookie/js-cookie + version: "v2.2.1" + license: + name: MIT + url: https://github.com/js-cookie/js-cookie/blob/v2.2.1/MIT-LICENSE.txt + gpl-compatible: true + js: + assets/vendor/js-cookie/js.cookie.min.js: {} + dependencies: + - core/drupal.object.assign diff --git a/core/misc/jquery.cookie.shim.es6.js b/core/misc/jquery.cookie.shim.es6.js new file mode 100644 index 0000000000000000000000000000000000000000..b6b925a10070e06f24115da563a09424a995e33b --- /dev/null +++ b/core/misc/jquery.cookie.shim.es6.js @@ -0,0 +1,207 @@ +/** + * @file + * Defines a backwards-compatible shim for jquery.cookie. + */ + +/** + * The core/js-cookie library object. + * + * @global + * + * @var {object} Cookies + */ + +(($, Drupal, cookies) => { + const deprecatedMessageSuffix = `is deprecated in Drupal 9.0.0 and will be removed in Drupal 10.0.0. Use the core/js-cookie library instead. See https://www.drupal.org/node/3104677`; + + /** + * Determines if an object is a function. + * + * @param {Object} obj + * The object to check. + * + * @return {boolean} + * True if the object is a function. + */ + const isFunction = obj => + Object.prototype.toString.call(obj) === '[object Function]'; + + /** + * Decodes cookie value for compatibility with jquery.cookie. + * + * @param {string} value + * The cookie value to parse. + * + * @return {string} + * The cookie value for the reader to return. + */ + const parseCookieValue = value => { + if (value.indexOf('"') === 0) { + value = value + .slice(1, -1) + .replace(/\\"/g, '"') + .replace(/\\\\/g, '\\'); + } + return decodeURIComponent(value.replace(/\+/g, ' ')); + }; + + /** + * Wraps the cookie value to support unsanitized values. + * + * Decoding strings is the job of the converter when using js-cookie, and + * the shim uses the same decode function as that library when the deprecated + * raw option is not used. + * + * @param {string} cookieValue + * The cookie value. + * @param {string} cookieName + * The cookie name. + * @param {reader~converterCallback} converter + * A function that takes the cookie value for further processing. + * @param {boolean} readUnsanitized + * Uses the unsanitized value when set to true. + * + * @return {string} + * The cookie value that js-cookie will return. + */ + const reader = (cookieValue, cookieName, converter, readUnsanitized) => { + const value = readUnsanitized ? cookieValue : parseCookieValue(cookieValue); + + if (converter !== undefined && isFunction(converter)) { + return converter(value, cookieName); + } + return value; + }; + + /** + * Gets or sets a browser cookie. + * + * @example + * // Returns 'myCookie=myCookieValue'. + * $.cookie('myCookie', 'myCookieValue'); + * @example + * // Returns 'myCookieValue'. + * $.cookie('myCookie'); + * + * @example + * // Returns the literal URI-encoded value of {"key": "value"} as the cookie + * // value along with the path as in the above example. + * $.cookie('myCookie', { key: 'value' }); + * @example + * $.cookie.json = true; + * // Returns { key: 'value' }. + * $.cookie('myCookie'); + * + * @param {string} key + * The name of the cookie. + * @param {string|Object|Function|undefined} value + * A js-cookie converter callback when used as a getter. This callback must + * be a function when using this shim for backwards-compatiblity with + * jquery.cookie. When used as a setter, value is the string or JSON object + * to be used as the cookie value. + * @param {Object|undefined} options + * Overrides the default options when used as a setter. See the js-cookie + * library README.md file for details. + * + * @return {string} + * Returns the cookie name, value, and other properties based on the + * return value of the document.cookie setter. + * + * @deprecated in Drupal 9.0.0 and is removed from Drupal 10.0.0. + * Use the core/js-cookie library instead. + * + * @see https://www.drupal.org/node/3104677 + * @see https://github.com/js-cookie/js-cookie/blob/v2.2.1/README.md + */ + $.cookie = (key, value = undefined, options = undefined) => { + Drupal.deprecationError({ + message: `jQuery.cookie() ${deprecatedMessageSuffix}`, + }); + if (value !== undefined && !isFunction(value)) { + // The caller is setting a cookie value and not trying to retrieve the + // cookie value using a converter callback. + const attributes = Object.assign($.cookie.defaults, options); + + if (!$.cookie.json) { + // An object that is passed in must be typecast to a string when the + // "json" option is not set because js-cookie will always stringify + // JSON cookie values. + value = String(value); + } + + // If the expires value is a non-empty string, it needs to be converted + // to a Date() object before being sent to js-cookie. + if (typeof attributes.expires === 'string' && attributes.expires !== '') { + attributes.expires = new Date(attributes.expires); + } + + const cookieSetter = cookies.withConverter({ + write: cookieValue => encodeURIComponent(cookieValue), + }); + + return cookieSetter.set(key, value, attributes); + } + + // Use either js-cookie or pass in a converter to get the raw cookie value, + // which has security implications, but remains in place for + // backwards-compatibility. + const userProvidedConverter = value; + const cookiesShim = cookies.withConverter((cookieValue, cookieName) => + reader(cookieValue, cookieName, userProvidedConverter, $.cookie.raw), + ); + + return $.cookie.json === true + ? cookiesShim.getJSON(key) + : cookiesShim.get(key); + }; + + /** + * @prop {Object} defaults + * The default options when setting a cookie. + * @prop {string} defaults.path + * The default path for the cookie is ''. + * @prop {undefined} defaults.expires + * There is no default value for the expires option. The default expiration + * is set to an empty string. + */ + $.cookie.defaults = { path: '', ...cookies.defaults }; + + /** + * @prop {boolean} json + * True if the cookie value should be parsed as JSON. + */ + $.cookie.json = false; + + /** + * @prop {boolean} json + * True if the cookie value should be returned as-is without decoding + * URI entities. In jquery.cookie, this also would not encode the cookie + * name, but js-cookie does not allow this. + */ + $.cookie.raw = false; + + /** + * Removes a browser cookie. + * + * @param {string} key + * The name of the cookie. + * @param {Object} options + * Optional options. See the js-cookie library README.md for more details. + * + * @return {boolean} + * Returns true when the cookie is successfully removed. + * + * @deprecated in Drupal 9.0.0 and is removed from Drupal 10.0.0. + * Use the core/js-cookie library instead. + * + * @see https://www.drupal.org/node/3104677 + * @see https://github.com/js-cookie/js-cookie/blob/v2.2.1/README.md + */ + $.removeCookie = (key, options) => { + Drupal.deprecationError({ + message: `jQuery.removeCookie() ${deprecatedMessageSuffix}`, + }); + cookies.remove(key, Object.assign($.cookie.defaults, options)); + return !cookies.get(key); + }; +})(jQuery, Drupal, window.Cookies); diff --git a/core/misc/jquery.cookie.shim.js b/core/misc/jquery.cookie.shim.js new file mode 100644 index 0000000000000000000000000000000000000000..29f98bca7944e7a3d3db08db203ab2f07b1d68f7 --- /dev/null +++ b/core/misc/jquery.cookie.shim.js @@ -0,0 +1,85 @@ +/** +* DO NOT EDIT THIS FILE. +* See the following change record for more information, +* https://www.drupal.org/node/2815083 +* @preserve +**/ + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +(function ($, Drupal, cookies) { + var deprecatedMessageSuffix = "is deprecated in Drupal 9.0.0 and will be removed in Drupal 10.0.0. Use the core/js-cookie library instead. See https://www.drupal.org/node/3104677"; + + var isFunction = function isFunction(obj) { + return Object.prototype.toString.call(obj) === '[object Function]'; + }; + + var parseCookieValue = function parseCookieValue(value) { + if (value.indexOf('"') === 0) { + value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } + + return decodeURIComponent(value.replace(/\+/g, ' ')); + }; + + var reader = function reader(cookieValue, cookieName, converter, readUnsanitized) { + var value = readUnsanitized ? cookieValue : parseCookieValue(cookieValue); + + if (converter !== undefined && isFunction(converter)) { + return converter(value, cookieName); + } + + return value; + }; + + $.cookie = function (key) { + var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; + Drupal.deprecationError({ + message: "jQuery.cookie() ".concat(deprecatedMessageSuffix) + }); + + if (value !== undefined && !isFunction(value)) { + var attributes = Object.assign($.cookie.defaults, options); + + if (!$.cookie.json) { + value = String(value); + } + + if (typeof attributes.expires === 'string' && attributes.expires !== '') { + attributes.expires = new Date(attributes.expires); + } + + var cookieSetter = cookies.withConverter({ + write: function write(cookieValue) { + return encodeURIComponent(cookieValue); + } + }); + return cookieSetter.set(key, value, attributes); + } + + var userProvidedConverter = value; + var cookiesShim = cookies.withConverter(function (cookieValue, cookieName) { + return reader(cookieValue, cookieName, userProvidedConverter, $.cookie.raw); + }); + return $.cookie.json === true ? cookiesShim.getJSON(key) : cookiesShim.get(key); + }; + + $.cookie.defaults = _objectSpread({ + path: '' + }, cookies.defaults); + $.cookie.json = false; + $.cookie.raw = false; + + $.removeCookie = function (key, options) { + Drupal.deprecationError({ + message: "jQuery.removeCookie() ".concat(deprecatedMessageSuffix) + }); + cookies.remove(key, Object.assign($.cookie.defaults, options)); + return !cookies.get(key); + }; +})(jQuery, Drupal, window.Cookies); \ No newline at end of file diff --git a/core/modules/system/tests/modules/js_cookie_test/js/js_cookie_shim_test.es6.js b/core/modules/system/tests/modules/js_cookie_test/js/js_cookie_shim_test.es6.js new file mode 100644 index 0000000000000000000000000000000000000000..b8f5f373e51a5c3be32d043fd72d3d09721472ca --- /dev/null +++ b/core/modules/system/tests/modules/js_cookie_test/js/js_cookie_shim_test.es6.js @@ -0,0 +1,31 @@ +/** + * @file + * Tests adding and removing browser cookies using the jquery_cookie shim. + */ +(({ behaviors }, $) => { + behaviors.jqueryCookie = { + attach: () => { + if ($('body').once('js_cookie_test-init').length) { + $('.js_cookie_test_add_button').on('click', () => { + $.cookie('js_cookie_test', 'red panda'); + }); + $('.js_cookie_test_add_raw_button').on('click', () => { + $.cookie.raw = true; + $.cookie('js_cookie_test_raw', 'red panda'); + }); + $('.js_cookie_test_add_json_button').on('click', () => { + $.cookie.json = true; + $.cookie('js_cookie_test_json', { panda: 'red' }); + $.cookie('js_cookie_test_json_simple', 'red panda'); + }); + $('.js_cookie_test_add_json_string_button').on('click', () => { + $.cookie.json = false; + $.cookie('js_cookie_test_json_string', { panda: 'red' }); + }); + $('.js_cookie_test_remove_button').on('click', () => { + $.removeCookie('js_cookie_test'); + }); + } + }, + }; +})(Drupal, jQuery); diff --git a/core/modules/system/tests/modules/js_cookie_test/js/js_cookie_shim_test.js b/core/modules/system/tests/modules/js_cookie_test/js/js_cookie_shim_test.js new file mode 100644 index 0000000000000000000000000000000000000000..a8dfa51c9fa518fdc021e4e10e43b6ae0feb2dd7 --- /dev/null +++ b/core/modules/system/tests/modules/js_cookie_test/js/js_cookie_shim_test.js @@ -0,0 +1,39 @@ +/** +* DO NOT EDIT THIS FILE. +* See the following change record for more information, +* https://www.drupal.org/node/2815083 +* @preserve +**/ + +(function (_ref, $) { + var behaviors = _ref.behaviors; + behaviors.jqueryCookie = { + attach: function attach() { + if ($('body').once('js_cookie_test-init').length) { + $('.js_cookie_test_add_button').on('click', function () { + $.cookie('js_cookie_test', 'red panda'); + }); + $('.js_cookie_test_add_raw_button').on('click', function () { + $.cookie.raw = true; + $.cookie('js_cookie_test_raw', 'red panda'); + }); + $('.js_cookie_test_add_json_button').on('click', function () { + $.cookie.json = true; + $.cookie('js_cookie_test_json', { + panda: 'red' + }); + $.cookie('js_cookie_test_json_simple', 'red panda'); + }); + $('.js_cookie_test_add_json_string_button').on('click', function () { + $.cookie.json = false; + $.cookie('js_cookie_test_json_string', { + panda: 'red' + }); + }); + $('.js_cookie_test_remove_button').on('click', function () { + $.removeCookie('js_cookie_test'); + }); + } + } + }; +})(Drupal, jQuery); \ No newline at end of file diff --git a/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.info.yml b/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..d33421d82352db55e1408a01f2c2c65896a95d24 --- /dev/null +++ b/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.info.yml @@ -0,0 +1,5 @@ +name: 'JS Cookie Test' +type: module +description: 'Module for the jsCookieTest.' +package: Testing +version: VERSION diff --git a/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.libraries.yml b/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.libraries.yml new file mode 100644 index 0000000000000000000000000000000000000000..0f15de6c686942da13b2f969e83ad83168e186fd --- /dev/null +++ b/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.libraries.yml @@ -0,0 +1,9 @@ +with_shim_test: + version: VERSION + js: + js/js_cookie_shim_test.js: {} + dependencies: + - core/jquery + - core/drupal + - core/jquery.cookie + - core/jquery.once diff --git a/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.routing.yml b/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.routing.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ee89b5496595b446be12ca4803952aa3675acad --- /dev/null +++ b/core/modules/system/tests/modules/js_cookie_test/js_cookie_test.routing.yml @@ -0,0 +1,7 @@ +js_cookie_test.with_shim: + path: '/js_cookie_with_shim_test' + defaults: + _controller: '\Drupal\js_cookie_test\Controller\JsCookieTestController::jqueryCookieShimTest' + _title: 'JQueryCookieShimTest' + requirements: + _access: 'TRUE' diff --git a/core/modules/system/tests/modules/js_cookie_test/src/Controller/JsCookieTestController.php b/core/modules/system/tests/modules/js_cookie_test/src/Controller/JsCookieTestController.php new file mode 100644 index 0000000000000000000000000000000000000000..7cce7cbf3bec6cbb9334a6eed9a1abc656b4b5ca --- /dev/null +++ b/core/modules/system/tests/modules/js_cookie_test/src/Controller/JsCookieTestController.php @@ -0,0 +1,59 @@ +<?php + +namespace Drupal\js_cookie_test\Controller; + +use Drupal\Core\Controller\ControllerBase; + +/** + * Test controller to assert js-cookie library integration. + */ +class JsCookieTestController extends ControllerBase { + + /** + * Provides buttons to add and remove cookies using JavaScript. + * + * @return array + * The render array. + */ + public function jqueryCookieShimTest() { + return [ + 'add' => [ + '#type' => 'button', + '#value' => $this->t('Add cookie'), + '#attributes' => [ + 'class' => ['js_cookie_test_add_button'], + ], + ], + 'add-raw' => [ + '#type' => 'button', + '#value' => $this->t('Add raw cookie'), + '#attributes' => [ + 'class' => ['js_cookie_test_add_raw_button'], + ], + ], + 'add-json' => [ + '#type' => 'button', + '#value' => $this->t('Add JSON cookie'), + '#attributes' => [ + 'class' => ['js_cookie_test_add_json_button'], + ], + ], + 'add-json-string' => [ + '#type' => 'button', + '#value' => $this->t('Add JSON cookie without json option'), + '#attributes' => [ + 'class' => ['js_cookie_test_add_json_string_button'], + ], + ], + 'remove' => [ + '#type' => 'button', + '#value' => $this->t('Remove cookie'), + '#attributes' => [ + 'class' => ['js_cookie_test_remove_button'], + ], + ], + '#attached' => ['library' => ['js_cookie_test/with_shim_test']], + ]; + } + +} diff --git a/core/tests/Drupal/Nightwatch/Tests/jsCookieTest.js b/core/tests/Drupal/Nightwatch/Tests/jsCookieTest.js new file mode 100644 index 0000000000000000000000000000000000000000..189d8bc15cf4c26d2f1bc497cd693a3a40075f0d --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Tests/jsCookieTest.js @@ -0,0 +1,274 @@ +const deprecatedMessageSuffix = `is deprecated in Drupal 9.0.0 and will be removed in Drupal 10.0.0. Use the core/js-cookie library instead. See https://www.drupal.org/node/3104677`; +// Nightwatch suggests non-ES6 functions when using the execute method. +// eslint-disable-next-line func-names, prefer-arrow-callback +const getJqueryCookie = function(cookieName) { + return undefined !== cookieName ? jQuery.cookie(cookieName) : jQuery.cookie(); +}; +// eslint-disable-next-line func-names, prefer-arrow-callback +const setJqueryCookieWithOptions = function( + cookieName, + cookieValue, + options = {}, +) { + return jQuery.cookie(cookieName, cookieValue, options); +}; +module.exports = { + '@tags': ['core'], + before(browser) { + browser.drupalInstall().drupalLoginAsAdmin(() => { + browser + .drupalRelativeURL('/admin/modules') + .setValue('input[type="search"]', 'JS Cookie Test') + .waitForElementVisible( + 'input[name="modules[js_cookie_test][enable]"]', + 1000, + ) + .click('input[name="modules[js_cookie_test][enable]"]') + .click('input[type="submit"]'); // Submit module form. + }); + }, + after(browser) { + browser.drupalUninstall(); + }, + 'Test jquery.cookie Shim Simple Value and jquery.removeCookie': browser => { + browser + .drupalRelativeURL('/js_cookie_with_shim_test') + .waitForElementVisible('.js_cookie_test_add_button', 1000) + .click('.js_cookie_test_add_button') + // prettier-ignore + .execute(getJqueryCookie, ['js_cookie_test'], result => { + browser.assert.equal( + result.value, + 'red panda', + '$.cookie returns cookie value', + ); + }) + .waitForElementVisible('.js_cookie_test_remove_button', 1000) + .click('.js_cookie_test_remove_button') + .execute(getJqueryCookie, ['js_cookie_test_remove'], result => { + browser.assert.equal(result.value, null, 'cookie removed'); + }) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim Empty Value': browser => { + browser + .setCookie({ + name: 'js_cookie_test_empty', + value: '', + }) + // prettier-ignore + .execute(getJqueryCookie, ['js_cookie_test_empty'], result => { + browser.assert.equal( + result.value, + '', + '$.cookie returns empty cookie value', + ); + }) + .getCookie('js_cookie_test_empty', result => { + browser.assert.equal(result.value, '', 'Cookie value is empty.'); + }) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim Undefined': browser => { + browser + .deleteCookie('js_cookie_test_undefined', () => { + browser.execute( + getJqueryCookie, + ['js_cookie_test_undefined'], + result => { + browser.assert.equal( + result.value, + undefined, + '$.cookie returns undefined cookie value', + ); + }, + ); + }) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim Decode': browser => { + browser + .setCookie({ + name: encodeURIComponent(' js_cookie_test_encoded'), + value: encodeURIComponent(' red panda'), + }) + .execute(getJqueryCookie, [' js_cookie_test_encoded'], result => { + browser.assert.equal( + result.value, + ' red panda', + '$.cookie returns decoded cookie value', + ); + }) + .setCookie({ + name: 'js_cookie_test_encoded_plus_to_space', + value: 'red+panda', + }) + .execute( + getJqueryCookie, + ['js_cookie_test_encoded_plus_to_space'], + result => { + browser.assert.equal( + result.value, + 'red panda', + '$.cookie returns decoded plus to space in cookie value', + ); + }, + ) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim With raw': browser => { + browser + .drupalRelativeURL('/js_cookie_with_shim_test') + .waitForElementVisible('.js_cookie_test_add_raw_button', 1000) + .click('.js_cookie_test_add_raw_button') + .execute(getJqueryCookie, ['js_cookie_test_raw'], result => { + browser.assert.equal( + result.value, + 'red%20panda', + '$.cookie returns raw cookie value', + ); + }) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim With JSON': browser => { + browser + .drupalRelativeURL('/js_cookie_with_shim_test') + .waitForElementVisible('.js_cookie_test_add_json_button', 1000) + .click('.js_cookie_test_add_json_button') + .execute(getJqueryCookie, ['js_cookie_test_json'], result => { + browser.assert.deepEqual( + result.value, + { panda: 'red' }, + 'Stringified JSON is returned as JSON.', + ); + }) + .getCookie('js_cookie_test_json', result => { + browser.assert.equal( + result.value, + '%7B%22panda%22%3A%22red%22%7D', + 'Cookie value is encoded backwards-compatible with jquery.cookie.', + ); + }) + .execute(getJqueryCookie, ['js_cookie_test_json_simple'], result => { + browser.assert.equal( + result.value, + 'red panda', + '$.cookie returns simple cookie value with JSON enabled', + ); + }) + .waitForElementVisible('.js_cookie_test_add_json_string_button', 1000) + .click('.js_cookie_test_add_json_string_button') + .execute(getJqueryCookie, ['js_cookie_test_json_string'], result => { + browser.assert.deepEqual( + result.value, + '[object Object]', + 'JSON used without json option is return as a string.', + ); + }) + .getCookie('js_cookie_test_json_string', result => { + browser.assert.equal( + result.value, + '%5Bobject%20Object%5D', + 'Cookie value is encoded backwards-compatible with jquery.cookie.', + ); + }) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim invalid URL encoding': browser => { + browser + .setCookie({ + name: 'js_cookie_test_bad', + value: 'red panda%', + }) + .execute(getJqueryCookie, ['js_cookie_test_bad'], result => { + browser.assert.equal( + result.value, + undefined, + '$.cookie won`t throw exception, returns undefined', + ); + }) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim Read all when there are cookies or return empty object': browser => { + browser + .getCookie('SIMPLETEST_USER_AGENT', simpletestCookie => { + const simpletestCookieValue = simpletestCookie.value; + browser + .drupalRelativeURL('/js_cookie_with_shim_test') + .deleteCookies(() => { + browser + .execute(getJqueryCookie, [], result => { + browser.assert.deepEqual( + result.value, + {}, + '$.cookie() returns empty object', + ); + }) + .setCookie({ + name: 'js_cookie_test_first', + value: 'red panda', + }) + .setCookie({ + name: 'js_cookie_test_second', + value: 'second red panda', + }) + .setCookie({ + name: 'js_cookie_test_third', + value: 'third red panda id bad%', + }) + .execute(getJqueryCookie, [], result => { + browser.assert.deepEqual( + result.value, + { + js_cookie_test_first: 'red panda', + js_cookie_test_second: 'second red panda', + }, + '$.cookie() returns object containing all cookies', + ); + }) + .setCookie({ + name: 'SIMPLETEST_USER_AGENT', + value: simpletestCookieValue, + }); + }); + }) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim $.cookie deprecation message': browser => { + browser + .drupalRelativeURL('/js_cookie_with_shim_test') + .waitForElementVisible('.js_cookie_test_add_button', 1000) + .click('.js_cookie_test_add_button') + .assert.deprecationErrorExists( + `jQuery.cookie() ${deprecatedMessageSuffix}`, + ) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim $.removeCookie deprecation message': browser => { + browser + .drupalRelativeURL('/js_cookie_with_shim_test') + .waitForElementVisible('.js_cookie_test_remove_button', 1000) + .click('.js_cookie_test_remove_button') + .assert.deprecationErrorExists( + `jQuery.removeCookie() ${deprecatedMessageSuffix}`, + ) + .drupalLogAndEnd({ onlyOnError: false }); + }, + 'Test jquery.cookie Shim expires option as Date instance': browser => { + const sevenDaysFromNow = new Date(); + sevenDaysFromNow.setDate(sevenDaysFromNow.getDate() + 7); + browser + .execute( + setJqueryCookieWithOptions, + ['c', 'v', { expires: sevenDaysFromNow }], + result => { + browser.assert.equal( + result.value, + `c=v; expires=${sevenDaysFromNow.toUTCString()}`, + 'should write the cookie string with expires', + ); + }, + ) + .drupalLogAndEnd({ onlyOnError: false }); + }, +};