All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 12m0s
185 lines
7.8 KiB
JavaScript
185 lines
7.8 KiB
JavaScript
const fs = require('fs');
|
|
const src = fs.readFileSync('shared/static/scripts/sx-browser.js', 'utf8');
|
|
|
|
global.window = { addEventListener: function(){}, history: { pushState:function(){}, replaceState:function(){} }, location: { pathname:'/', search:'' } };
|
|
global.document = {
|
|
readyState: 'complete',
|
|
createElement: function() { return { setAttribute:function(){}, appendChild:function(){}, style:{}, addEventListener:function(){} }; },
|
|
createDocumentFragment: function() { return { appendChild:function(){}, childNodes:[] }; },
|
|
createTextNode: function(t) { return { textContent: t, nodeType: 3 }; },
|
|
createElementNS: function() { return { setAttribute:function(){}, appendChild:function(){} }; },
|
|
head: { querySelector:function(){return null;}, appendChild:function(){} },
|
|
body: { querySelectorAll:function(){return [];}, querySelector:function(){return null;}, getAttribute:function(){return null;} },
|
|
querySelectorAll: function(){return [];},
|
|
querySelector: function(){return null;},
|
|
addEventListener: function(){},
|
|
cookie: ''
|
|
};
|
|
global.navigator = { serviceWorker: { register: function() { return { then: function(f) { return { catch: function(){} }; } }; } } };
|
|
global.localStorage = { getItem:function(){return null;}, setItem:function(){}, removeItem:function(){} };
|
|
global.CustomEvent = function(n,o){ this.type=n; this.detail=(o||{}).detail; };
|
|
global.MutationObserver = function(){ return { observe:function(){} }; };
|
|
global.HTMLElement = function(){};
|
|
global.EventSource = function(){};
|
|
|
|
// Prevent module.exports detection so it sets global.Sx
|
|
var _module = module;
|
|
module = undefined;
|
|
var patchedSrc = fs.readFileSync('/tmp/sx-browser-patched.js', 'utf8');
|
|
eval(patchedSrc);
|
|
module = _module;
|
|
var Sx = global.Sx;
|
|
console.log('Sx loaded:', Sx ? true : false);
|
|
|
|
var env = Object.create(Sx.componentEnv);
|
|
|
|
// Test 1: computed with SX lambda
|
|
try {
|
|
var r = Sx.eval(Sx.parse('(let ((a (signal 3)) (b (computed (fn () (* 2 (deref a)))))) (deref b))')[0], env);
|
|
console.log('TEST 1 computed:', r, '(expected 6)', r === 6 ? 'PASS' : 'FAIL');
|
|
} catch(e) {
|
|
console.log('TEST 1 computed ERROR:', e.message);
|
|
}
|
|
|
|
// Test 2: swap! with dec
|
|
try {
|
|
var r2 = Sx.eval(Sx.parse('(let ((s (signal 10))) (swap! s dec) (deref s))')[0], env);
|
|
console.log('TEST 2 swap!:', r2, '(expected 9)', r2 === 9 ? 'PASS' : 'FAIL');
|
|
} catch(e) {
|
|
console.log('TEST 2 swap! ERROR:', e.message);
|
|
}
|
|
|
|
// Test 3: effect with SX lambda
|
|
try {
|
|
var r3 = Sx.eval(Sx.parse('(let ((s (signal 0)) (log (list)) (_e (effect (fn () (append! log (deref s)))))) (reset! s 1) (first log))')[0], env);
|
|
console.log('TEST 3 effect:', r3, '(expected 0)', r3 === 0 ? 'PASS' : 'FAIL');
|
|
} catch(e) {
|
|
console.log('TEST 3 effect ERROR:', e.message);
|
|
}
|
|
|
|
// Test 4: effect re-runs on change
|
|
try {
|
|
var r4 = Sx.eval(Sx.parse('(let ((s (signal 0)) (log (list)) (_e (effect (fn () (append! log (deref s)))))) (reset! s 5) (last log))')[0], env);
|
|
console.log('TEST 4 effect re-run:', r4, '(expected 5)', r4 === 5 ? 'PASS' : 'FAIL');
|
|
} catch(e) {
|
|
console.log('TEST 4 effect re-run ERROR:', e.message);
|
|
}
|
|
|
|
// Test 5: on-click handler lambda
|
|
try {
|
|
var r5 = Sx.eval(Sx.parse('(let ((s (signal 0)) (f (fn (e) (swap! s inc)))) (f nil) (f nil) (deref s))')[0], env);
|
|
console.log('TEST 5 lambda call:', r5, '(expected 2)', r5 === 2 ? 'PASS' : 'FAIL');
|
|
} catch(e) {
|
|
console.log('TEST 5 lambda call ERROR:', e.message);
|
|
}
|
|
|
|
// Test 6: defisland + renderToDom simulation
|
|
try {
|
|
// Override DOM stubs with tracking versions
|
|
var listenCalls = [];
|
|
// Override methods on the EXISTING document object (don't replace)
|
|
document.createElement = function(tag) {
|
|
var el = {
|
|
tagName: tag,
|
|
childNodes: [],
|
|
style: {},
|
|
setAttribute: function(k, v) { this['_attr_'+k] = v; },
|
|
getAttribute: function(k) { return this['_attr_'+k]; },
|
|
appendChild: function(c) { this.childNodes.push(c); return c; },
|
|
addEventListener: function(name, fn) { listenCalls.push({el: this, name: name, fn: fn}); },
|
|
removeEventListener: function() {},
|
|
textContent: '',
|
|
nodeType: 1
|
|
};
|
|
return el;
|
|
};
|
|
document.createTextNode = function(t) { return { textContent: String(t), nodeType: 3 }; };
|
|
document.createDocumentFragment = function() {
|
|
return {
|
|
childNodes: [],
|
|
appendChild: function(c) { if (c) this.childNodes.push(c); return c; },
|
|
nodeType: 11
|
|
};
|
|
};
|
|
|
|
var env2 = Object.create(Sx.componentEnv);
|
|
|
|
// Define island
|
|
Sx.eval(Sx.parse('(defisland ~test-click (&key initial) (let ((count (signal (or initial 0)))) (div (button :on-click (fn (e) (swap! count inc)) "Click") (span (deref count)))))')[0], env2);
|
|
|
|
// Patch domListen to trace calls
|
|
// The domListen is inside the IIFE closure, so we can't patch it directly.
|
|
// Instead, patch addEventListener on elements by wrapping createElement
|
|
var origCE = document.createElement;
|
|
document.createElement = function(tag) {
|
|
var el = origCE(tag);
|
|
var origAEL = el.addEventListener;
|
|
el.addEventListener = function(name, fn) {
|
|
console.log(' addEventListener called:', tag, name);
|
|
listenCalls.push({el: el, name: name, fn: fn});
|
|
origAEL.call(el, name, fn);
|
|
};
|
|
return el;
|
|
};
|
|
|
|
// Temporarily hook into isCallable and domListen for debugging
|
|
// We can't patch the closure vars directly, but we can test via eval
|
|
var testLambda = Sx.eval(Sx.parse('(fn (e) e)')[0], env2);
|
|
console.log(' lambda type:', typeof testLambda, testLambda ? testLambda._lambda : 'no _lambda');
|
|
console.log(' Sx.isTruthy(lambda):', Sx.isTruthy(testLambda));
|
|
|
|
// Test what render-dom-element does with on-click
|
|
// Simpler test: just a button with on-click, no island
|
|
var parsed = Sx.parse('(button :on-click (fn (e) nil) "test")')[0];
|
|
console.log(' parsed expr:', JSON.stringify(parsed, function(k,v) {
|
|
if (v && v._sym) return 'SYM:' + v.name;
|
|
if (v && v._kw) return 'KW:' + v.name;
|
|
return v;
|
|
}));
|
|
var simpleTest = Sx.renderToDom(parsed, env2, null);
|
|
console.log(' simple button rendered:', simpleTest ? simpleTest.tagName : 'null');
|
|
console.log(' listeners after simple:', listenCalls.length);
|
|
|
|
// Render it
|
|
var dom = Sx.renderToDom(Sx.parse('(~test-click :initial 0)')[0], env2, null);
|
|
console.log('TEST 6 island rendered:', dom ? 'yes' : 'no');
|
|
console.log(' listeners attached:', listenCalls.length);
|
|
|
|
if (listenCalls.length > 0) {
|
|
var clickHandler = listenCalls[0];
|
|
console.log(' event name:', clickHandler.name);
|
|
// Simulate click
|
|
clickHandler.fn({type: 'click'});
|
|
// Find the span's text node
|
|
var container = dom; // div[data-sx-island]
|
|
var innerDiv = container.childNodes[0]; // the body div
|
|
console.log(' container tag:', container.tagName);
|
|
console.log(' container children:', container.childNodes.length);
|
|
if (innerDiv && innerDiv.childNodes) {
|
|
console.log(' innerDiv tag:', innerDiv.tagName);
|
|
console.log(' innerDiv children:', innerDiv.childNodes.length);
|
|
var button = innerDiv.childNodes[0];
|
|
var span = innerDiv.childNodes[1];
|
|
console.log(' button tag:', button ? button.tagName : 'none');
|
|
console.log(' span tag:', span ? span.tagName : 'none');
|
|
if (span && span.childNodes && span.childNodes[0]) {
|
|
console.log(' span text BEFORE click effect:', span.childNodes[0].textContent);
|
|
}
|
|
}
|
|
// Click again
|
|
clickHandler.fn({type: 'click'});
|
|
if (innerDiv && innerDiv.childNodes) {
|
|
var span2 = innerDiv.childNodes[1];
|
|
if (span2 && span2.childNodes && span2.childNodes[0]) {
|
|
console.log(' span text AFTER 2 clicks:', span2.childNodes[0].textContent);
|
|
console.log(' TEST 6:', span2.childNodes[0].textContent === '2' ? 'PASS' : 'FAIL (expected 2, got ' + span2.childNodes[0].textContent + ')');
|
|
}
|
|
}
|
|
} else {
|
|
console.log(' TEST 6: FAIL (no listeners attached)');
|
|
}
|
|
} catch(e) {
|
|
console.log('TEST 6 island ERROR:', e.message);
|
|
console.log(e.stack.split('\n').slice(0,5).join('\n'));
|
|
}
|