| var localStorage; |
| var document; |
| var window; |
| var XMLHttpRequest; |
| var lastCreatedXhr; |
| |
| function testGetSetValue() { |
| localStorage = new LocalStorageFake(); |
| GM_setValue('string', 'value'); |
| assert('value', GM_getValue('string')); |
| GM_setValue('integer', 123); |
| assert(123, GM_getValue('integer')); |
| GM_setValue('boolean', true); |
| assert(true, GM_getValue('boolean')); |
| |
| assert(undefined, GM_getValue('notset')); |
| assert('default', GM_getValue('notset', 'default')); |
| |
| // illegal values |
| assertThrow(function() { |
| GM_setValue('illegal value', null); |
| }); |
| assertThrow(function() { |
| GM_setValue('illegal value', 1.5); |
| }); |
| } |
| |
| function testDeleteValue() { |
| localStorage = new LocalStorageFake(); |
| GM_setValue('delete', 'value'); |
| assert('value', GM_getValue('delete')); |
| GM_deleteValue('delete'); |
| assert(undefined, GM_getValue('delete')); |
| GM_deleteValue('notset'); |
| } |
| |
| function testListValues() { |
| localStorage = new LocalStorageFake(); |
| var a = GM_listValues(); |
| assert(0, a.length); |
| |
| GM_setValue('one', 'first'); |
| a = GM_listValues(); |
| assert(1, a.length); |
| assert('one', a[0]); |
| |
| GM_setValue('two', 'second'); |
| a = GM_listValues(); |
| |
| // Test that keys that are not namespaced can't be read. |
| localStorage.setItem('three', 'third'); |
| assert(2, a.length); |
| assert(true, a.indexOf('one') >= 0); |
| assert(true, a.indexOf('two') >= 0); |
| } |
| |
| function testGetResourceURL() { |
| assertThrow(function() { |
| var resource = GM_getResourceURL('urlResourceName'); |
| }); |
| } |
| |
| function testGetResourceText() { |
| assertThrow(function() { |
| var resource = GM_getResourceText('textResourceName'); |
| }); |
| } |
| |
| function testAddStyle() { |
| var documentElement = new ElementFake(''); |
| var head = new ElementFake('head'); |
| document = new DocumentFake(documentElement, head); |
| var css = 'color: #decaff;'; |
| GM_addStyle(css); |
| assert(0, documentElement.childNodes.length); |
| assert(1, head.childNodes.length); |
| var style = head.childNodes[0]; |
| assert("style", style.tagName); |
| assert("text/css", style.type); |
| assert(1, style.childNodes.length); |
| var textNode = style.childNodes[0]; |
| assert(css, textNode.text); |
| |
| document = new DocumentFake(documentElement, null); |
| GM_addStyle(css); |
| assert(1, documentElement.childNodes.length); |
| var style = documentElement.childNodes[0]; |
| assert("text/css", style.type); |
| assert(1, style.childNodes.length); |
| textNode = style.childNodes[0]; |
| assert(css, textNode.text); |
| } |
| |
| function testXmlhttpRequest() { |
| XMLHttpRequest = XMLHttpRequestFake; |
| var url = 'http://example.com'; |
| |
| var onLoadCallback = function(state) { |
| onLoadCallbackResponseState = state; |
| }; |
| var onErrorCallback = function(state) { |
| onErrorCallbackResponseState = state; |
| }; |
| var onReadyStateChangeCallback = function(state) { |
| onReadyStateChangeCallbackResponseState = state; |
| }; |
| |
| var details = { |
| onload: onLoadCallback, |
| onerror: onErrorCallback, |
| onreadystatechange: onReadyStateChangeCallback, |
| method: 'GET', |
| url: url, |
| overrideMimeType: 'text/html', |
| headers: { |
| 'X-Header': 'foo' |
| }, |
| data: 'data' |
| }; |
| |
| GM_xmlhttpRequest(details); |
| var xhr = lastCreatedXhr; |
| |
| assert('GET', xhr.openedMethod); |
| assert(url, xhr.openedUrl); |
| assert('text/html', xhr.overrideMimeType); |
| assert('foo', xhr.requestHeaders['X-Header']); |
| assert('data', xhr.sentBody); |
| |
| xhr.responseText = 'foo'; |
| xhr.responseHeaders['X-Response'] = 'foo'; |
| xhr.status = 200; |
| xhr.statusText = 'OK'; |
| |
| xhr.readyState = 1; |
| xhr.onreadystatechange(); |
| var state = onReadyStateChangeCallbackResponseState; |
| assert(xhr.responseText, state.responseText); |
| assert(xhr.readyState, state.readyState); |
| assert('', state.responseHeaders); |
| assert(0, state.status); |
| assert('', state.statusText); |
| assert('', state.finalUrl); |
| |
| xhr.readyState = 0; |
| xhr.onerror(); |
| state = onErrorCallbackResponseState; |
| assert(xhr.responseText, state.responseText); |
| assert(xhr.readyState, state.readyState); |
| assert('', state.responseHeaders); |
| assert(0, state.status); |
| assert('', state.statusText); |
| assert('', state.finalUrl); |
| |
| xhr.readyState = 4; |
| xhr.onload(); |
| state = onLoadCallbackResponseState; |
| assert(xhr.responseText, state.responseText); |
| assert(xhr.readyState, state.readyState); |
| assert('X-Response: foo\r\n', state.responseHeaders); |
| assert(xhr.status, state.status); |
| assert(xhr.statusText, state.statusText); |
| assert(url, state.finalUrl); |
| } |
| |
| function testRegisterMenuCommand() { |
| assertThrow(function() { |
| GM_registerMenuCommand('name', function() {}, 'a', '', 'a'); |
| }); |
| } |
| |
| function testOpenInTab() { |
| var mockWindow = { |
| open: function(url, name, opt_options) { |
| this.openedUrl = url; |
| this.openedName = name; |
| this.openedOptions = opt_options; |
| } |
| }; |
| window = mockWindow; |
| var url = 'http://example.com'; |
| GM_openInTab(url); |
| assert(mockWindow.openedUrl, url); |
| } |
| |
| function testLog() { |
| var mockWindow = { |
| console: { |
| message: null, |
| log: function(message) { |
| this.message = message; |
| } |
| } |
| }; |
| window = mockWindow; |
| var message = 'hello world'; |
| GM_log(message); |
| assert(message, mockWindow.console.message); |
| } |
| |
| function LocalStorageFake() { |
| this.map_ = {}; |
| this.keys_ = []; |
| } |
| LocalStorageFake.prototype = { |
| length: 0, |
| key: function(index) { |
| if (index >= this.length) { |
| throw new Error('INDEX_SIZE_ERR'); |
| } |
| return this.keys_[index]; |
| }, |
| getItem: function(key) { |
| if (key in this.map_) { |
| return this.map_[key]; |
| } |
| return null; |
| }, |
| setItem: function(key, data) { |
| this.map_[key] = data; |
| this.updateKeys_(); |
| }, |
| removeItem: function(key) { |
| delete this.map_[key]; |
| this.updateKeys_(); |
| }, |
| clear: function() { |
| this.map_ = {}; |
| this.updateKeys_(); |
| }, |
| updateKeys_: function() { |
| var keys = []; |
| for (var key in this.map_) { |
| keys.push(key); |
| } |
| this.keys_ = keys; |
| this.length = keys.length; |
| } |
| } |
| |
| function DocumentFake(documentElement, head) { |
| this.documentElement = documentElement; |
| this.head_ = head; |
| } |
| DocumentFake.prototype = { |
| getElementsByTagName: function(tagName) { |
| if (tagName == 'head' && this.head_) { |
| return [this.head_]; |
| } |
| return []; |
| }, |
| createElement: function(tagName) { |
| return new ElementFake(tagName); |
| }, |
| createTextNode: function(text) { |
| return new TextNodeFake(text); |
| } |
| } |
| |
| function ElementFake(tagName) { |
| this.tagName = tagName; |
| this.childNodes = []; |
| } |
| ElementFake.prototype = { |
| appendChild: function(e) { |
| this.childNodes.push(e); |
| return e; |
| } |
| } |
| |
| function TextNodeFake(text) { |
| this.text = text; |
| } |
| |
| function XMLHttpRequestFake() { |
| lastCreatedXhr = this; |
| this.onload = null; |
| this.onerror = null; |
| this.onreadystatechange = null; |
| this.openedMethod = null; |
| this.openededUrl = null; |
| this.overriddenMimeType = null; |
| this.requestHeaders = {}; |
| this.sentBody = null; |
| this.responseText = null; |
| this.readyState = null; |
| this.responseHeaders = {}; |
| this.status = null; |
| this.statusText = null; |
| } |
| XMLHttpRequestFake.prototype = { |
| open: function(method, url) { |
| this.openedMethod = method; |
| this.openedUrl = url; |
| }, |
| overrideMimeType: function(mimeType) { |
| this.overrideMimeType = mimeType; |
| }, |
| setRequestHeader: function(header, value) { |
| this.requestHeaders[header] = value; |
| }, |
| send: function(opt_body) { |
| this.sentBody = opt_body; |
| }, |
| getAllResponseHeaders: function() { |
| var s = ''; |
| for (var header in this.responseHeaders) { |
| // The delimiter used in Webkit's XMLHttpRequest is \r\n, however |
| // the new Chrome networking code (and Firefox) uses \n, so watch |
| // out for this! |
| s += header + ': ' + this.responseHeaders[header] + '\r\n'; |
| } |
| return s; |
| } |
| } |
| |
| function assert(expected, actual) { |
| if (expected !== actual) { |
| throw new Error('Assert failed: "' + expected + '" !== "' + actual + '"'); |
| } |
| } |
| |
| function fail() { |
| throw new Error('Fail'); |
| } |
| |
| function assertThrow(f) { |
| var threw = false; |
| try { |
| f(); |
| } catch(e) { |
| threw = true; |
| } |
| if (!threw) { |
| throw new Error('Assert failed, expression did not throw.'); |
| } |
| } |
| |