fix: enable added week deletion and preserve learning concepts when cloning courses

This commit is contained in:
2026-09-22 15:32:22 +09:00
parent 1d959e2c51
commit 33aac68f16
18 changed files with 633 additions and 61 deletions
+52
View File
@@ -0,0 +1,52 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const path = require('node:path');
const html = fs.readFileSync(path.join(__dirname, '../src/renderer/public/slam-viewer/index.html'), 'utf8');
const code = html.slice(html.indexOf('let maxLinear ='), html.indexOf('// A static Gazebo model'));
function create(real = true, yahboom = true) {
const handlers = {}, messages = [], intervals = new Map();
let id = 0;
const context = vm.createContext({
REAL_RUNTIME: real, USE_YAHBOOM_MODEL: yahboom, REAL_YAHBOOM_TELEOP: real && yahboom,
USE_MKMINI_MODEL: !yahboom, DIRECT_BRIDGE: true,
latestSlamState: {teleop_deadman: true}, latestSlamStateReceivedAt: Date.now(),
controlSocket: {readyState: 1, bufferedAmount: 0, send: data => messages.push(JSON.parse(data)), close() {this.readyState = 3;}},
WebSocket: {OPEN: 1}, document: {hidden: false, hasFocus: () => true, addEventListener: (name, fn) => handlers[name] = fn},
window: {addEventListener: (name, fn) => handlers[name] = fn,
setInterval: fn => {intervals.set(++id, fn); return id;}, clearInterval: id => intervals.delete(id),
setTimeout: () => ++id, clearTimeout: () => {}},
console, Date,
});
vm.runInContext(code, context);
const key = (type, value, extra = {}) => handlers[type]({key: value, target: {tagName: 'CANVAS'}, preventDefault() {}, ...extra});
return {context, handlers, messages, intervals, key};
}
let t = create();
t.key('keydown', 'i');
assert.equal(t.messages.at(-1).linear, 0.1);
assert.equal(t.messages.at(-1).deadman, true);
t.key('keyup', 'i', {target: {tagName: 'INPUT'}});
assert.equal(t.messages.at(-1).linear, 0);
assert.equal(t.intervals.size, 0);
for (const key of ['j', 'l', ',', 'u', 'o', 'm', '.']) {
t.key('keydown', key); t.key('keyup', key);
assert.equal(t.messages.at(-1).linear, 0);
assert.equal(t.messages.at(-1).angular, 0);
}
t.key('keydown', 'i'); t.handlers.blur();
assert.equal(t.messages.at(-1).linear, 0);
t.key('keydown', 'i'); t.context.document.hidden = true; t.handlers.visibilitychange();
assert.equal(t.messages.at(-1).linear, 0);
t = create();
t.key('keydown', 'i'); t.context.latestSlamStateReceivedAt = 0;
for (const tick of t.intervals.values()) tick();
assert.equal(t.messages.at(-1).linear, 0);
assert.equal(t.intervals.size, 0);
t = create(); t.context.latestSlamState.teleop_deadman = false; t.key('keydown', 'i');
assert.equal(t.messages.length, 0);
t = create(true, false); t.key('keydown', 'i'); assert.equal(t.messages.length, 0);
t = create(false); t.key('keydown', 'i'); t.key('keyup', 'i');
assert.equal(t.messages.at(-1).linear, 0.22);
assert.equal(t.intervals.size, 1);
console.log('PASS: deadman release, focus/visibility/stale-state stops, legacy bridge and other real vehicles blocked, simulation unchanged');