Skip to content

Instantly share code, notes, and snippets.

@rynomad
Last active April 14, 2023 20:57
Show Gist options
  • Save rynomad/b123f1698655bbc9c0c155c4485ba748 to your computer and use it in GitHub Desktop.
Save rynomad/b123f1698655bbc9c0c155c4485ba748 to your computer and use it in GitHub Desktop.
Jasmine TestRunner for ChatMonkey
// ==UserScript==
// @name Jasmine Test Runner
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Run Jasmine tests on a page with GM.communicator
// @author You
// @match *://*/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.10.0/jasmine.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.10.0/jasmine-html.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.10.0/boot.min.js
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/chai/4.3.4/chai.min.js
// @grant GM.addStyle
// @grant GM.deleteValue
// @grant GM.getValue
// @grant GM.listValues
// @grant GM.setValue
// @grant GM.xmlHttpRequest
// @grant GM.notification
// @grant GM.openInTab
// @grant GM.getResourceUrl
// @grant GM.setClipboard
// @grant GM.info
// @grant GM.registerMenuCommand
// @grant GM.unregisterMenuCommand
// @grant GM.download
// @grant GM.getTab
// @grant GM.saveTab
// @grant GM.getTabs
// @grant GM.connect
// @grant GM.setIcon
// @grant GM.fetch
// @grant GM.communicator
// @grant. GM.gpt
// @grant unsafeWindow
// @inject-into content
// ==/UserScript==
(async function() {
'use strict';
if (!unsafeWindow.location.hash) {
function clearJasmine(){
$('.jasmine_html-reporter').remove()
requestIdleCallback(clearJasmine)
}
requestIdleCallback(clearJasmine)
return;
}
// Load Jasmine CSS
const jasmineCssSource = 'https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.10.0/jasmine.min.css';
await new Promise((resolve, reject) => {
const link = document.createElement('link');
link.href = jasmineCssSource;
link.rel = 'stylesheet';
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
// Extract @require URLs from the code string
function extractRequireUrls(code) {
const regex = /\/\/\s*@require\s+(https?:\/\/[^\s]+)/g;
const urls = [];
let match;
while ((match = regex.exec(code)) !== null) {
urls.push(match[1]);
}
return urls;
}
// Create a fullscreen modal for test results
// Load required scripts and create the Jasmine test runner
async function runCode(code, test) {
const requireUrls = extractRequireUrls(code);
for (const url of requireUrls) {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
if (code.startsWith('javascript:')){
code = code.substring('javascript:'.length)
}
try {
// ... (same as before, without the `executeCode` and `executeTest` lines)
const executeCode = new Function('unsafeWindow', 'GM','$','jQuery', `const window = unsafeWindow; ${code}; return window;`)(unsafeWindow, GM, jQuery, jQuery);
if (!test){
return {
success: true
}
}
console.log("JASMINE", jasmine, jasmine.expect)
const executeTest = new Function('window','jasmine','describe','it','$','jQuery','expect',
`${test};
class CustomReporter {
constructor() {
this.finished = false;
this.result = null;
}
jasmineDone(result) {
this.finished = true;
this.result = result;
}
}
return (async () => {
const jasmineEnv = jasmine.getEnv();
const customReporter = new CustomReporter();
jasmineEnv.addReporter(customReporter);
jasmineEnv.execute();
console.log('executed test')
await new Promise((resolve) => {
const checkTestsFinished = setInterval(() => {
if (customReporter.finished) {
clearInterval(checkTestsFinished);
resolve();
}
}, 100);
});
return customReporter.result;
})();
`
)(unsafeWindow, jasmine, describe,it,jQuery, jQuery, chai.expect);
return executeTest;
} catch (e) {
return {
success: false,
error: e.message,
stack: e.stack
}
}
}
// Setup GM.communicator
const com = GM.communicator();
// Add the handler
com.registerHandler(location.hash.substring(1), async (payload, sendResponse) => {
const code = payload.code;
const test = payload.test;
console.log('got code', code, test)
const result = await runCode(code, test)
console.log('run result', result)
result.success = result.success || result.overallStatus === 'passed'
sendResponse(result)
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment