Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
"use strict";
const TEST_URI = `data:text/html;charset=utf-8,<!DOCTYPE html>
<script>
let x = 3, y = 4;
function zzyzx() {
x = 10;
}
function zzyzx2() {
x = 10;
}
var obj = {propA: "A", propB: "B"};
var array = [1, 2, 3];
var $$ = 42;
</script>
<h1>title</h1>
`;
const EAGER_EVALUATION_PREF = "devtools.webconsole.input.eagerEvaluation";
// Basic testing of eager evaluation functionality. Expressions which can be
// eagerly evaluated should show their results, and expressions with side
// effects should not perform those side effects.
add_task(async function () {
// Open the inspector first to select a node, so that we can later test "$0"
const toolbox = await openNewTabAndToolbox(TEST_URI, "inspector");
await selectNodeWithPicker(toolbox, "h1");
info("Picker mode stopped, <h1> selected, now switching to the console");
const hud = await openConsole();
// Do an evaluation to populate $_
await executeAndWaitForResultMessage(
hud,
"'result: ' + (x + y)",
"result: 7"
);
setInputValue(hud, "x + y");
await waitForEagerEvaluationResult(hud, "7");
setInputValue(hud, "x + y + undefined");
await waitForEagerEvaluationResult(hud, "NaN");
setInputValue(hud, "1 - 1");
await waitForEagerEvaluationResult(hud, "0");
setInputValue(hud, "!true");
await waitForEagerEvaluationResult(hud, "false");
setInputValue(hud, `"ab".slice(0, 0)`);
await waitForEagerEvaluationResult(hud, `""`);
setInputValue(hud, `JSON.parse("null")`);
await waitForEagerEvaluationResult(hud, "null");
setInputValue(hud, "-x / 0");
await waitForEagerEvaluationResult(hud, "-Infinity");
setInputValue(hud, "x = 10");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "x + 1");
await waitForEagerEvaluationResult(hud, "4");
setInputValue(hud, "zzyzx()");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "x + 2");
await waitForEagerEvaluationResult(hud, "5");
setInputValue(hud, "x +");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "x + z");
await waitForEagerEvaluationResult(hud, /ReferenceError/);
setInputValue(hud, "var a = 5");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "x + a");
await waitForEagerEvaluationResult(hud, /ReferenceError/);
setInputValue(hud, '"foobar".slice(1, 5)');
await waitForEagerEvaluationResult(hud, '"ooba"');
setInputValue(hud, '"foobar".toString()');
await waitForEagerEvaluationResult(hud, '"foobar"');
setInputValue(hud, "(new Array()).push(3)");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "(new Uint32Array([1,2,3])).includes(2)");
await waitForEagerEvaluationResult(hud, "true");
setInputValue(hud, "Math.round(3.2)");
await waitForEagerEvaluationResult(hud, "3");
info("Check web console commands");
setInputValue(hud, "help()");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "$0");
await waitForEagerEvaluationResult(hud, `<h1>`);
setInputValue(hud, "$('html')");
await waitForEagerEvaluationResult(hud, `<html>`);
setInputValue(hud, "$$");
await waitForEagerEvaluationResult(hud, `42`);
info("Check that $_ wasn't polluted by eager evaluations");
setInputValue(hud, "$_");
await waitForEagerEvaluationResult(hud, `"result: 7"`);
setInputValue(hud, "'> ' + $_");
await waitForEagerEvaluationResult(hud, `"> result: 7"`);
info("Switch to editor mode");
await toggleLayout(hud);
await waitForEagerEvaluationResult(hud, `"> result: 7"`);
ok(true, "eager evaluation is still displayed in editor mode");
setInputValue(hud, "4 + 7");
await waitForEagerEvaluationResult(hud, "11");
// go back to inline layout.
await toggleLayout(hud);
setInputValue(hud, "typeof new Proxy({}, {})");
await waitForEagerEvaluationResult(hud, `"object"`);
setInputValue(hud, "typeof Proxy.revocable({}, {}).revoke");
await waitForEagerEvaluationResult(hud, `"function"`);
setInputValue(hud, "Reflect.apply(() => 1, null, [])");
await waitForEagerEvaluationResult(hud, "1");
setInputValue(
hud,
`Reflect.apply(() => {
globalThis.sideEffect = true;
return 2;
}, null, [])`
);
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.construct(Array, []).length");
await waitForEagerEvaluationResult(hud, "0");
setInputValue(
hud,
`Reflect.construct(function() {
globalThis.sideEffect = true;
}, [])`
);
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.defineProperty({}, 'a', {value: 1})");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.deleteProperty({a: 1}, 'a')");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.get({a: 1}, 'a')");
await waitForEagerEvaluationResult(hud, "1");
setInputValue(hud, "Reflect.get({get a(){return 2}, 'a')");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.getOwnPropertyDescriptor({a: 1}, 'a').value");
await waitForEagerEvaluationResult(hud, "1");
setInputValue(
hud,
`Reflect.getOwnPropertyDescriptor(
new Proxy({ a: 2 }, { getOwnPropertyDescriptor() {
globalThis.sideEffect = true;
return { value: 2 };
}}),
"a"
)`
);
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.getPrototypeOf({}) === Object.prototype");
await waitForEagerEvaluationResult(hud, "true");
setInputValue(
hud,
`Reflect.getPrototypeOf(
new Proxy({}, { getPrototypeOf() {
globalThis.sideEffect = true;
return null;
}})
)`
);
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.has({a: 1}, 'a')");
await waitForEagerEvaluationResult(hud, "true");
setInputValue(
hud,
`Reflect.has(
new Proxy({ a: 2 }, { has() {
globalThis.sideEffect = true;
return true;
}}), "a"
)`
);
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.isExtensible({})");
await waitForEagerEvaluationResult(hud, "true");
setInputValue(
hud,
`Reflect.isExtensible(
new Proxy({}, { isExtensible() {
globalThis.sideEffect = true;
return true;
}})
)`
);
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.ownKeys({a: 1})[0]");
await waitForEagerEvaluationResult(hud, `"a"`);
setInputValue(
hud,
`Reflect.ownKeys(
new Proxy({}, { ownKeys() {
globalThis.sideEffect = true;
return ['a'];
}})
)`
);
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.preventExtensions({})");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.set({}, 'a', 1)");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "Reflect.setPrototypeOf({}, null)");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "[] instanceof Array");
await waitForEagerEvaluationResult(hud, "true");
setInputValue(hud, "Int8Array.from({length: 1})[0]");
await waitForEagerEvaluationResult(hud, "0");
setInputValue(hud, "Float64Array.of(1)[0]");
await waitForEagerEvaluationResult(hud, "1");
setInputValue(hud, "array.fill()");
await waitForNoEagerEvaluationResult(hud);
setInputValue(hud, "array");
await waitForEagerEvaluationResult(hud, "Array(3) [ 1, 2, 3 ]");
info("Check that top-level await expression are not evaluated");
setInputValue(hud, "await 1; 2 + 3;");
await waitForNoEagerEvaluationResult(hud);
ok(true, "instant evaluation is disabled for top-level await expressions");
});
// Test that the currently selected autocomplete result is eagerly evaluated.
add_task(async function () {
const hud = await openNewTabAndConsole(TEST_URI);
const { jsterm } = hud;
const { autocompletePopup: popup } = jsterm;
ok(!popup.isOpen, "popup is not open");
let onPopupOpen = popup.once("popup-opened");
EventUtils.sendString("zzy");
await onPopupOpen;
await waitForEagerEvaluationResult(hud, "function zzyzx()");
EventUtils.synthesizeKey("KEY_ArrowDown");
await waitForEagerEvaluationResult(hud, "function zzyzx2()");
// works when the input isn't properly cased but matches an autocomplete item
setInputValue(hud, "o");
onPopupOpen = popup.once("popup-opened");
EventUtils.sendString("B");
await waitForEagerEvaluationResult(hud, `Object { propA: "A", propB: "B" }`);
// works when doing element access without quotes
setInputValue(hud, "obj[p");
onPopupOpen = popup.once("popup-opened");
EventUtils.sendString("RoP");
await waitForEagerEvaluationResult(hud, `"A"`);
EventUtils.synthesizeKey("KEY_ArrowDown");
await waitForEagerEvaluationResult(hud, `"B"`);
// closing the autocomplete popup updates the eager evaluation result
let onPopupClose = popup.once("popup-closed");
EventUtils.synthesizeKey("KEY_Escape");
await onPopupClose;
await waitForNoEagerEvaluationResult(hud);
info(
"Check that closing the popup by adding a space will update the instant eval result"
);
await setInputValueForAutocompletion(hud, "x");
await waitForEagerEvaluationResult(hud, "3");
EventUtils.synthesizeKey("KEY_ArrowDown");
// Navigates to the XMLDocument item in the popup
await waitForEagerEvaluationResult(hud, `function XMLDocument()`);
onPopupClose = popup.once("popup-closed");
EventUtils.sendString(" ");
await waitForEagerEvaluationResult(hud, `3`);
});
// Test that the setting works as expected.
add_task(async function () {
// start with the pref off.
await pushPref(EAGER_EVALUATION_PREF, false);
const hud = await openNewTabAndConsole(TEST_URI);
info("Check that the setting is disabled");
checkConsoleSettingState(
hud,
".webconsole-console-settings-menu-item-eager-evaluation",
false
);
// Wait for the autocomplete popup to be displayed so we know the eager evaluation could
// have occured.
const onPopupOpen = hud.jsterm.autocompletePopup.once("popup-opened");
await setInputValueForAutocompletion(hud, "x + y");
await onPopupOpen;
is(
getEagerEvaluationElement(hud),
null,
"There's no eager evaluation element"
);
hud.jsterm.autocompletePopup.hidePopup();
info("Turn on the eager evaluation");
toggleConsoleSetting(
hud,
".webconsole-console-settings-menu-item-eager-evaluation"
);
await waitFor(() => getEagerEvaluationElement(hud));
ok(true, "The eager evaluation element is now displayed");
is(
Services.prefs.getBoolPref(EAGER_EVALUATION_PREF),
true,
"Pref was changed"
);
setInputValue(hud, "1 + 2");
await waitForEagerEvaluationResult(hud, "3");
ok(true, "Eager evaluation result is displayed");
info("Turn off the eager evaluation");
toggleConsoleSetting(
hud,
".webconsole-console-settings-menu-item-eager-evaluation"
);
await waitFor(() => !getEagerEvaluationElement(hud));
is(
Services.prefs.getBoolPref(EAGER_EVALUATION_PREF),
false,
"Pref was changed"
);
ok(true, "Eager evaluation element is no longer displayed");
// reset the preference
await pushPref(EAGER_EVALUATION_PREF, true);
});
// Test that the console instant evaluation is updated on page navigation
add_task(async function () {
const start_uri = "data:text/html, Start uri";
const new_uri = "data:text/html, Test console refresh instant value";
const hud = await openNewTabAndConsole(start_uri);
setInputValue(hud, "globalThis.location.href");
await waitForEagerEvaluationResult(hud, `"${start_uri}"`);
await navigateTo(new_uri);
await waitForEagerEvaluationResult(hud, `"${new_uri}"`);
});