Skip to content

Instantly share code, notes, and snippets.

@gera2ld
Last active March 28, 2023 23:39
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gera2ld/1c14672b77ade31ad8f0984725de18fc to your computer and use it in GitHub Desktop.
Save gera2ld/1c14672b77ade31ad8f0984725de18fc to your computer and use it in GitHub Desktop.
Test Violentmonkey functions
// ==UserScript==
// @name test script
// @namespace Violentmonkey Scripts
// @icon https://cn.gravatar.com/avatar/a0ad718d86d21262ccd6ff271ece08a3?s=80
// @homepage https://gist.github.com/gera2ld/1c14672b77ade31ad8f0984725de18fc
// @resource baidu https://www.baidu.com/img/baidu_jgylogo3.gif
// @resource text data:text/plain,hello,world
// @resource mochaCss https://cdn.jsdelivr.net/npm/mocha@7.0.1/mocha.min.css
// @resource cjk https://cdn.jsdelivr.net/gh/intellilab/translator.user.js/README.md
// @run-at document-start
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_getResourceURL
// @grant GM_getResourceText
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_log
// @grant GM_openInTab
// @grant GM_notification
// @grant GM_setClipboard
// @grant GM_xmlhttpRequest
// @grant window.close
// @match https://violentmonkey.github.io/*
// @match https://github.com/*
// @require https://cdn.jsdelivr.net/npm/chai@4.2.0/chai.min.js
// @require https://cdn.jsdelivr.net/npm/mocha@7.0.1/mocha.min.js
// ==/UserScript==
const { assert } = chai;
let div;
const globalThis = this;
function initialize() {
if (!div) {
div = document.createElement('div');
div.id = 'mocha';
document.body.append(div);
GM_addStyle(GM_getResourceText('mochaCss'));
GM_addStyle(`
#___gatsby { display: none; }
#mocha {
position: fixed;
top: 30px;
left: 30px;
right: 30px;
bottom: 30px;
overflow: auto;
background: white;
border: 1px solid #ddd;
box-shadow: 0 0 5px rgba(0,0,0,.4);
}
`);
}
const mocha = new Mocha({
reporter: 'html',
// ui: 'bdd',
});
mocha.ui('bdd');
mocha.suite.emit('pre-require', window, null, mocha);
return mocha;
}
GM_registerMenuCommand('[Test] GM_* functions', () => {
const mocha = initialize();
describe('wrapper', function () {
it('Object.keys', () => {
assert.ok(Object.keys(window));
});
});
describe('this / window / unsafeWindow', function () {
it('this === window', () => {
assert.strictEqual(globalThis, window, 'this should be window');
assert.notStrictEqual(globalThis, unsafeWindow, 'this should not be unsafeWindow');
});
it('this.self is configurable', () => {
assert.strictEqual(window.self, window, 'window.self should be window');
window.self = unsafeWindow;
assert.strictEqual(window.self, unsafeWindow, 'window.self should be unsafeWindow now');
window.self = window;
assert.strictEqual(window.self, window, 'window.self should be window again');
});
});
describe('GM_info', function () {
it('check fields', () => {
GM_log(GM_info);
assert(GM_info.uuid, 'UUID is missing');
assert(/(^|\n)[ \t]*\/\/ @name /.test(GM_info.scriptMetaStr), 'scriptMetaStr is invalid');
assert.typeOf(GM_info.scriptWillUpdate, 'boolean');
assert.equal(GM_info.scriptHandler, 'Violentmonkey');
assert(/^\d+\.\d+\.\d+$/.test(GM_info.version));
assert.typeOf(GM_info.injectInto, 'string');
});
});
describe('GM_*Value*', function () {
const KEY = 'test';
it('should support string', () => {
GM_deleteValue(KEY);
assert.strictEqual(GM_getValue(KEY), undefined);
assert.notOk(GM_listValues().includes(KEY));
GM_setValue(KEY, 'hello');
assert.strictEqual(GM_getValue(KEY), 'hello');
assert.ok(GM_listValues().includes(KEY));
});
it('should support object', () => {
GM_setValue(KEY, { a: 1, b: [2, 3] });
assert.deepEqual(GM_getValue(KEY), { a: 1, b: [2, 3] });
GM_deleteValue(KEY);
});
});
describe('GM_getResource*', function () {
it('GM_getResourceURL', async () => {
const urlBaidu = GM_getResourceURL('baidu');
await new Promise((resolve, reject) => {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = urlBaidu;
image.setAttribute('style', 'position:absolute;top:0;left:0;z-index:10000');
document.body.appendChild(image);
});
});
it('GM_getResourceText', () => {
assert.equal(GM_getResourceText('text'), 'hello,world', 'resource text should be `hello,world`');
assert.ok(GM_getResourceText('cjk').includes('安装'), 'CJK should be decoded');
});
});
describe('GM_openInTab', function () {
this.timeout(0);
it('GM_openInTab', async () => {
// const tab = GM_openInTab('data:text/plain,hello,world');
const tab = GM_openInTab('https://www.baidu.com/');
assert.strictEqual(tab.closed, false);
let closed = false;
const onClose = () => { closed = true; };
tab.onclose = onClose;
await delay(1000);
tab.close();
await delay(500);
assert.strictEqual(tab.closed, true, 'tab.closed should be true');
assert.strictEqual(closed, true, 'closed should be true');
});
});
describe('GM_setClipboard', function () {
it('GM_setClipboard', () => {
const string = 'setClipboard ' + new Date();
GM_setClipboard(string);
GM_log('Write to clipboard:', string);
});
});
describe('GM_xmlhttpRequest', function () {
this.timeout(0);
it('post form', async () => {
const text = await new Promise((resolve, reject) => {
const fd = new FormData();
fd.set('file', new File(['test'], 'test.txt'));
fd.set('text', 'test');
GM_xmlhttpRequest({
method: 'post',
url: 'http://httpbin.org/post',
data: fd,
onload: res => {
resolve(res.responseText);
},
onerror: reject,
});
});
const result = JSON.parse(text);
assert.deepEqual(result.args, {}, 'args should be {}');
assert.strictEqual(result.data, '', 'data should be empty');
assert.deepEqual(result.files, { file: 'test' }, 'files not correct');
assert.deepEqual(result.form, { text: 'test' }, 'form not correct');
assert.strictEqual(result.headers.Host, 'httpbin.org', 'Host should be httpbin.org');
});
it('fetch blob', async () => {
const blob = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'get',
url: 'http://httpbin.org/get',
responseType: 'blob',
onload: res => {
resolve(res.response);
},
onerror: reject,
});
});
assert.ok(blob instanceof Blob);
const text = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsText(blob);
});
const result = JSON.parse(text);
assert.deepEqual(result.args, {}, 'args should be {}');
assert.strictEqual(result.headers.Host, 'httpbin.org', 'Host should be httpbin.org');
});
it('fetch json', async () => {
const json = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'get',
url: 'http://httpbin.org/json',
responseType: 'json',
onload: res => {
resolve(res.response);
},
onerror: reject,
});
});
assert.ok(json.slideshow);
assert.strictEqual(json.slideshow.title, 'Sample Slide Show');
});
it('fetch json with progress', async () => {
const json = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: 'http://mirrors.cloud.tencent.com/npm/qrcanvas',
method: 'GET',
responseType: 'json',
onload (res) {
resolve(res.response);
},
onprogress() {
},
});
});
assert.ok(json);
assert.strictEqual(json.name, 'qrcanvas');
});
it('fetch blob with progress', async () => {
const blob = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: 'http://mirrors.cloud.tencent.com/npm/qrcanvas',
method: 'GET',
responseType: 'blob',
onload (res) {
resolve(res.response);
},
onprogress(res) {
},
});
});
const json = JSON.parse(await blob.text());
assert.ok(json);
assert.strictEqual(json.name, 'qrcanvas');
});
});
mocha.run();
});
function delay(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
function testValueChanges() {
GM_log('TEST value changes');
GM_setValue('test', `[${new Date()}] hello`);
}
function testValueShow() {
GM_log('TEST value show');
GM_log(GM_getValue('test'));
}
function testLargeValues() {
GM_log('TEST large values');
console.time('largeValues');
for (let i = 0; i < 10000; i++) {
const key = `abcdefg_${i}`;
GM_setValue(key, Math.random().toString().slice(2, 8));
}
console.timeEnd('largeValues');
}
function testDeleteValues() {
GM_log('TEST delete all values');
GM_listValues().forEach(key => {
GM_deleteValue(key);
});
GM_log('all values deleted');
}
function testListValues() {
GM_log('TEST list values');
GM_log(GM_listValues());
}
function test401() {
GM_xmlhttpRequest({
method: 'GET',
url: 'http://httpbin.org/basic-auth/user/passwd2',
onload: res => {
GM_log(res);
},
onerror: res => {
GM_log(res);
},
});
}
function testInjectInto() {
GM_log(GM_info.injectInto);
GM_log('unsafeWindow.ga:', unsafeWindow.ga);
}
GM_registerMenuCommand('[Test] notification click', () => {
const mocha = initialize();
describe('GM_notification', function () {
this.timeout(5000);
it('should support click', async () => {
await new Promise((resolve, reject) => {
GM_notification('click me', 'title', null, resolve);
});
});
});
mocha.run();
});
GM_registerMenuCommand('[Test] notification close', () => {
const mocha = initialize();
describe('GM_notification', function () {
this.timeout(5000);
it('should support close', async () => {
await new Promise((resolve, reject) => {
GM_notification({
text: 'click close',
title: 'detailed notification',
ondone: () => {
resolve();
setTimeout(() => {
GM_notification('notification with no callback');
});
},
});
});
});
});
mocha.run();
});
GM_registerMenuCommand('[Test] close window', () => {
window.close();
});
GM_registerMenuCommand('[Test] large amount of values', testLargeValues);
GM_registerMenuCommand('[Test] delete all values', testDeleteValues);
GM_registerMenuCommand('[Test] list values', testListValues);
GM_registerMenuCommand('[Test] value changes', testValueChanges);
GM_registerMenuCommand('[Test] value show', testValueShow);
GM_registerMenuCommand('[Test] 401', test401);
GM_registerMenuCommand('[Test] injectInto', testInjectInto);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment