2 * Supports pseudo-"namespacing" localStorage for a given test
3 * by generating and using a unique prefix for keys. Why trounce on other
4 * tests' localStorage items when you can keep it "separated"?
6 * PrefixedLocalStorageTest: Instantiate in testharness.js tests to generate
7 * a new unique-ish prefix
8 * PrefixedLocalStorageResource: Instantiate in supporting test resource
9 * files to use/share a prefix generated by a test.
11 var PrefixedLocalStorage = function () {
12 this.prefix = ''; // Prefix for localStorage keys
13 this.param = 'prefixedLocalStorage'; // Param to use in querystrings
16 PrefixedLocalStorage.prototype.clear = function () {
17 if (this.prefix === '') { return; }
18 Object.keys(localStorage).forEach(sKey => {
19 if (sKey.indexOf(this.prefix) === 0) {
20 localStorage.removeItem(sKey);
26 * Append/replace prefix parameter and value in URI querystring
27 * Use to generate URLs to resource files that will share the prefix.
29 PrefixedLocalStorage.prototype.url = function (uri) {
30 function updateUrlParameter (uri, key, value) {
31 var i = uri.indexOf('#');
32 var hash = (i === -1) ? '' : uri.substr(i);
33 uri = (i === -1) ? uri : uri.substr(0, i);
34 var re = new RegExp(`([?&])${key}=.*?(&|$)`, 'i');
35 var separator = uri.indexOf('?') !== -1 ? '&' : '?';
36 uri = (uri.match(re)) ? uri.replace(re, `$1${key}=${value}$2`) :
37 `${uri}${separator}${key}=${value}`;
40 return updateUrlParameter(uri, this.param, this.prefix);
43 PrefixedLocalStorage.prototype.prefixedKey = function (baseKey) {
44 return `${this.prefix}${baseKey}`;
47 PrefixedLocalStorage.prototype.setItem = function (baseKey, value) {
48 localStorage.setItem(this.prefixedKey(baseKey), value);
52 * Listen for `storage` events pertaining to a particular key,
53 * prefixed with this object's prefix. Ignore when value is being set to null
56 PrefixedLocalStorage.prototype.onSet = function (baseKey, fn) {
57 window.addEventListener('storage', e => {
58 var match = this.prefixedKey(baseKey);
59 if (e.newValue !== null && e.key.indexOf(match) === 0) {
65 /*****************************************************************************
66 * Use in a testharnessjs test to generate a new key prefix.
68 * var prefixedStorage = new PrefixedLocalStorageTest();
69 * t.add_cleanup(() => prefixedStorage.cleanup());
73 var PrefixedLocalStorageTest = function () {
74 PrefixedLocalStorage.call(this);
75 this.prefix = `${document.location.pathname}-${Math.random()}-${Date.now()}-`;
77 PrefixedLocalStorageTest.prototype = Object.create(PrefixedLocalStorage.prototype);
78 PrefixedLocalStorageTest.prototype.constructor = PrefixedLocalStorageTest;
81 * Use in a cleanup function to clear out prefixed entries in localStorage
83 PrefixedLocalStorageTest.prototype.cleanup = function () {
84 this.setItem('closeAll', 'true');
88 /*****************************************************************************
89 * Use in test resource files to share a prefix generated by a
90 * PrefixedLocalStorageTest. Will look in URL querystring for prefix.
91 * Setting `close_on_cleanup` opt truthy will make this script's window listen
92 * for storage `closeAll` event from controlling test and close itself.
94 * var PrefixedLocalStorageResource({ close_on_cleanup: true });
96 var PrefixedLocalStorageResource = function (options) {
97 PrefixedLocalStorage.call(this);
98 this.options = Object.assign({}, {
99 close_on_cleanup: false
101 // Check URL querystring for prefix to use
102 var regex = new RegExp(`[?&]${this.param}(=([^&#]*)|&|#|$)`),
103 results = regex.exec(document.location.href);
104 if (results && results[2]) {
105 this.prefix = results[2];
107 // Optionally have this window close itself when the PrefixedLocalStorageTest
108 // sets a `closeAll` item.
109 if (this.options.close_on_cleanup) {
110 this.onSet('closeAll', () => {
115 PrefixedLocalStorageResource.prototype = Object.create(PrefixedLocalStorage.prototype);
116 PrefixedLocalStorageResource.prototype.constructor = PrefixedLocalStorageResource;