c++bug修复
This commit is contained in:
71
scratch-gui/scripts/diagnose-cpp.cjs
Normal file
71
scratch-gui/scripts/diagnose-cpp.cjs
Normal file
@@ -0,0 +1,71 @@
|
||||
// Read-only diagnostic: exercise the current editor interpreter with mocked game APIs.
|
||||
// Run from scratch-gui: node scripts/diagnose-cpp.cjs
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
const babel = require('@babel/core');
|
||||
const filename = path.resolve(__dirname, '../src/components/stage-cpp/cpp-parser.js');
|
||||
const transformed = babel.transformSync(fs.readFileSync(filename, 'utf8'), {
|
||||
filename, configFile: false, babelrc: false,
|
||||
plugins: ['@babel/plugin-transform-modules-commonjs']
|
||||
}).code;
|
||||
const context = {
|
||||
exports: {}, require, setTimeout,
|
||||
console: {log() {}, warn() {}, error() {}}, window: {}
|
||||
};
|
||||
vm.runInNewContext(transformed, context, {filename});
|
||||
const {CppCodeExecutor, validateCppSyntax} = context.exports;
|
||||
const main = body => `int main() {\n${body}\n}`;
|
||||
const customer = `void test() {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
turnLeft(-1);
|
||||
for (int i = 0; i < 1; i++) {
|
||||
test();
|
||||
playerMove(i);
|
||||
}
|
||||
}`;
|
||||
const cases = [
|
||||
['customer_local_i', customer, {moves: [0]}],
|
||||
['customer_renamed_inner_j', customer.replace('int i = 0; i < 2; i++', 'int j = 0; j < 2; j++'), {moves: [0]}],
|
||||
['cout_literal', main('cout << "Hello" << endl;'), {output: ['Hello\n']}],
|
||||
['cout_variable', main('int i = 7;\ncout << "i=" << i << endl;'), {output: ['i=7\n']}],
|
||||
['cout_string_assignment_shape', main('cout << "value=42" << endl;'), {output: ['value=42\n']}],
|
||||
['cout_increment_expression', main('int i = 0;\ncout << i++ << endl;\nplayerMove(i);'), {output: ['0\n'], moves: [1]}],
|
||||
['cout_increment_literal', main('cout << "C++" << endl;'), {output: ['C++\n']}],
|
||||
['cout_std_qualified', main('std::cout << "Hello" << std::endl;'), {output: ['Hello\n']}],
|
||||
['main_return_zero', main('return 0;'), {error: null}],
|
||||
['legal_space_before_call', main('playerMove (1);'), {valid: true, moves: [1]}],
|
||||
['nested_loop_shadowing', main('for (int i = 0; i < 2; i++) {\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);\n}'), {moves: [0, 1]}],
|
||||
['negative_integer_division', main('int a = -3;\na /= 2;\nplayerMove(a);'), {moves: [-1]}],
|
||||
['function_return_value', 'int answer() {\nreturn 7;\n}\n' + main('int n = answer();\nplayerMove(n);'), {moves: [7]}],
|
||||
['nested_if_break', main('for (int i = 0; i < 3; i++) {\nif (i == 1) {\nbreak;\n}\nplayerMove(i);\n}'), {moves: [0]}],
|
||||
['floating_point', main('double x = 1.5;\ncout << x << endl;'), {output: ['1.5\n']}],
|
||||
['array_index', main('int a[2] = {3, 7};\nplayerMove(a[1]);'), {moves: [7]}],
|
||||
['unknown_variable', main('playerMove(missing);'), {valid: false}],
|
||||
['early_return_zero', 'int stop() {\nreturn 0;\nplayerMove(9);\n}\n' + main('stop();'), {moves: []}]
|
||||
];
|
||||
(async () => {
|
||||
const results = [];
|
||||
for (const [name, code, expected] of cases) {
|
||||
const actual = {valid: false, moves: [], turns: [], output: [], error: null};
|
||||
context.window = {isScriptRunOver: false, cppGameAPI: {
|
||||
playerMove: async n => actual.moves.push(n),
|
||||
turnLeft: async n => actual.turns.push(n),
|
||||
cmdPrint: text => actual.output.push(text)
|
||||
}};
|
||||
const validation = await validateCppSyntax(code);
|
||||
actual.valid = validation.isValid;
|
||||
if (!actual.valid) actual.validationErrors = validation.errors;
|
||||
const executor = new CppCodeExecutor(code, null);
|
||||
if (actual.valid) {
|
||||
try { await executor.run(); } catch (error) {actual.error = error.message;}
|
||||
}
|
||||
const matchesExpected = Object.entries(expected).every(([key, value]) =>
|
||||
JSON.stringify(actual[key]) === JSON.stringify(value));
|
||||
results.push({name, expected, actual, matchesExpected});
|
||||
}
|
||||
process.stdout.write(JSON.stringify(results, null, 2) + '\n');
|
||||
})().catch(error => {console.error(error); process.exitCode = 1;});
|
||||
162
scratch-gui/scripts/test-cpp-regressions.cjs
Normal file
162
scratch-gui/scripts/test-cpp-regressions.cjs
Normal file
@@ -0,0 +1,162 @@
|
||||
// Regression tests for the editor's real interpreter and output callback.
|
||||
// Run: node scripts/test-cpp-regressions.cjs
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
const babel = require('@babel/core');
|
||||
const parserPath = path.resolve(__dirname, '../src/components/stage-cpp/cpp-parser.js');
|
||||
const source = fs.readFileSync(parserPath, 'utf8');
|
||||
const compiled = babel.transformSync(source, {
|
||||
filename: parserPath, configFile: false, babelrc: false,
|
||||
plugins: ['@babel/plugin-transform-modules-commonjs'],
|
||||
presets: process.argv.includes('--transpiled') ? ['@babel/preset-env'] : []
|
||||
}).code;
|
||||
const silentConsole = {log() {}, warn() {}, error() {}};
|
||||
const context = {exports: {}, require, setTimeout, console: silentConsole, window: {}};
|
||||
vm.runInNewContext(compiled, context, {filename: parserPath});
|
||||
const {CppCodeExecutor, validateCppSyntax} = context.exports;
|
||||
const main = body => `int main() {\n${body}\n}`;
|
||||
// Complete program from the first email screenshot; the second repeats this issue.
|
||||
const customerEmailCode = `void test() {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// 在这里编写你的C++代码来控制角色
|
||||
turnLeft(-1);
|
||||
for (int i = 0; i < 1; i++) {
|
||||
test();
|
||||
playerMove(i);
|
||||
}
|
||||
return 0;
|
||||
}`;
|
||||
// The third screenshot reports cout without supplying a cout example.
|
||||
// Add observable output to the supplied program to check both fixes together.
|
||||
const customerEmailWithOutput = customerEmailCode.replace(' playerMove(i);',
|
||||
' cout << "i=" << i << endl;\n playerMove(i);');
|
||||
|
||||
async function run(code, expected, configure) {
|
||||
const moves = [];
|
||||
const turns = [];
|
||||
const output = [];
|
||||
context.window = {isScriptRunOver: false, cppGameAPI: {
|
||||
playerMove: async n => moves.push(n),
|
||||
turnLeft: async n => turns.push(n),
|
||||
cmdPrint: text => output.push(text)
|
||||
}};
|
||||
const executor = new CppCodeExecutor(code, null);
|
||||
if (configure) configure(executor, context.window);
|
||||
const validation = await validateCppSyntax(code);
|
||||
assert.strictEqual(validation.isValid, true, JSON.stringify(validation.errors));
|
||||
await executor.run();
|
||||
if (expected.moves) assert.deepStrictEqual(moves, expected.moves);
|
||||
if (expected.turns) assert.deepStrictEqual(turns, expected.turns);
|
||||
if (expected.output) assert.deepStrictEqual(output, expected.output);
|
||||
assert.strictEqual(executor.variables, executor.globalVariables, 'scope must unwind after execution');
|
||||
assert.strictEqual(executor.callStack.length, 0);
|
||||
return executor;
|
||||
}
|
||||
|
||||
const cases = [
|
||||
['customer email complete program (screenshots 1 and 2)', customerEmailCode, {moves: [0], turns: [-1]}],
|
||||
['customer email with variable output (screenshot 3)', customerEmailWithOutput, {moves: [0], turns: [-1], output: ['i=0\n']}],
|
||||
['nested loops', main('for (int i = 0; i < 2; i++) {\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);\n}'), {moves: [0, 1]}],
|
||||
['loop initializer restores shadowed value', main('int i = 9;\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);'), {moves: [9]}],
|
||||
['loop assignment updates existing value', main('int i = 9;\nfor (i = 0; i < 2; i++) {\n}\nplayerMove(i);'), {moves: [2]}],
|
||||
['block declaration versus assignment', main('int n = 1;\nif (true) {\nint n = 7;\nplayerMove(n);\n}\nif (true) {\nn = 3;\n}\nplayerMove(n);'), {moves: [7, 3]}],
|
||||
['string block shadowing', main('string msg = "outer";\nif (true) {\nstring msg = "C++";\ncout << msg;\n}\ncout << msg;'), {output: ['C++', 'outer']}],
|
||||
['for body scope per iteration', main('int x = 9;\nfor (int i = 0; i < 2; i++) {\nplayerMove(x);\nint x = 2;\n}\nplayerMove(x);'), {moves: [9, 9, 9]}],
|
||||
['while body scope per iteration', main('int x = 9;\nint i = 0;\nwhile (i < 2) {\nplayerMove(x);\nint x = 2;\ni++;\n}\nplayerMove(x);'), {moves: [9, 9, 9]}],
|
||||
['function locals', 'void f() {\nint n = 7;\nplayerMove(n);\n}\n' + main('int n = 2;\nf();\nplayerMove(n);'), {moves: [7, 2]}],
|
||||
['all arguments evaluated before binding', 'void f(int a, int b) {\nplayerMove(a);\nplayerMove(b);\n}\n' + main('int a = 2;\nf(7, a);\nplayerMove(a);'), {moves: [7, 2, 2]}],
|
||||
['recursive locals and parameters', 'void f(int n) {\nint saved = n;\nif (n > 0) {\nf(n - 1);\n}\nplayerMove(saved);\n}\n' + main('f(2);'), {moves: [0, 1, 2]}],
|
||||
['early void return unwinds scope', 'void f() {\nint n = 8;\nreturn;\n}\n' + main('int n = 2;\nf();\nplayerMove(n);'), {moves: [2]}],
|
||||
['continue unwinds body scope', main('int x = 9;\nfor (int i = 0; i < 2; i++) {\nint x = 2;\ncontinue;\n}\nplayerMove(x);'), {moves: [9]}],
|
||||
['break does not increment', main('int i = 0;\nfor (i = 0; i < 2; i++) {\nbreak;\n}\nplayerMove(i);'), {moves: [0]}],
|
||||
['cout with equals', main('int i = 7;\ncout << "i=" << i << endl;'), {output: ['i=7\n']}],
|
||||
['cout with operator text', main('cout << "value=42 C++ -- +=" << endl;'), {output: ['value=42 C++ -- +=\n']}],
|
||||
['cout with game API text', main('cout << "playerMove(2)" << endl;'), {moves: [], output: ['playerMove(2)\n']}],
|
||||
['qualified cout', main('std::cout << "Hello" << std::endl;'), {output: ['Hello\n']}],
|
||||
['cout postfix and prefix', main('int i = 0;\ncout << i++ << endl;\ncout << ++i << endl;\ncout << i-- << endl;\ncout << --i << endl;'), {output: ['0\n', '2\n', '2\n', '0\n']}],
|
||||
['cout arithmetic', main('int i = 3;\ncout << "result=" << (i * 2 + 1) << endl;'), {output: ['result=7\n']}],
|
||||
['escaped quotes and stream delimiters', main(String.raw`cout << "a\"<<b\nC++" << endl;`), {output: ['a"<<b\nC++\n']}],
|
||||
['consecutive output fragments', main('cout << "A";\ncout << "B" << endl;'), {output: ['A', 'B\n']}]
|
||||
];
|
||||
|
||||
(async () => {
|
||||
for (const [name, code, expected] of cases) {
|
||||
try { await run(code, expected); } catch (error) {error.message = `${name}: ${error.message}`; throw error;}
|
||||
console.log(`PASS ${name}`);
|
||||
}
|
||||
// Seed the interpreter's global environment to verify lexical lookup and writes.
|
||||
const globalExecutor = await run('void f() {\ng += 1;\nplayerMove(g);\n}\n' + main('int g = 9;\nf();\nplayerMove(g);'), {moves: [2, 9]}, executor => executor.variables.set('g', 1));
|
||||
assert.strictEqual(globalExecutor.variables.get('g'), 2);
|
||||
console.log('PASS global writes survive function calls; caller locals are not visible');
|
||||
|
||||
for (const stop of [false, true]) {
|
||||
const executor = new CppCodeExecutor('void f(int n) {\nfor (int i = 0; i < 2; i++) {\nplayerMove(i);\n}\n}\n' + main('f(2);'), null);
|
||||
executor.variables.set('n', 9);
|
||||
context.window = {isScriptRunOver: false, cppGameAPI: {playerMove: async () => {
|
||||
if (stop) context.window.isScriptRunOver = true;
|
||||
else throw new Error('game failure');
|
||||
}}};
|
||||
if (stop) await executor.run();
|
||||
else await assert.rejects(executor.run(), /game failure/);
|
||||
assert.strictEqual(executor.variables, executor.globalVariables);
|
||||
assert.strictEqual(executor.variables.get('n'), 9);
|
||||
assert.strictEqual(executor.variables.has('i'), false);
|
||||
assert.strictEqual(executor.callStack.length, 0);
|
||||
assert.strictEqual(executor.currentExecutionContext, null);
|
||||
console.log(`PASS scope cleanup on ${stop ? 'stop' : 'error'}`);
|
||||
}
|
||||
|
||||
// Extract the actual React component callback, avoiding a game-engine dependency.
|
||||
const uiPath = path.resolve(__dirname, '../src/components/stage-cpp/stage-cpp-clang.jsx');
|
||||
const uiSource = fs.readFileSync(uiPath, 'utf8');
|
||||
const ast = babel.parseSync(uiSource, {configFile: false, babelrc: false, parserOpts: {plugins: ['jsx']}});
|
||||
let callback;
|
||||
function visit(node) {
|
||||
if (!node || typeof node !== 'object') return;
|
||||
if (node.type === 'ObjectProperty' && node.key.name === 'cmdPrint') callback = node.value;
|
||||
for (const value of Object.values(node)) {
|
||||
if (Array.isArray(value)) value.forEach(visit);
|
||||
else if (value && typeof value === 'object') visit(value);
|
||||
}
|
||||
}
|
||||
visit(ast);
|
||||
assert(callback, 'cmdPrint callback exists');
|
||||
for (const engineState of ['absent', 'ready', 'throwing']) {
|
||||
let displayed = '';
|
||||
const sent = [];
|
||||
const window = {};
|
||||
if (engineState !== 'absent') window.unityInstance = {SendMessage: (...args) => {
|
||||
if (engineState === 'throwing') throw new Error('engine unavailable');
|
||||
sent.push(args);
|
||||
}};
|
||||
const cmdPrint = vm.runInNewContext(`(${uiSource.slice(callback.start, callback.end)})`, {
|
||||
window, console: silentConsole, setCompilationOutput: update => {displayed = update(displayed);}
|
||||
});
|
||||
cmdPrint('A');
|
||||
cmdPrint('B\n');
|
||||
assert.strictEqual(displayed, 'AB\n');
|
||||
assert.strictEqual(window.cmd_print_text, 'AB\n');
|
||||
if (engineState === 'ready') assert.deepStrictEqual(sent[1], ['UIMain', 'SetText', 'AB\n']);
|
||||
console.log(`PASS output callback with ${engineState} engine`);
|
||||
|
||||
// Connect the real interpreter to the real UI callback, including repeated runs.
|
||||
for (let attempt = 0; attempt < 2; attempt++) {
|
||||
displayed = '';
|
||||
window.cmd_print_text = '';
|
||||
await run(customerEmailWithOutput, {moves: [0], turns: [-1]}, (executor, runtimeWindow) => {
|
||||
runtimeWindow.cppGameAPI.cmdPrint = cmdPrint;
|
||||
});
|
||||
assert.strictEqual(displayed, 'i=0\n');
|
||||
assert.strictEqual(window.cmd_print_text, 'i=0\n');
|
||||
if (engineState === 'ready') assert.deepStrictEqual(sent[sent.length - 1], ['UIMain', 'SetText', 'i=0\n']);
|
||||
}
|
||||
console.log(`PASS customer program through output callback with ${engineState} engine`);
|
||||
}
|
||||
console.log(`${cases.length + 9} regression checks passed`);
|
||||
})().catch(error => {console.error(error); process.exitCode = 1;});
|
||||
166
scratch-gui/scripts/test-devtool-control.cjs
Normal file
166
scratch-gui/scripts/test-devtool-control.cjs
Normal file
@@ -0,0 +1,166 @@
|
||||
// Run: npm run test:devtool
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
const babel = require('@babel/core');
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const controlPath = 'src/playground/devtool-control.js';
|
||||
const compile = file => babel.transformFileSync(path.join(root, file), {
|
||||
configFile: false, babelrc: false, presets: ['@babel/preset-env', '@babel/preset-react']
|
||||
}).code;
|
||||
const controlCode = compile(controlPath);
|
||||
const homeCode = compile('src/playground/home.js');
|
||||
let checks = 0;
|
||||
function test(name, action) {
|
||||
action();
|
||||
checks++;
|
||||
console.log(`PASS ${name}`);
|
||||
}
|
||||
|
||||
function page(storage = new Map(), {enabled = true, brokenStorage = false, immediate = false} = {}) {
|
||||
const starts = [];
|
||||
const effects = [];
|
||||
function detector(options) {
|
||||
starts.push(options);
|
||||
// Simulate a library probe firing during initialization with F12 already open.
|
||||
if (immediate) options.ondevtoolopen('already-open');
|
||||
}
|
||||
detector.isSuspend = false;
|
||||
const window = {
|
||||
location: {href: '/editor.html'}, onbeforeunload: () => {},
|
||||
cppCodeStopRun: () => effects.push('stop C++'),
|
||||
loadCppProject: () => effects.push('clear C++'),
|
||||
pythonCodeStopRun: () => effects.push('stop Python'),
|
||||
loadPythonProject: () => effects.push('clear Python'),
|
||||
unityInstance: {Quit: () => effects.push('quit engine')}
|
||||
};
|
||||
const sessionStorage = {
|
||||
getItem(key) {if (brokenStorage) throw new Error('storage denied'); return storage.get(key) || null;},
|
||||
setItem(key, value) {if (brokenStorage) throw new Error('storage denied'); storage.set(key, value);}
|
||||
};
|
||||
const sandbox = {
|
||||
exports: {}, window, sessionStorage,
|
||||
localStorage: {getItem: () => '/index.html'},
|
||||
vm: {
|
||||
stopAll: () => effects.push('stop Scratch'),
|
||||
editingTarget: {blocks: {deleteAllBlocks: () => effects.push('clear Scratch')}},
|
||||
emitWorkspaceUpdate: () => effects.push('update workspace')
|
||||
},
|
||||
require(name) {
|
||||
if (name === 'disable-devtool') return detector;
|
||||
if (name === './config.js') return {disabledev: enabled};
|
||||
if (name === './devtool-control.js') return api;
|
||||
throw new Error(`Unexpected import: ${name}`);
|
||||
}
|
||||
};
|
||||
const context = vm.createContext(sandbox);
|
||||
vm.runInContext(controlCode, context, {filename: controlPath});
|
||||
const api = sandbox.exports;
|
||||
return {api, starts, effects, detector, window, context};
|
||||
}
|
||||
|
||||
// Use the actual options and destructive callback from each editor mode.
|
||||
function stageOptions(file) {
|
||||
const source = fs.readFileSync(path.join(root, file), 'utf8');
|
||||
const ast = babel.parseSync(source, {configFile: false, babelrc: false, parserOpts: {plugins: ['jsx']}});
|
||||
let options;
|
||||
function visit(node) {
|
||||
if (!node || typeof node !== 'object') return;
|
||||
if (node.type === 'ImportDeclaration') {
|
||||
assert.notStrictEqual(node.source.value, 'disable-devtool', `${file} bypasses shared control`);
|
||||
}
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'initDisableDevtool') options = node.arguments[0];
|
||||
for (const value of Object.values(node)) {
|
||||
if (Array.isArray(value)) value.forEach(visit);
|
||||
else if (value && typeof value === 'object') visit(value);
|
||||
}
|
||||
}
|
||||
visit(ast);
|
||||
assert(options, `${file} must initialize through shared control`);
|
||||
// Also verify the entire JSX file remains compatible with project Babel presets.
|
||||
compile(file);
|
||||
return source.slice(options.start, options.end);
|
||||
}
|
||||
|
||||
const storage = new Map();
|
||||
test('home exposes stopDev and persists it for navigation', () => {
|
||||
const home = page(storage);
|
||||
vm.runInContext(homeCode, home.context);
|
||||
home.window.stopDev();
|
||||
assert.strictEqual(storage.get('stopDev'), '1');
|
||||
assert.strictEqual(home.detector.isSuspend, true);
|
||||
});
|
||||
|
||||
for (const mode of ['stage-unity', 'stage-python-unity', 'stage-cpp-unity']) {
|
||||
const optionSource = stageOptions(`src/components/${mode}/stage-unity.jsx`);
|
||||
test(`${mode}: home -> editor with F12 open does not start detection or clear code`, () => {
|
||||
const editor = page(storage, {immediate: true});
|
||||
const options = vm.runInContext(`(${optionSource})`, editor.context);
|
||||
editor.api.initDisableDevtool(options);
|
||||
assert.strictEqual(editor.starts.length, 0);
|
||||
assert.strictEqual(editor.detector.isSuspend, true);
|
||||
assert.strictEqual(editor.window.location.href, '/editor.html');
|
||||
assert.deepStrictEqual(editor.effects, []);
|
||||
// A refresh or another mode initialization in this tab must stay stopped.
|
||||
editor.api.initDisableDevtool(options);
|
||||
assert.strictEqual(editor.starts.length, 0);
|
||||
});
|
||||
test(`${mode}: stopDev blocks an already queued destructive callback`, () => {
|
||||
const editor = page();
|
||||
const options = vm.runInContext(`(${optionSource})`, editor.context);
|
||||
editor.api.initDisableDevtool(options);
|
||||
assert.strictEqual(editor.starts.length, 1);
|
||||
const callback = editor.starts[0];
|
||||
editor.window.stopDev();
|
||||
assert.strictEqual(callback.ignore(), true);
|
||||
callback.ondevtoolopen('queued');
|
||||
assert.strictEqual(editor.window.location.href, '/editor.html');
|
||||
assert.deepStrictEqual(editor.effects, []);
|
||||
});
|
||||
}
|
||||
|
||||
test('normal session retains default detection and redirect', () => {
|
||||
const normal = page();
|
||||
normal.api.initDisableDevtool();
|
||||
assert.strictEqual(normal.starts.length, 1);
|
||||
assert.strictEqual(normal.starts[0].ignore(), false);
|
||||
normal.starts[0].ondevtoolopen();
|
||||
assert.strictEqual(normal.window.location.href, '/index.html');
|
||||
});
|
||||
test('custom options, ignore and callback arguments remain intact', () => {
|
||||
const normal = page();
|
||||
const seen = [];
|
||||
const next = () => {};
|
||||
normal.api.initDisableDevtool({interval: 123, ignore: () => true, ondevtoolopen: (...args) => seen.push(args)});
|
||||
assert.strictEqual(normal.starts[0].interval, 123);
|
||||
assert.strictEqual(normal.starts[0].ignore(), true);
|
||||
normal.starts[0].ondevtoolopen(7, next);
|
||||
assert.deepStrictEqual(seen, [[7, next]]);
|
||||
});
|
||||
test('disabled configuration does not initialize; stopDev remains available', () => {
|
||||
const development = page(new Map(), {enabled: false});
|
||||
development.api.initDisableDevtool();
|
||||
assert.strictEqual(development.starts.length, 0);
|
||||
assert.strictEqual(typeof development.window.stopDev, 'function');
|
||||
});
|
||||
test('blocked storage still permits immediate stop on current page', () => {
|
||||
const broken = page(new Map(), {brokenStorage: true});
|
||||
broken.api.initDisableDevtool();
|
||||
broken.window.stopDev();
|
||||
assert.strictEqual(broken.detector.isSuspend, true);
|
||||
assert.strictEqual(broken.starts[0].ignore(), true);
|
||||
broken.starts[0].ondevtoolopen();
|
||||
broken.api.initDisableDevtool();
|
||||
assert.strictEqual(broken.starts.length, 1);
|
||||
assert.strictEqual(broken.window.location.href, '/editor.html');
|
||||
});
|
||||
for (const file of ['src/playground/competition.js', 'src/playground/competition_sum.js']) {
|
||||
const optionsSource = stageOptions(file);
|
||||
test(`${file}: same stopDev state is honored`, () => {
|
||||
const competition = page(storage, {immediate: true});
|
||||
competition.api.initDisableDevtool(vm.runInContext(`(${optionsSource})`, competition.context));
|
||||
assert.strictEqual(competition.starts.length, 0);
|
||||
});
|
||||
}
|
||||
console.log(`${checks} devtool regression checks passed`);
|
||||
Reference in New Issue
Block a user