├── .gitignore ├── .travis.yml ├── .zuul.yml ├── LICENSE ├── README.md ├── bower.json ├── browser-cookies.png ├── browser_compatibility.svg ├── dist ├── browser-cookies.min.js └── browser-cookies.test.js ├── gulpfile.mjs ├── package.json ├── src ├── browser-cookies.d.ts └── browser-cookies.js └── test ├── index.html └── spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files 2 | .DS_Store 3 | 4 | # Dependency directory 5 | node_modules 6 | 7 | # Code coverage 8 | coverage 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "6" 5 | 6 | after_success: 7 | "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 8 | 9 | env: 10 | global: 11 | - secure: SF/VM+H5FQ6v7WGtRSNsWpCWad4KBN4NlGw+E6vkE2viO3/yj/TkF2pIqLEys4+x0uhtiVbnRIuFwLx/5BDAY3aQ+abWphi7v1/wqDDs4O9OhMe/BWdxBwxd7TETl5uO0XFIOgYe73y/NXbf8aimp0H+IBqkO6Nl4zTRTBclHCE= 12 | - secure: EnVqtfiBuO+M45UQxyqZtArDOcVSY57GolYGYtnXI8uRddxbMIQqtGFgcptUeAxwvf2qQgdVwvsV/ujHWXmdPGlwxuAf65qD1PyNqIg4PPBDp/0PVpVHPFTuvU0SzwIDHLxAIRqk7CF8wZwmCz/dIsu8UlM47YmQ2pAeqhIUDvQ= 13 | - secure: TA01+HMO1Md3/nGbNG6+quA/wZ3spyinEyGXStgMX5nyTHIDWNRwNpq03ED+UygnMlhtasIXf7KqrbLAc8yRmTDqahNGBEQWxntYdqxvVOOBXTLwGMkiXJlvBl3fkIfKDv9q/vuneTLvTr7PXWrzBboYj7KIRBc7PHhVmayu98Y= 14 | -------------------------------------------------------------------------------- /.zuul.yml: -------------------------------------------------------------------------------- 1 | name: browser-cookies 2 | ui: jasmine 3 | tunnel: 4 | type: ngrok 5 | bind_tls: false 6 | concurrency: 1 7 | scripts: 8 | - "dist/browser-cookies.test.js" 9 | browsers: 10 | - name: iphone 11 | version: oldest 12 | - name: iphone 13 | version: latest 14 | - name: chrome 15 | version: oldest 16 | - name: chrome 17 | version: latest 18 | - name: firefox 19 | version: oldest 20 | - name: firefox 21 | version: latest 22 | - name: internet explorer 23 | version: oldest 24 | - name: internet explorer 25 | version: lateste 26 | - name: MicrosoftEdge 27 | version: oldest 28 | - name: MicrosoftEdge 29 | version: latest 30 | - name: safari 31 | version: oldest 32 | - name: safari 33 | version: latest 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # browser-cookies 4 | **Tiny cookies library for the browser** 5 | 6 | [![NPM Version][npm-version-image]][npm-url] 7 | [![NPM Downloads][npm-downloads-image]][npm-url] 8 | [![Build Status][travis-image]][travis-url] 9 | [![Coveralls Status][coveralls-image]][coveralls-url] 10 | 11 | - [Features](#features) 12 | - [Browser compatibility](#browser-compatibility) 13 | - [Installation](#installation) 14 | - [Usage](#usage) 15 | - [API](#api) 16 | - [Options](#options) 17 | - [Examples](#examples) 18 | - [How to use with PHP](#how-to-use-with-php) 19 | 20 | ### Features 21 | - Clean and easy to use API 22 | - Small footprint (minified and gzipped ~ 0.5kB) 23 | - No dependencies 24 | - RFC6265 compliant 25 | - Cross browser support 26 | - Supports CommonJS (e.g. Browserify) 27 | 28 | ### Browser compatibility 29 | Cross browser support is verified on real browsers using automated testing: 30 | [![Sauce Test Status][saucelabs-image]][saucelabs-url] 31 | Or [run the unit tests][ref-unittests] right now in your current browser. 32 | 33 | ### Installation 34 | Using NPM 35 | `npm install browser-cookies` 36 | 37 | Using Bower 38 | `bower install browser-cookies` 39 | 40 | ### Usage 41 | ```javascript 42 | var cookies = require('browser-cookies'); 43 | 44 | cookies.set('firstName', 'Lisa'); 45 | cookies.set('firstName', 'Lisa', {expires: 365}); // Expires after 1 year 46 | cookies.set('firstName', 'Lisa', {secure: true, domain: 'www.example.org'}); 47 | 48 | cookies.get('firstName'); // Returns cookie value (or null) 49 | 50 | cookies.erase('firstName'); // Removes cookie 51 | ``` 52 | [More examples](#examples) 53 | 54 | ### API 55 | API contents: 56 | - method [cookies.set(`name`, `value` [, `options`])](#cookies-set) 57 | - method [cookies.get(`name`)](#cookies-get) 58 | - method [cookies.erase(`name`, [, `options`])](#cookies-erase) 59 | - method [cookies.all()](#cookies-all) 60 | - property [cookies.defaults](#cookies-defaults) 61 | 62 |
63 | 64 | [cookies.set(`name`, `value` [, `options`])](#cookies-set) 65 |
66 | Method to save a cookie. 67 | 68 | | argument | type | description 69 | |---------------|--------|------------ 70 | | **`name`** | string | The name of the cookie to save. 71 | | **`value`** | string | The value to save, [percent encoding][ref-percent-encoding] will automatically be applied. Note that only strings are allowed as value, the [examples](#examples) section shows how to save JSON data. 72 | | **`options`** | object | May contain any of the properties specified in [options](#options) below. If an option is not specified, the value configured in [cookies.defaults](#cookies-defaults) will be used. 73 | 74 | 75 |
76 | 77 | [cookies.get(`name`)](#cookies-get) 78 |
79 | Method that returns a cookie value, or **null** if the cookie is not found. [Percent encoded][ref-percent-encoding] values will automatically be decoded. 80 | 81 | | argument | type | description 82 | |---------------|--------|------------ 83 | | **`name`** | string | The name of the cookie to retrieve. 84 | 85 |
86 | 87 | [cookies.erase(`name` [, `options` ])](#cookies-erase) 88 |
89 | Method to remove a cookie. 90 | 91 | | argument | type | description 92 | |---------------|--------|------------ 93 | | **`name`** | string | The name of the cookie to remove. 94 | | **`options`** | object | May contain the `domain` and `path` properties specified in [options](#options) below. If an option is not specified, the value configured in [cookies.defaults](#cookies-defaults) will be used. 95 | 96 |
97 | 98 | [cookies.all()](#cookies-all) 99 |
100 | Method to get all cookies. 101 | Returns an object containing all cookie values with the cookie names used as keys. Percent encoded names and values will automatically be decoded. 102 | 103 |
104 | 105 | [cookies.defaults](#cookies-defaults) 106 |
107 | This object may be used to change the default value of each option specified in [options](#options) below. 108 | 109 | 110 | ### Options 111 | The options shown in the table below may be set globally using [cookies.defaults](#cookies-defaults) or passed as function argument to [cookies.set()](#cookies-set) and [cookies.erase()](#cookies-erase). Also check out the [Examples](#examples) further below. 112 | 113 | | Name | Type | Default | Description 114 | |------------|----------------------------|---------|-------- 115 | | `expires` | `Number`, `Date`, `String` | `0` | Configure when the cookie expires by using one of the following types as value: 116 | | `domain` | `String` | `""` | The [domain][ref-cookie-domain] from where the cookie is readable. 117 | | `path` | `String` | `"/"` | The path from where the cookie is readable. 118 | | `secure` | `Boolean` | `false` | If true the cookie will only be transmitted over secure protocols like https. 119 | | `httponly` | `Boolean` | `false` | If true the cookie may only be read by the web server. 120 | | `samesite` | `String` | `""` | The `samesite` argument may be used to [prevent cookies from being sent along with cross-site requests][ref-samesite].This is an experimental feature as only [a few browsers support SameSite][ref-samesite-caniuse] and [the standard][ref-samesite-spec] has not been finalized yet. Don't use this feature in production environments. 121 | 122 | ### Examples 123 | Count the number of a visits to a page: 124 | ```javascript 125 | var cookies = require('browser-cookies'); 126 | 127 | // Get cookie value 128 | var visits = cookies.get('count') || 0; 129 | console.log("You've been here " + parseInt(visits) + " times before!"); 130 | 131 | // Increment the counter and set (or update) the cookie 132 | cookies.set('count', parseInt(visits) + 1, {expires: 365}); 133 | ``` 134 | 135 | JSON may be saved by converting the JSON object into a string: 136 | ```javascript 137 | var cookies = require('browser-cookies'); 138 | 139 | // Store JSON data 140 | var user = {firstName: 'Sofia', lastName: 'Dueñas'}; 141 | cookies.set('user', JSON.stringify(user)) 142 | 143 | // Retrieve JSON data 144 | var userString = cookies.get('user'); 145 | alert('Hi ' + JSON.parse(userString).firstName); 146 | ``` 147 | 148 | The default cookie options may be changed: 149 | ```javascript 150 | var cookies = require('browser-cookies'); 151 | 152 | // Override defaults 153 | cookies.defaults.secure = true; 154 | cookies.defaults.expires = 7; 155 | 156 | // 'secure' option enabled and cookie expires in 7 days 157 | cookies.set('FirstName', 'John') 158 | 159 | // 'secure' option enabled and cookie expires in 30 days 160 | cookies.set('LastName', 'Smith', {expires: 30}) 161 | ``` 162 | 163 | The `cookies.all` method can be used for more advanced functionality, for example to erase all cookies except one: 164 | ```javascript 165 | var cookies = require('browser-cookies'); 166 | var cookieToKeep = 'FirstName'; // Name of the cookie to keep 167 | 168 | // Get all cookies as an object 169 | var allCookies = cookies.all(); 170 | 171 | // Iterate over all cookie names 172 | for (var cookieName in allCookies) { 173 | // Erase the cookie (except if it's the cookie that needs to be kept) 174 | if(allCookies.hasOwnProperty(cookieName) && cookieName != cookieToKeep) { 175 | cookies.erase(cookieName); 176 | } 177 | } 178 | ``` 179 | 180 | ### How to use with PHP 181 | Use [setrawcookie()][ref-php-setrawcookie] instead of `setcookie()` to prevent PHP from replacing spaces with `+` characters: 182 | ```php 183 | // Set cookie 184 | setrawcookie('fullName', rawurlencode('Lisa Cuddy')); 185 | 186 | // Get cookie 187 | $_COOKIE['fullName']; 188 | ``` 189 | 190 | ### Development 191 | The design goal is to provide the smallest possible size (when minified and gzipped) for the given API while remaining compliant to RFC6265 and providing cross-browser compatibility and consistency. 192 | 193 | Development setup (requires [node][ref-node-download] and [git][ref-git-setup] to be installed): 194 | ```python 195 | git clone https://github.com/voltace/browser-cookies.git 196 | cd browser-cookies 197 | npm install # Install dev dependencies 198 | npm run test:local # Run unit tests locally (takes ~5 seconds) 199 | npm run build # Create minified version 200 | ``` 201 | 202 | Feel free to submit an issue on GitHub for any question, bug or feature requesst you may have. 203 | 204 | ### License 205 | Public Domain ([UNLICENSE][ref-licence]) 206 | 207 | [ref-browser-cookies-shim]: https://www.github.com/voltace/browser-cookies-shim 208 | [ref-cookie-domain]: https://stackoverflow.com/questions/1062963/how-do-browser-cookie-domains-work 209 | [ref-date-parse]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse 210 | [ref-httponly]: http://blog.codinghorror.com/protecting-your-cookies-httponly/ 211 | [ref-ie-cookies]: http://erik.io/blog/2014/03/04/definitive-guide-to-cookie-domains/ 212 | [ref-git-setup]: https://help.github.com/articles/set-up-git/ 213 | [ref-licence]: http://choosealicense.com/licenses/#unlicense 214 | [ref-node-download]: https://nodejs.org/download/ 215 | [ref-percent-encoding]: http://en.wikipedia.org/wiki/Percent-encoding 216 | [ref-php-setrawcookie]: http://php.net/manual/en/function.setrawcookie.php 217 | [ref-unittests]: https://rawgit.com/voltace/browser-cookies/master/test/index.html 218 | [ref-samesite]: http://www.sjoerdlangkemper.nl/2016/04/14/preventing-csrf-with-samesite-cookie-attribute/ 219 | [ref-samesite-caniuse]: http://caniuse.com/#feat=same-site-cookie-attribute 220 | [ref-samesite-spec]: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 221 | 222 | [npm-url]: https://npmjs.org/package/browser-cookies 223 | [npm-version-image]: https://img.shields.io/npm/v/browser-cookies.svg 224 | [npm-downloads-image]: https://img.shields.io/npm/dm/browser-cookies.svg 225 | 226 | [travis-url]: https://travis-ci.org/voltace/browser-cookies 227 | [travis-image]: https://img.shields.io/travis/voltace/browser-cookies.svg 228 | 229 | [coveralls-url]: https://coveralls.io/r/voltace/browser-cookies 230 | [coveralls-image]: https://img.shields.io/coveralls/voltace/browser-cookies/master.svg 231 | 232 | [saucelabs-url]: https://saucelabs.com/u/browser-cookies 233 | [saucelabs-image]: https://rawgit.com/voltace/browser-cookies/master/browser_compatibility.svg 234 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browser-cookies", 3 | "main": "src/browser-cookies.js", 4 | "version": "1.2.0", 5 | "homepage": "https://github.com/voltace/browser-cookies", 6 | "authors": [ 7 | "Voltace " 8 | ], 9 | "description": "Tiny cookies library for the browser", 10 | "keywords": [ 11 | "cookies", 12 | "cookie", 13 | "client" 14 | ], 15 | "moduleType": "node", 16 | "license": "Unlicense", 17 | "ignore": [ 18 | "**/*", 19 | "!src/browser-cookies.js" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /browser-cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voltace/browser-cookies/69051914678d11cf1f89c748ac20898c525bcbdd/browser-cookies.png -------------------------------------------------------------------------------- /browser_compatibility.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Android 89 | 90 | 91 | 92 | 93 | 94 | 4.4 95 | 96 | * 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 5.0 106 | 107 | * 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 6.0 117 | 118 | * 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | Firefox 128 | 129 | 130 | 131 | 132 | 133 | 4 134 | 135 | 10.9 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 10 145 | 146 | 10.12 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 20 156 | 157 | 10.12 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 30 167 | 168 | 8 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 40 178 | 179 | u 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 57 189 | 190 | 10.11 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | Chrome 200 | 201 | 202 | 203 | 204 | 205 | 26 206 | 207 | 8 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 30 217 | 218 | 7 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 40 228 | 229 | 10.10 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 63 239 | 240 | 10.12 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | IE 250 | 251 | 252 | 253 | 254 | 255 | 8 256 | 257 | 7 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 9 267 | 268 | 7 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 10 278 | 279 | 8 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 11 289 | 290 | 10 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | iPhone 300 | 301 | 302 | 303 | 304 | 305 | 8.1 306 | 307 | 10.10 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 9.0 317 | 318 | 10.10 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 11 328 | 329 | 10.12 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | Edge 339 | 340 | 341 | 342 | 343 | 344 | 13 345 | 346 | 10 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 14 356 | 357 | 10 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 15 367 | 368 | 10 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 16 378 | 379 | 10 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | Safari 389 | 390 | 391 | 392 | 393 | 394 | 7 395 | 396 | 10.9 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 8 406 | 407 | 10.10 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 9 417 | 418 | 10.11 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 10 428 | 429 | 10.12 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 11 439 | 440 | u 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | -------------------------------------------------------------------------------- /dist/browser-cookies.min.js: -------------------------------------------------------------------------------- 1 | exports.defaults={},exports.set=function(e,t,o){var o=o||{},n=exports.defaults,p=o.expires||n.expires,r=o.domain||n.domain,i=void 0!==o.path?o.path:void 0!==n.path?n.path:"/",s=(void 0!==o.secure?o:n).secure,a=(void 0!==o.httponly?o:n).httponly,o=(void 0!==o.samesite?o:n).samesite,n=p?new Date("number"==typeof p?(new Date).getTime()+864e5*p:p):0;document.cookie=e.replace(/[^+#$&^`|]/g,encodeURIComponent).replace(/\(/g,"%28").replace(/\)/g,"%29")+"="+t.replace(/[^+#$&/:<-\[\]-}]/g,encodeURIComponent)+(n&&0<=n.getTime()?";expires="+n.toUTCString():"")+(r?";domain="+r:"")+(i?";path="+i:"")+(s?";secure":"")+(a?";httponly":"")+(o?";samesite="+o:"")},exports.get=function(e){for(var t=document.cookie.split(";");t.length;){var o=t.pop(),n=(n=o.indexOf("="))<0?o.length:n;if(decodeURIComponent(o.slice(0,n).replace(/^\s+/,""))===e)return decodeURIComponent(o.slice(n+1))}return null},exports.erase=function(e,t){exports.set(e,"",{expires:-1,domain:t&&t.domain,path:t&&t.path,secure:0,httponly:0})},exports.all=function(){for(var e={},t=document.cookie.split(";");t.length;){var o=t.pop(),n=(n=o.indexOf("="))<0?o.length:n;e[decodeURIComponent(o.slice(0,n).replace(/^\s+/,""))]=decodeURIComponent(o.slice(n+1))}return e}; -------------------------------------------------------------------------------- /dist/browser-cookies.test.js: -------------------------------------------------------------------------------- 1 | function requireCookies(document, Date, exports) { exports.defaults = {}; 2 | 3 | exports.set = function(name, value, options) { 4 | // Retrieve options and defaults 5 | var opts = options || {}; 6 | var defaults = exports.defaults; 7 | 8 | // Apply default value for unspecified options 9 | var expires = opts.expires || defaults.expires; 10 | var domain = opts.domain || defaults.domain; 11 | var path = opts.path !== undefined ? opts.path : (defaults.path !== undefined ? defaults.path : '/'); 12 | var secure = opts.secure !== undefined ? opts.secure : defaults.secure; 13 | var httponly = opts.httponly !== undefined ? opts.httponly : defaults.httponly; 14 | var samesite = opts.samesite !== undefined ? opts.samesite : defaults.samesite; 15 | 16 | // Determine cookie expiration date 17 | // If succesful the result will be a valid Date, otherwise it will be an invalid Date or false(ish) 18 | var expDate = expires ? new Date( 19 | // in case expires is an integer, it should specify the number of days till the cookie expires 20 | typeof expires === 'number' ? new Date().getTime() + (expires * 864e5) : 21 | // else expires should be either a Date object or in a format recognized by Date.parse() 22 | expires 23 | ) : 0; 24 | 25 | // Set cookie 26 | document.cookie = name.replace(/[^+#$&^`|]/g, encodeURIComponent) // Encode cookie name 27 | .replace(/\(/g, '%28') 28 | .replace(/\)/g, '%29') + 29 | '=' + value.replace(/[^+#$&/:<-\[\]-}]/g, encodeURIComponent) + // Encode cookie value (RFC6265) 30 | (expDate && expDate.getTime() >= 0 ? ';expires=' + expDate.toUTCString() : '') + // Add expiration date 31 | (domain ? ';domain=' + domain : '') + // Add domain 32 | (path ? ';path=' + path : '') + // Add path 33 | (secure ? ';secure' : '') + // Add secure option 34 | (httponly ? ';httponly' : '') + // Add httponly option 35 | (samesite ? ';samesite=' + samesite : ''); // Add samesite option 36 | }; 37 | 38 | exports.get = function(name) { 39 | var cookies = document.cookie.split(';'); 40 | 41 | // Iterate all cookies 42 | while(cookies.length) { 43 | var cookie = cookies.pop(); 44 | 45 | // Determine separator index ("name=value") 46 | var separatorIndex = cookie.indexOf('='); 47 | 48 | // IE<11 emits the equal sign when the cookie value is empty 49 | separatorIndex = separatorIndex < 0 ? cookie.length : separatorIndex; 50 | 51 | var cookie_name = decodeURIComponent(cookie.slice(0, separatorIndex).replace(/^\s+/, '')); 52 | 53 | // Return cookie value if the name matches 54 | if (cookie_name === name) { 55 | return decodeURIComponent(cookie.slice(separatorIndex + 1)); 56 | } 57 | } 58 | 59 | // Return `null` as the cookie was not found 60 | return null; 61 | }; 62 | 63 | exports.erase = function(name, options) { 64 | exports.set(name, '', { 65 | expires: -1, 66 | domain: options && options.domain, 67 | path: options && options.path, 68 | secure: 0, 69 | httponly: 0} 70 | ); 71 | }; 72 | 73 | exports.all = function() { 74 | var all = {}; 75 | var cookies = document.cookie.split(';'); 76 | 77 | // Iterate all cookies 78 | while(cookies.length) { 79 | var cookie = cookies.pop(); 80 | 81 | // Determine separator index ("name=value") 82 | var separatorIndex = cookie.indexOf('='); 83 | 84 | // IE<11 emits the equal sign when the cookie value is empty 85 | separatorIndex = separatorIndex < 0 ? cookie.length : separatorIndex; 86 | 87 | // add the cookie name and value to the `all` object 88 | var cookie_name = decodeURIComponent(cookie.slice(0, separatorIndex).replace(/^\s+/, '')); 89 | all[cookie_name] = decodeURIComponent(cookie.slice(separatorIndex + 1)); 90 | } 91 | 92 | return all; 93 | }; 94 | } -------------------------------------------------------------------------------- /gulpfile.mjs: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import karma from 'karma'; 3 | import rename from 'gulp-rename'; 4 | import size from 'gulp-size'; 5 | import uglify from 'gulp-uglify'; 6 | import util from 'util'; 7 | import wrap from 'gulp-wrap'; 8 | 9 | // Defines 10 | var FILENAME_DEV = 'src/browser-cookies.js'; 11 | var FILENAME_MIN = 'browser-cookies.min.js'; 12 | var FILENAME_TST = 'browser-cookies.test.js'; 13 | 14 | // Base Karma configuration, contains everything needed for a local test run 15 | // The config is extended by gulp tasks below to add coverage/etc 16 | var karmaConfig = { 17 | basePath: '', 18 | frameworks: ['jasmine'], 19 | files: ['dist/' + FILENAME_TST, 'test/spec.js'], 20 | reporters: ['progress', 'spec'], 21 | port: 9876, 22 | colors: true, 23 | autoWatch: false, 24 | singleRun: true, 25 | preprocessors: {}, 26 | //browsers: ['ChromiumHeadless', 'Edge', 'FirefoxHeadless'], 27 | browsers: ['ChromeHeadless'], 28 | concurrency: 1 29 | //logLevel: 'DEBUG' 30 | }; 31 | 32 | gulp.task('build:production', function (done) { 33 | return gulp.src(FILENAME_DEV) 34 | .pipe(size({gzip: false, title: FILENAME_DEV + ' size:'})) 35 | .pipe(uglify()) 36 | .pipe(rename(FILENAME_MIN)) 37 | .pipe(size({gzip: false, title: FILENAME_MIN + ' size:'})) 38 | .pipe(size({gzip: true, title: FILENAME_MIN + ' size:'})) 39 | .pipe(gulp.dest('dist')); 40 | }); 41 | 42 | gulp.task('build:test', function (done) { 43 | return gulp.src(FILENAME_DEV) 44 | .pipe(rename(FILENAME_TST)) 45 | .pipe(wrap('function requireCookies(document, Date, exports) { <%= contents %> }')) 46 | .pipe(gulp.dest("dist")); 47 | }); 48 | 49 | gulp.task('karma:local', function (done) { 50 | // Copy the Karma config 51 | var config = util._extend({}, karmaConfig); 52 | 53 | // Run Karma 54 | new karma.Server(config, done).start(); 55 | }); 56 | 57 | gulp.task('karma:coverage', function (done) { 58 | // Copy the Karma config 59 | var config = util._extend({}, karmaConfig); 60 | 61 | // Enable code coverage 62 | config.reporters.push('coverage'); 63 | config.preprocessors['dist/' + FILENAME_TST] = ['coverage']; 64 | config.coverageReporter = { 65 | dir: 'coverage/', 66 | reporters: [ 67 | {type: 'lcovonly', subdir: '.' } 68 | ] 69 | }; 70 | 71 | // Run Karma 72 | new karma.Server(config, done).start(); 73 | }); 74 | 75 | gulp.task('build', gulp.series(['build:production', 'build:test'])); 76 | gulp.task('coverage', gulp.series(['build:test', 'karma:coverage'])); 77 | gulp.task('localtest', gulp.series(['build:test', 'karma:local'])); 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browser-cookies", 3 | "description": "Tiny cookies library for the browser", 4 | "version": "1.2.0", 5 | "main": "src/browser-cookies.js", 6 | "files": [ 7 | "src/browser-cookies.js", 8 | "src/browser-cookies.d.ts", 9 | "README.md", 10 | "LICENCE" 11 | ], 12 | "dependencies": { 13 | }, 14 | "devDependencies": { 15 | "concurrently": "8.2.2", 16 | "coveralls": "3.1.1", 17 | "gulp": "5.0.0", 18 | "gulp-rename": "2.0.0", 19 | "gulp-size": "5.0.0", 20 | "gulp-uglify": "3.0.2", 21 | "gulp-wrap": "0.15.0", 22 | "karma": "6.4.4", 23 | "karma-chrome-launcher": "3.2.0", 24 | "karma-edge-launcher": "0.4.2", 25 | "karma-firefox-launcher": "2.1.3", 26 | "karma-coverage": "2.2.1", 27 | "karma-jasmine": "5.1.0", 28 | "karma-spec-reporter": "0.0.36" 29 | }, 30 | "scripts": { 31 | "build": "node ./node_modules/gulp/bin/gulp.js build", 32 | "test:local": "node ./node_modules/gulp/bin/gulp.js localtest", 33 | "test:full": "concurrently \"node ./node_modules/gulp/bin/gulp.js build\" \"node ./node_modules/zuul/bin/zuul -- test/*.js\"", 34 | "test:travis_saucelabs": "node ./node_modules/gulp/bin/gulp.js coverage && node ./node_modules/zuul/bin/zuul -- test/*.js", 35 | "test": "if [ -z \"$SAUCE_ACCESS_KEY\" ]; then npm run test:local; else npm run test:travis_saucelabs; fi" 36 | }, 37 | "types": "./src/browser-cookies.d.ts", 38 | "author": { 39 | "name": "Voltace", 40 | "email": "support@voltace.com", 41 | "url": "http://www.voltace.com/" 42 | }, 43 | "license": "Unlicense", 44 | "repository": { 45 | "type": "git", 46 | "url": "git://github.com/voltace/browser-cookies" 47 | }, 48 | "bugs": { 49 | "url": "https://github.com/voltace/browser-cookies/issues" 50 | }, 51 | "keywords": [ 52 | "cookies", 53 | "cookie", 54 | "client" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /src/browser-cookies.d.ts: -------------------------------------------------------------------------------- 1 | export interface CookieOptions { 2 | expires?: number | Date | string; 3 | domain?: string; 4 | path?: string; 5 | secure?: boolean; 6 | httponly?: boolean; 7 | samesite?: "" | "Strict" | "Lax" | None; 8 | } 9 | export function set(name: string, value: string, options?: CookieOptions): void; 10 | export function get(name: string): string | null; 11 | export function erase(name: string, options?: CookieOptions): void; 12 | export function all(): { [key: string]: string }; 13 | export var defaults: CookieOptions; 14 | 15 | -------------------------------------------------------------------------------- /src/browser-cookies.js: -------------------------------------------------------------------------------- 1 | exports.defaults = {}; 2 | 3 | exports.set = function(name, value, options) { 4 | // Retrieve options and defaults 5 | var opts = options || {}; 6 | var defaults = exports.defaults; 7 | 8 | // Apply default value for unspecified options 9 | var expires = opts.expires || defaults.expires; 10 | var domain = opts.domain || defaults.domain; 11 | var path = opts.path !== undefined ? opts.path : (defaults.path !== undefined ? defaults.path : '/'); 12 | var secure = opts.secure !== undefined ? opts.secure : defaults.secure; 13 | var httponly = opts.httponly !== undefined ? opts.httponly : defaults.httponly; 14 | var samesite = opts.samesite !== undefined ? opts.samesite : defaults.samesite; 15 | 16 | // Determine cookie expiration date 17 | // If succesful the result will be a valid Date, otherwise it will be an invalid Date or false(ish) 18 | var expDate = expires ? new Date( 19 | // in case expires is an integer, it should specify the number of days till the cookie expires 20 | typeof expires === 'number' ? new Date().getTime() + (expires * 864e5) : 21 | // else expires should be either a Date object or in a format recognized by Date.parse() 22 | expires 23 | ) : 0; 24 | 25 | // Set cookie 26 | document.cookie = name.replace(/[^+#$&^`|]/g, encodeURIComponent) // Encode cookie name 27 | .replace(/\(/g, '%28') 28 | .replace(/\)/g, '%29') + 29 | '=' + value.replace(/[^+#$&/:<-\[\]-}]/g, encodeURIComponent) + // Encode cookie value (RFC6265) 30 | (expDate && expDate.getTime() >= 0 ? ';expires=' + expDate.toUTCString() : '') + // Add expiration date 31 | (domain ? ';domain=' + domain : '') + // Add domain 32 | (path ? ';path=' + path : '') + // Add path 33 | (secure ? ';secure' : '') + // Add secure option 34 | (httponly ? ';httponly' : '') + // Add httponly option 35 | (samesite ? ';samesite=' + samesite : ''); // Add samesite option 36 | }; 37 | 38 | exports.get = function(name) { 39 | var cookies = document.cookie.split(';'); 40 | 41 | // Iterate all cookies 42 | while(cookies.length) { 43 | var cookie = cookies.pop(); 44 | 45 | // Determine separator index ("name=value") 46 | var separatorIndex = cookie.indexOf('='); 47 | 48 | // IE<11 emits the equal sign when the cookie value is empty 49 | separatorIndex = separatorIndex < 0 ? cookie.length : separatorIndex; 50 | 51 | var cookie_name = decodeURIComponent(cookie.slice(0, separatorIndex).replace(/^\s+/, '')); 52 | 53 | // Return cookie value if the name matches 54 | if (cookie_name === name) { 55 | return decodeURIComponent(cookie.slice(separatorIndex + 1)); 56 | } 57 | } 58 | 59 | // Return `null` as the cookie was not found 60 | return null; 61 | }; 62 | 63 | exports.erase = function(name, options) { 64 | exports.set(name, '', { 65 | expires: -1, 66 | domain: options && options.domain, 67 | path: options && options.path, 68 | secure: 0, 69 | httponly: 0} 70 | ); 71 | }; 72 | 73 | exports.all = function() { 74 | var all = {}; 75 | var cookies = document.cookie.split(';'); 76 | 77 | // Iterate all cookies 78 | while(cookies.length) { 79 | var cookie = cookies.pop(); 80 | 81 | // Determine separator index ("name=value") 82 | var separatorIndex = cookie.indexOf('='); 83 | 84 | // IE<11 emits the equal sign when the cookie value is empty 85 | separatorIndex = separatorIndex < 0 ? cookie.length : separatorIndex; 86 | 87 | // add the cookie name and value to the `all` object 88 | var cookie_name = decodeURIComponent(cookie.slice(0, separatorIndex).replace(/^\s+/, '')); 89 | all[cookie_name] = decodeURIComponent(cookie.slice(separatorIndex + 1)); 90 | } 91 | 92 | return all; 93 | }; 94 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | browser-cookies online unit tests 6 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/spec.js: -------------------------------------------------------------------------------- 1 | // Fully stubbed test suite (document.cookie and Date object) 2 | describe("Stubbed Test Suite", function() { 3 | beforeEach(function() { 4 | var self = this; 5 | 6 | // Create document.cookie stub 7 | function documentCookieStub() { 8 | this.cookie = ''; 9 | } 10 | this.docStub = new documentCookieStub(); 11 | 12 | // Create Date stub 13 | this.dateStub = function(string) { 14 | // Create a date in UTC time (to prevent time zone dependent test results) 15 | if (string !== undefined) { 16 | var d = new Date(string); 17 | this.date = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds())); 18 | } else { 19 | this.date = new Date(Date.UTC(2030, 11, 20, 23, 15, 30, 0)); // 2030-12-20 23:15:30 20 | } 21 | 22 | // Proxy the Date function 23 | this.getTime = function() { 24 | return this.date.getTime(); 25 | }; 26 | this.setTime = function(milliseconds) { 27 | return this.date.setTime(milliseconds); 28 | }; 29 | this.toUTCString = function() { 30 | function pad(n) { 31 | return n < 10 ? '0' + n : n; 32 | } 33 | 34 | var date = this.date; 35 | var weekday = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 36 | var month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; 37 | 38 | return weekday[date.getUTCDay()] + ', ' + 39 | pad(date.getUTCDate()) + ' ' + month[date.getUTCMonth()] + ' ' + date.getUTCFullYear() + ' ' + 40 | pad(date.getUTCHours()) + ':' + pad(date.getUTCMinutes()) + ':' + pad(date.getUTCSeconds()) + ' GMT'; 41 | }; 42 | }; 43 | 44 | // Create instance of browser-cookies with 'document' and 'Date' stubbed 45 | this.browsercookies = {}; 46 | requireCookies(this.docStub, this.dateStub, this.browsercookies); 47 | }); 48 | 49 | afterEach(function() { 50 | // 51 | }); 52 | 53 | it("Get/set/erase basics", function() { 54 | // Test set 55 | this.browsercookies.set('banana', 'yellow'); 56 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 57 | 58 | // Test get 59 | this.browsercookies.set('banana', 'yellow'); 60 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 61 | 62 | // Test erase 63 | this.browsercookies.erase('banana'); 64 | expect(this.docStub.cookie).toBe('banana=;expires=Thu, 19 Dec 2030 23:15:30 GMT;path=/'); 65 | }); 66 | 67 | it("Set a cookie using all possible options", function() { 68 | // Set all options 69 | var options = { 70 | expires: 4, 71 | domain: 'www.test.com', 72 | path: '/some/path', 73 | secure: true, 74 | httponly: true, 75 | samesite: 'Strict' 76 | }; 77 | this.browsercookies.set('banana', 'yellow', options); 78 | 79 | // All options should have been applied 80 | expect(this.docStub.cookie).toBe('banana=yellow;expires=Tue, 24 Dec 2030 23:15:30 GMT;domain=www.test.com;path=/some/path;secure;httponly;samesite=Strict'); 81 | 82 | // Original options structure not modified 83 | expect(options).toEqual({ 84 | expires: 4, 85 | domain: 'www.test.com', 86 | path: '/some/path', 87 | secure: true, 88 | httponly: true, 89 | samesite: 'Strict' 90 | }); 91 | }); 92 | 93 | it("Set all possible defaults", function() { 94 | // Set new defaults 95 | this.browsercookies.defaults = { 96 | expires: 7, 97 | domain: 'www.test.com', 98 | path: '/some/path', 99 | secure: true, 100 | httponly: true, 101 | samesite: 'Strict' 102 | }; 103 | 104 | // Set cookie, all default options should be applies 105 | this.browsercookies.set('banana', 'yellow'); 106 | expect(this.docStub.cookie).toBe('banana=yellow;expires=Fri, 27 Dec 2030 23:15:30 GMT;domain=www.test.com;path=/some/path;secure;httponly;samesite=Strict'); 107 | 108 | // The defaults should not have been modified 109 | expect(this.browsercookies.defaults).toEqual({ 110 | expires: 7, 111 | domain: 'www.test.com', 112 | path: '/some/path', 113 | secure: true, 114 | httponly: true, 115 | samesite: 'Strict' 116 | }); 117 | }); 118 | 119 | it("Set empty cookie", function() { 120 | this.browsercookies.set('banana', ''); 121 | expect(this.docStub.cookie).toBe('banana=;path=/'); 122 | }); 123 | 124 | it("Set cookie using using no global defaults at all", function() { 125 | this.browsercookies.defaults = {}; 126 | this.browsercookies.set('banana', 'yellow'); 127 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 128 | }); 129 | 130 | it("Set expires option", function() { 131 | // Set cookie with custom expiration time 132 | this.browsercookies.set('banana', 'yellow', {expires: 30}); 133 | expect(this.docStub.cookie).toBe('banana=yellow;expires=Sun, 19 Jan 2031 23:15:30 GMT;path=/'); 134 | 135 | // Set a default expiration time 136 | this.browsercookies.defaults.expires = 7; 137 | this.browsercookies.set('banana', 'yellow'); 138 | expect(this.docStub.cookie).toBe('banana=yellow;expires=Fri, 27 Dec 2030 23:15:30 GMT;path=/'); 139 | 140 | // Override the default expiration time using the function option 141 | this.browsercookies.set('banana', 'yellow', {expires: 14}); 142 | expect(this.docStub.cookie).toBe('banana=yellow;expires=Fri, 03 Jan 2031 23:15:30 GMT;path=/'); 143 | }); 144 | 145 | it("Verify erase options are applied", function() { 146 | // Erase cookie with all available options 147 | this.browsercookies.erase('banana', {domain: 'example.org', path: '/a/path'}); 148 | expect(this.docStub.cookie).toBe('banana=;expires=Thu, 19 Dec 2030 23:15:30 GMT;domain=example.org;path=/a/path'); 149 | 150 | // Erase cookie with only the path set 151 | this.browsercookies.erase('banana', {path: '/a/path'}); 152 | expect(this.docStub.cookie).toBe('banana=;expires=Thu, 19 Dec 2030 23:15:30 GMT;path=/a/path'); 153 | 154 | // Erase cookie with only the domain set 155 | this.browsercookies.erase('banana', {domain: 'example.org'}); 156 | expect(this.docStub.cookie).toBe('banana=;expires=Thu, 19 Dec 2030 23:15:30 GMT;domain=example.org;path=/'); 157 | 158 | }); 159 | 160 | it("Verify erase doesn't apply default configuration", function() { 161 | // Set some defaults 162 | this.browsercookies.defaults = { 163 | expires: 7, 164 | domain: 'default.example.org', 165 | path: '/default/path', 166 | secure: true, 167 | httponly: true 168 | }; 169 | 170 | // Erase cookie should apply the domain and path specified in the defaults above 171 | this.browsercookies.erase('banana'); 172 | expect(this.docStub.cookie).toBe('banana=;expires=Thu, 19 Dec 2030 23:15:30 GMT;domain=default.example.org;path=/default/path'); 173 | 174 | // Erase cookie with specified domain and path overrules the defaults 175 | this.browsercookies.erase('banana', {domain: 'other.example.org', path: '/other/path'}); 176 | expect(this.docStub.cookie).toBe('banana=;expires=Thu, 19 Dec 2030 23:15:30 GMT;domain=other.example.org;path=/other/path'); 177 | 178 | // All options besides domain and path should be ignored 179 | this.browsercookies.erase('banana', {domain: 'other.example.org', path: '/other/path', expires: 100, secure: true, httponly: true}); 180 | expect(this.docStub.cookie).toBe('banana=;expires=Thu, 19 Dec 2030 23:15:30 GMT;domain=other.example.org;path=/other/path'); 181 | }); 182 | 183 | it("Verify all allowed formats for the 'expires' option", function() { 184 | // Verify usage of Date() format 185 | this.browsercookies.set('banana', 'yellow', {expires: new Date(2030, 11, 20)}); 186 | expect(this.docStub.cookie.replace('GMT', 'UTC')) 187 | .toBe(('banana=yellow;expires=' + new Date(2030, 11, 20).toUTCString().replace('GMT', 'UTC') + ';path=/')); 188 | 189 | // Verify usage of integer format (days till expiration) 190 | this.browsercookies.set('banana', 'yellow', {expires: 5}); 191 | expect(this.docStub.cookie).toBe('banana=yellow;expires=Wed, 25 Dec 2030 23:15:30 GMT;path=/'); 192 | 193 | // Verify usage of float format (set to one and a half day) 194 | this.browsercookies.set('banana', 'yellow', {expires: 1.5}); 195 | expect(this.docStub.cookie).toBe('banana=yellow;expires=Sun, 22 Dec 2030 11:15:30 GMT;path=/'); 196 | 197 | // Verify usage of string format (in a format recognized by Date.parse() ) 198 | this.browsercookies.set('banana', 'yellow', {expires: '01/08/2031'}); 199 | var expectedDate = (new this.dateStub('01/08/2031')).toUTCString(); 200 | expect(this.docStub.cookie).toBe('banana=yellow;expires=' + expectedDate + ';path=/'); 201 | 202 | // Verify date may be set to unix epoch 203 | this.browsercookies.set('banana', 'yellow', {expires: new Date(0)}); 204 | expectedDate = (new this.dateStub(0)).toUTCString(); 205 | expect(this.docStub.cookie).toBe('banana=yellow;expires=' + expectedDate + ';path=/'); 206 | }); 207 | 208 | it("Verify unsupported formats for the 'expires' option are ignored", function() { 209 | this.browsercookies.set('banana', 'yellow', {expires: 'anInvalidDateString'}); 210 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 211 | 212 | this.browsercookies.set('banana', 'yellow', {expires: ['an', 'array']}); 213 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 214 | 215 | this.browsercookies.set('banana', 'yellow', {expires: NaN}); 216 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 217 | 218 | this.browsercookies.set('banana', 'yellow', {expires: null}); 219 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 220 | }); 221 | 222 | it("Set domain option", function() { 223 | // Set cookie with custom domain 224 | this.browsercookies.set('banana', 'yellow', {domain: 'www.test.com'}); 225 | expect(this.docStub.cookie).toBe('banana=yellow;domain=www.test.com;path=/'); 226 | 227 | // Set a default domain 228 | this.browsercookies.defaults.domain = 'default.domain.com'; 229 | this.browsercookies.set('banana', 'yellow'); 230 | expect(this.docStub.cookie).toBe('banana=yellow;domain=default.domain.com;path=/'); 231 | 232 | // Override the default domain using the function option 233 | this.browsercookies.set('banana', 'yellow', {domain: 'override.domain.com'}); 234 | expect(this.docStub.cookie).toBe('banana=yellow;domain=override.domain.com;path=/'); 235 | }); 236 | 237 | it("Set path option", function() { 238 | // Path defaults to '/' 239 | this.browsercookies.set('banana', 'yellow'); 240 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 241 | 242 | // Set cookie with custom path 243 | this.browsercookies.set('banana', 'yellow', {path: '/some/path'}); 244 | expect(this.docStub.cookie).toBe('banana=yellow;path=/some/path'); 245 | 246 | // Set cookie with an empty path (the browser will use the current path) 247 | this.browsercookies.set('banana', 'yellow', {path: ''}); 248 | expect(this.docStub.cookie).toBe('banana=yellow'); 249 | 250 | // Change the default path 251 | this.browsercookies.defaults.path = '/a/default/path'; 252 | this.browsercookies.set('banana', 'yellow'); 253 | expect(this.docStub.cookie).toBe('banana=yellow;path=/a/default/path'); 254 | 255 | // Override the default path using the function option 256 | this.browsercookies.set('banana', 'yellow', {path: '/override/path'}); 257 | expect(this.docStub.cookie).toBe('banana=yellow;path=/override/path'); 258 | 259 | // Default path may set set to '' 260 | this.browsercookies.defaults.path = ''; 261 | this.browsercookies.set('banana', 'yellow'); 262 | expect(this.docStub.cookie).toBe('banana=yellow'); 263 | }); 264 | 265 | it("Set secure option", function() { 266 | // Set cookie with the secure option 267 | this.browsercookies.set('banana', 'yellow', {secure: true}); 268 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;secure'); 269 | 270 | // Set cookie without the secure option 271 | this.browsercookies.set('banana', 'yellow', {secure: false}); 272 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 273 | 274 | // Change the default to true 275 | this.browsercookies.defaults.secure = true; 276 | this.browsercookies.set('banana', 'yellow'); 277 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;secure'); 278 | 279 | // Override the default to false using the function option 280 | this.browsercookies.set('banana', 'yellow', {secure: false}); 281 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 282 | 283 | // Change the default to false 284 | this.browsercookies.defaults.secure = false; 285 | this.browsercookies.set('banana', 'yellow'); 286 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 287 | 288 | // Override the default to true using the function option 289 | this.browsercookies.set('banana', 'yellow', {secure: true}); 290 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;secure'); 291 | }); 292 | 293 | it("Set httponly option", function() { 294 | // Set cookie with the httponly option 295 | this.browsercookies.set('banana', 'yellow', {httponly: true}); 296 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;httponly'); 297 | 298 | // Set cookie without the httponly option 299 | this.browsercookies.set('banana', 'yellow', {httponly: false}); 300 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 301 | 302 | // Change the default to true 303 | this.browsercookies.defaults.httponly = true; 304 | this.browsercookies.set('banana', 'yellow'); 305 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;httponly'); 306 | 307 | // Override the default to false using the function option 308 | this.browsercookies.set('banana', 'yellow', {httponly: false}); 309 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 310 | 311 | // Change the default to false 312 | this.browsercookies.defaults.httponly = false; 313 | this.browsercookies.set('banana', 'yellow'); 314 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 315 | 316 | // Override the default to true using the function option 317 | this.browsercookies.set('banana', 'yellow', {httponly: true}); 318 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;httponly'); 319 | }); 320 | 321 | it("Set samesite option", function() { 322 | // Set cookie with the samesite option (to Strict) 323 | this.browsercookies.set('banana', 'yellow', {samesite: 'Strict'}); 324 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;samesite=Strict'); 325 | 326 | // Set cookie with the samesite option (to Lax) 327 | this.browsercookies.set('banana', 'yellow', {samesite: 'Lax'}); 328 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;samesite=Lax'); 329 | 330 | // Set cookie without the samesite option 331 | this.browsercookies.set('banana', 'yellow', {samesite: ''}); 332 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 333 | 334 | // Change the default to 'Lax' 335 | this.browsercookies.defaults.samesite = 'Lax'; 336 | this.browsercookies.set('banana', 'yellow'); 337 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;samesite=Lax'); 338 | 339 | // Override the default using the function option to disable samesite 340 | this.browsercookies.set('banana', 'yellow', {samesite: ''}); 341 | expect(this.docStub.cookie).toBe('banana=yellow;path=/'); 342 | 343 | // Override the default using the function option (to 'Strict') 344 | this.browsercookies.set('banana', 'yellow', {samesite: 'Strict'}); 345 | expect(this.docStub.cookie).toBe('banana=yellow;path=/;samesite=Strict'); 346 | }); 347 | 348 | it("Verify cookie name encoding", function() { 349 | // Should apply URI encoding 350 | this.browsercookies.set('báñâñâ', 'yellow'); 351 | expect(this.docStub.cookie).toBe('b%C3%A1%C3%B1%C3%A2%C3%B1%C3%A2=yellow;path=/'); 352 | 353 | // rfc6265 specifies a cookie name is of the `token` type as defined in rfc2616: 354 | // token = 1* 355 | // separators = "(" | ")" | "<" | ">" | "@" 356 | // | "," | ";" | ":" | "\" | <"> 357 | // | "/" | "[" | "]" | "?" | "=" 358 | // | "{" | "}" | SP | HT 359 | // 360 | // Note that a CHAR is defined as any US-ASCII character (octets 0 - 127) 361 | var separators = { 362 | '(' : '%28', 363 | ')' : '%29', 364 | '<' : '%3C', 365 | '>' : '%3E', 366 | '@' : '%40', 367 | ',' : '%2C', 368 | ';' : '%3B', 369 | ':' : '%3A', 370 | '\\' : '%5C', 371 | '\"' : '%22', 372 | '/' : '%2F', 373 | '[' : '%5B', 374 | ']' : '%5D', 375 | '?' : '%3F', 376 | '=' : '%3D', 377 | '{' : '%7B', 378 | '}' : '%7D', 379 | ' ' : '%20', 380 | '\t' : '%09' 381 | }; 382 | 383 | // Check whether all separators are encoded 384 | for (var separator in separators) { 385 | this.browsercookies.set('cookie' + separator, 'value'); 386 | expect(this.docStub.cookie).toBe('cookie' + separators[separator] + '=value;path=/'); 387 | } 388 | 389 | // Check whether all separators are also encoded when the same separate is present multiple times 390 | for (var separator in separators) { 391 | this.browsercookies.set('cookie' + separator + '_' + separator, 'value2'); 392 | expect(this.docStub.cookie).toBe('cookie' + separators[separator] + '_' + separators[separator] + '=value2;path=/'); 393 | } 394 | 395 | // Check whether CTLs are encoded 396 | this.browsercookies.set('\x10', 'value'); expect(this.docStub.cookie).toBe('%10=value;path=/'); 397 | this.browsercookies.set('\x7F', 'value'); expect(this.docStub.cookie).toBe('%7F=value;path=/'); 398 | 399 | // The '%' sign should be encoded as it's used as prefix for percentage encoding 400 | this.browsercookies.set('%', 'value'); expect(this.docStub.cookie).toBe('%25=value;path=/'); 401 | 402 | // Check whether all US-ASCII CHARS except for separators and CTLs are encoded 403 | for (var i = 0; i < 256; i++) { 404 | var ascii = String.fromCharCode(i); 405 | 406 | // Skip CTLs, the % sign and separators 407 | if (i < 32 || i >= 127 || ascii == '%' || separators.hasOwnProperty(ascii)) { 408 | continue; 409 | } 410 | 411 | this.browsercookies.set('cookie' + ascii, 'value'); 412 | expect(this.docStub.cookie).toBe('cookie' + ascii + '=value;path=/'); 413 | } 414 | }); 415 | 416 | it("Verify cookie name decoding", function() { 417 | this.docStub.cookie = 'b%C3%A1%C3%B1%C3%A2%C3%B1%C3%A2=yellow'; 418 | expect(this.browsercookies.get('báñâñâ')).toBe('yellow'); 419 | expect(this.browsercookies.all()['báñâñâ']).toBe('yellow'); 420 | }); 421 | 422 | it("Verify cookie name parsing using whitespace", function() { 423 | // Without whitespace 424 | this.docStub.cookie = 'a=1;b=2;c=3'; 425 | expect(this.browsercookies.get('a')).toBe('1'); 426 | expect(this.browsercookies.get('b')).toBe('2'); 427 | expect(this.browsercookies.get('c')).toBe('3'); 428 | expect(this.browsercookies.all()).toEqual({'a':'1', 'b':'2', 'c':'3'}); 429 | 430 | // With leading whitespace 431 | this.docStub.cookie = 'a=1; b=2;c=3'; 432 | expect(this.browsercookies.get('a')).toBe('1'); 433 | expect(this.browsercookies.get('b')).toBe('2'); 434 | expect(this.browsercookies.get('c')).toBe('3'); 435 | expect(this.browsercookies.all()).toEqual({'a':'1', 'b':'2', 'c':'3'}); 436 | }); 437 | 438 | it("Verify cookie value encoding", function() { 439 | // Should apply URI encoding 440 | this.browsercookies.set('banana', '¿yéllów?'); 441 | expect(this.docStub.cookie).toBe('banana=%C2%BFy%C3%A9ll%C3%B3w?;path=/'); 442 | 443 | // Should not modify the original value 444 | var value = '¿yéllów?'; 445 | this.browsercookies.set('banana', value); 446 | expect(this.docStub.cookie).toBe('banana=%C2%BFy%C3%A9ll%C3%B3w?;path=/'); 447 | expect(value).toBe('¿yéllów?'); 448 | 449 | // RFC 6265 (http://tools.ietf.org/html/rfc6265) is the 'real world' cookies specification. 450 | // The specification allows the following subset of ASCII characters to be unescaped: 451 | // hex : dec : ASCII 452 | // 0x21 : 33 : ! 453 | this.browsercookies.set('a', '!'); expect(this.docStub.cookie).toBe('a=!;path=/'); 454 | // 0x23-2B: 35-43 : #$%&'()*+ (note that % should be encoded because it's the prefix for percent encoding) 455 | this.browsercookies.set('b', '#$%&\'()*+'); expect(this.docStub.cookie).toBe('b=#$%25&\'()*+;path=/'); 456 | // 0x2D-3A: 45-58 : -./0123456789: 457 | this.browsercookies.set('c', '-./0123456789:'); expect(this.docStub.cookie).toBe('c=-./0123456789:;path=/'); 458 | // 0x3C-5B: 60-91 : <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[ 459 | this.browsercookies.set('d', '<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ['); expect(this.docStub.cookie).toBe('d=<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[;path=/'); 460 | // 0x5D-7E: 93-126: ]^_`abcdefghijklmnopqrstuvwxyz{|}~ 461 | this.browsercookies.set('e', ']^_`abcdefghijklmnopqrstuvwxyz{|}~'); expect(this.docStub.cookie).toBe('e=]^_`abcdefghijklmnopqrstuvwxyz{|}~;path=/'); 462 | 463 | // Now check the inverse of above: whether remaining character ranges are percent encoded (they should be) 464 | this.browsercookies.set('f_CTL', '\x10'); expect(this.docStub.cookie).toBe('f_CTL=%10;path=/'); 465 | this.browsercookies.set('f_whitespace', ' ' ); expect(this.docStub.cookie).toBe('f_whitespace=%20;path=/'); 466 | this.browsercookies.set('f_DQUOTE', '"' ); expect(this.docStub.cookie).toBe('f_DQUOTE=%22;path=/'); 467 | this.browsercookies.set('f_comma', ',' ); expect(this.docStub.cookie).toBe('f_comma=%2C;path=/'); 468 | this.browsercookies.set('f_semicolon', ';' ); expect(this.docStub.cookie).toBe('f_semicolon=%3B;path=/'); 469 | this.browsercookies.set('f_backslash', '\\' ); expect(this.docStub.cookie).toBe('f_backslash=%5C;path=/'); 470 | this.browsercookies.set('f_CTL2', '\x7F'); expect(this.docStub.cookie).toBe('f_CTL2=%7F;path=/'); 471 | }); 472 | 473 | describe("Verify compatibility with PHP server side", function() { 474 | it("Using PHP setcookie() - doesn't encode the plus sign properly", function() { 475 | // PHP output was generated using PHP 5.5 476 | // http://php.net/manual/en/function.setcookie.php 477 | 478 | // 479 | this.docStub.cookie = 'banana=%C2%BFy%C3%A9ll%C3%B3w%3F'; 480 | expect(this.browsercookies.get('banana')).toBe('¿yéllów?'); 481 | 482 | // 483 | this.docStub.cookie = 'a=%21%23%24%25%26%27%28%29%2A%2B-.%2F0123456789%3A'; 484 | expect(this.browsercookies.get('a')).toBe('!#$%&\'()*+-./0123456789:'); 485 | 486 | // ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ['); ?> 487 | this.docStub.cookie = 'b=%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B'; 488 | expect(this.browsercookies.get('b')).toBe('<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ['); 489 | 490 | // 491 | this.docStub.cookie = 'c=%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E'; 492 | expect(this.browsercookies.get('c')).toBe(']^_`abcdefghijklmnopqrstuvwxyz{|}~'); 493 | 494 | // 495 | this.docStub.cookie = 'f=%10'; 496 | expect(this.browsercookies.get('f')).toBe('\x10'); 497 | 498 | // 499 | this.docStub.cookie = 'f=+'; 500 | expect(this.browsercookies.get('f')).toBe('+'); // Note: should have been ' ' instead of '+' 501 | 502 | // 503 | this.docStub.cookie = 'f=%22'; 504 | expect(this.browsercookies.get('f')).toBe('"'); 505 | 506 | // 507 | this.docStub.cookie = 'f=%2C'; 508 | expect(this.browsercookies.get('f')).toBe(','); 509 | 510 | // 511 | this.docStub.cookie = 'f=%3B'; 512 | expect(this.browsercookies.get('f')).toBe(';'); 513 | 514 | // 515 | this.docStub.cookie = 'f=%5C'; 516 | expect(this.browsercookies.get('f')).toBe('\\'); 517 | 518 | // 519 | this.docStub.cookie = 'f=%7F'; 520 | expect(this.browsercookies.get('f')).toBe('\x7F'); 521 | 522 | // PHP cookie array notation 523 | // 524 | // 525 | this.docStub.cookie = 'cookie[one]=1; cookie[two]=2'; 526 | expect(this.browsercookies.get('cookie[one]')).toBe('1'); 527 | expect(this.browsercookies.get('cookie[two]')).toBe('2'); 528 | 529 | // PHP will overwrite existing cookies (which is the correct behavior) 530 | // 531 | // 532 | this.docStub.cookie = 'c=2'; 533 | expect(this.browsercookies.get('c')).toBe('2'); 534 | }); 535 | 536 | it("Using PHP setrawcookie() and rawurlencode", function() { 537 | // PHP output was generated using PHP 5.5 538 | // http://php.net/manual/en/function.setcookie.php 539 | // http://php.net/manual/en/function.rawurlencode.php 540 | 541 | // 542 | this.docStub.cookie = 'banana=%C2%BFy%C3%A9ll%C3%B3w%3F'; 543 | expect(this.browsercookies.get('banana')).toBe('¿yéllów?'); 544 | 545 | // 546 | this.docStub.cookie = 'a=%21%23%24%25%26%27%28%29%2A%2B-.%2F0123456789%3A'; 547 | expect(this.browsercookies.get('a')).toBe('!#$%&\'()*+-./0123456789:'); 548 | 549 | // ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[')); ?> 550 | this.docStub.cookie = 'b=%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B'; 551 | expect(this.browsercookies.get('b')).toBe('<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ['); 552 | 553 | // 554 | this.docStub.cookie = 'c=%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~'; 555 | expect(this.browsercookies.get('c')).toBe(']^_`abcdefghijklmnopqrstuvwxyz{|}~'); 556 | 557 | // 558 | this.docStub.cookie = 'f=%10'; 559 | expect(this.browsercookies.get('f')).toBe('\x10'); 560 | 561 | // 562 | this.docStub.cookie = 'f=%20'; 563 | expect(this.browsercookies.get('f')).toBe(' '); 564 | 565 | // 566 | this.docStub.cookie = 'f=%22'; 567 | expect(this.browsercookies.get('f')).toBe('"'); 568 | 569 | // 570 | this.docStub.cookie = 'f=%2C'; 571 | expect(this.browsercookies.get('f')).toBe(','); 572 | 573 | // 574 | this.docStub.cookie = 'f=%3B'; 575 | expect(this.browsercookies.get('f')).toBe(';'); 576 | 577 | // 578 | this.docStub.cookie = 'f=%5C'; 579 | expect(this.browsercookies.get('f')).toBe('\\'); 580 | 581 | // 582 | this.docStub.cookie = 'f=%7F'; 583 | expect(this.browsercookies.get('f')).toBe('\x7F'); 584 | 585 | // PHP cookie array notation 586 | // 587 | // 588 | this.docStub.cookie = 'cookie[one]=1; cookie[two]=2'; 589 | expect(this.browsercookies.get('cookie[one]')).toBe('1'); 590 | expect(this.browsercookies.get('cookie[two]')).toBe('2'); 591 | 592 | // PHP will overwrite existing cookies (which is the correct behavior) 593 | // 594 | // 595 | this.docStub.cookie = 'c=2'; 596 | expect(this.browsercookies.get('c')).toBe('2'); 597 | }); 598 | }); 599 | }); 600 | 601 | 602 | 603 | 604 | 605 | // Test cases to be executed using an actual browser 606 | describe("Browser-based Test Suite", function() { 607 | beforeEach(function() { 608 | // Create non stubbed instance of browser-cookies 609 | this.browsercookies = {}; 610 | requireCookies(document, Date, this.browsercookies); 611 | 612 | // Remove temporary cookies 613 | var cookies = ['banana', 'b%C3%A1%C3%B1%C3%A2%C3%B1%C3%A2', 'a', 'b', 'c', 'd', 'e', 'f']; 614 | for (var i = 0; i < cookies.length; i++) { 615 | document.cookie = cookies[i] + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/'; 616 | } 617 | }); 618 | 619 | afterEach(function() { 620 | // Remove temporary cookies 621 | var cookies = ['banana', 'b%C3%A1%C3%B1%C3%A2%C3%B1%C3%A2', 'a', 'b', 'c', 'd', 'e', 'f']; 622 | for (var i = 0; i < cookies.length; i++) { 623 | document.cookie = cookies[i] + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/'; 624 | } 625 | }); 626 | 627 | it("Get/set/erase basics", function() { 628 | // Test get (when no cookie has been set) 629 | expect(this.browsercookies.get('banana')).toBe(null); 630 | 631 | // Test set 632 | this.browsercookies.set('banana', 'yellow'); 633 | expect(this.browsercookies.get('banana')).toBe('yellow'); 634 | 635 | // Test erase 636 | this.browsercookies.erase('banana'); 637 | expect(this.browsercookies.get('banana')).toBe(null); 638 | }); 639 | 640 | it("Get/set/erase cookie using expire option", function() { 641 | // Test get (when no cookie has been set) 642 | expect(this.browsercookies.get('banana')).toBe(null); 643 | 644 | // Test set with the expires option set 645 | this.browsercookies.set('banana', 'yellow', {expires: 100}); 646 | expect(this.browsercookies.get('banana')).toBe('yellow'); 647 | 648 | // Test erase 649 | this.browsercookies.erase('banana'); 650 | expect(this.browsercookies.get('banana')).toBe(null); 651 | }); 652 | 653 | it("Set cookie using all possible options", function() { 654 | this.browsercookies.set('banana', 'yellow', { 655 | expires: 30, 656 | domain: 'www.test.com', 657 | path: '/some/path', 658 | secure: true, 659 | httponly: true 660 | }); 661 | // Note that the cookie won't be set because the domain/path/secure options are 662 | // not correct for the PhantomJS session 663 | expect(this.browsercookies.get('banana')).toBe(null); 664 | }); 665 | 666 | it("Set empty cookie", function() { 667 | this.browsercookies.set('banana', ''); 668 | expect(this.browsercookies.get('banana')).toBe(''); 669 | }); 670 | 671 | it("Erase non-existing cookie", function() { 672 | // Shouldn't raise any error 673 | expect(this.browsercookies.erase('orange')).toBe(undefined); 674 | }); 675 | 676 | it("Verify cookie name encoding and decoding", function() { 677 | this.browsercookies.set('báñâñâ', 'yellow'); 678 | expect(this.browsercookies.get('báñâñâ')).toBe('yellow'); 679 | 680 | // Check whether all US-ASCII CHARS are identical before encoding and after decoding 681 | for (var i = 0; i < 256; i++) { 682 | var name = 'cookie' + String.fromCharCode(i); 683 | 684 | // Set cookie 685 | this.browsercookies.set(name, 'value'); 686 | 687 | // Get cookie 688 | expect(this.browsercookies.get(name)).toBe('value'); 689 | expect(this.browsercookies.all()[name]).toBe('value'); 690 | 691 | // Erase cookie 692 | this.browsercookies.erase(name); 693 | expect(this.browsercookies.get(name)).toBe(null); 694 | } 695 | }); 696 | 697 | it("Verify cookie value encoding and decoding", function() { 698 | // Should apply URI encoding 699 | this.browsercookies.set('banana', '¿yéllów?'); 700 | expect(this.browsercookies.get('banana')).toBe('¿yéllów?'); 701 | expect(this.browsercookies.all()['banana']).toBe('¿yéllów?'); 702 | 703 | // Should not modify the original value 704 | var value = '¿yéllów?'; 705 | this.browsercookies.set('banana', value); 706 | expect(this.browsercookies.get('banana')).toBe('¿yéllów?'); 707 | expect(value).toBe('¿yéllów?'); 708 | 709 | // Check whether all characters allowed to be escaped by rfc6265 are identical before encoding and after decoding 710 | this.browsercookies.set('a', '!'); expect(this.browsercookies.get('a')).toBe('!'); 711 | this.browsercookies.set('b', '#$%&\'()*+'); expect(this.browsercookies.get('b')).toBe('#$%&\'()*+'); 712 | this.browsercookies.set('c', '-./0123456789:'); expect(this.browsercookies.get('c')).toBe('-./0123456789:'); 713 | this.browsercookies.set('d', '<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ['); expect(this.browsercookies.get('d')).toBe('<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ['); 714 | this.browsercookies.set('e', ']^_`abcdefghijklmnopqrstuvwxyz{|}~'); expect(this.browsercookies.get('e')).toBe(']^_`abcdefghijklmnopqrstuvwxyz{|}~'); 715 | 716 | // Check whether all characters that must be escaped by rfc6265 are identical before encoding and after decoding 717 | this.browsercookies.set('f', '\x10'); expect(this.browsercookies.get('f')).toBe('\x10'); 718 | this.browsercookies.set('f', ' ' ); expect(this.browsercookies.get('f')).toBe(' ' ); 719 | this.browsercookies.set('f', '"' ); expect(this.browsercookies.get('f')).toBe('"' ); 720 | this.browsercookies.set('f', ',' ); expect(this.browsercookies.get('f')).toBe(',' ); 721 | this.browsercookies.set('f', ';' ); expect(this.browsercookies.get('f')).toBe(';' ); 722 | this.browsercookies.set('f', '\\' ); expect(this.browsercookies.get('f')).toBe('\\' ); 723 | this.browsercookies.set('f', '\x7F'); expect(this.browsercookies.get('f')).toBe('\x7F'); 724 | }); 725 | 726 | it("Verify retrieval of multiple cookies", function() { 727 | this.browsercookies.set('a', '1'); 728 | this.browsercookies.set('b', '2'); 729 | this.browsercookies.set('c', '3'); 730 | expect(this.browsercookies.get('a')).toBe('1'); 731 | expect(this.browsercookies.get('b')).toBe('2'); 732 | expect(this.browsercookies.get('c')).toBe('3'); 733 | expect(this.browsercookies.all()).toEqual({'a':'1', 'b':'2', 'c':'3'}); 734 | }); 735 | 736 | it("Verify retrieval of multiple cookies with separator characters in the value", function() { 737 | this.browsercookies.set('a', '=1='); 738 | this.browsercookies.set('b', ':2:'); 739 | this.browsercookies.set('c', ';3;'); 740 | expect(this.browsercookies.get('a')).toBe('=1='); 741 | expect(this.browsercookies.get('b')).toBe(':2:'); 742 | expect(this.browsercookies.get('c')).toBe(';3;'); 743 | expect(this.browsercookies.all()).toEqual({'a':'=1=', 'b':':2:', 'c':';3;'}); 744 | }); 745 | }); 746 | --------------------------------------------------------------------------------