Bug 773565 - GCLI Autocomplete goes wild when boolean params are used in a group; r=dcamp

--HG--
rename : browser/devtools/highlighter/test/helper.js => browser/devtools/highlighter/test/helpers.js
rename : browser/devtools/responsivedesign/test/helper.js => browser/devtools/responsivedesign/test/helpers.js
rename : browser/devtools/shared/test/helper.js => browser/devtools/shared/test/helpers.js
rename : browser/devtools/styleeditor/test/helper.js => browser/devtools/styleeditor/test/helpers.js
This commit is contained in:
Joe Walker 2012-08-24 16:04:45 +01:00
parent 67f8af60a8
commit af1fd14c62
36 changed files with 5805 additions and 3347 deletions

View File

@ -21,7 +21,6 @@ gcli.addCommand({
name: 'jsb',
description: gcli.lookup('jsbDesc'),
returnValue:'string',
hidden: true,
params: [
{
name: 'url',
@ -40,7 +39,10 @@ gcli.addCommand({
name: 'indentChar',
type: {
name: 'selection',
lookup: [{name: "space", value: " "}, {name: "tab", value: "\t"}]
lookup: [
{ name: "space", value: " " },
{ name: "tab", value: "\t" }
]
},
description: gcli.lookup('jsbIndentCharDesc'),
manual: gcli.lookup('jsbIndentCharManual'),
@ -50,8 +52,7 @@ gcli.addCommand({
name: 'preserveNewlines',
type: 'boolean',
description: gcli.lookup('jsbPreserveNewlinesDesc'),
manual: gcli.lookup('jsbPreserveNewlinesManual'),
defaultValue: true
manual: gcli.lookup('jsbPreserveNewlinesManual')
},
{
name: 'preserveMaxNewlines',
@ -64,8 +65,7 @@ gcli.addCommand({
name: 'jslintHappy',
type: 'boolean',
description: gcli.lookup('jsbJslintHappyDesc'),
manual: gcli.lookup('jsbJslintHappyManual'),
defaultValue: false
manual: gcli.lookup('jsbJslintHappyManual')
},
{
name: 'braceStyle',
@ -81,58 +81,56 @@ gcli.addCommand({
name: 'spaceBeforeConditional',
type: 'boolean',
description: gcli.lookup('jsbSpaceBeforeConditionalDesc'),
manual: gcli.lookup('jsbSpaceBeforeConditionalManual'),
defaultValue: true
manual: gcli.lookup('jsbSpaceBeforeConditionalManual')
},
{
name: 'unescapeStrings',
type: 'boolean',
description: gcli.lookup('jsbUnescapeStringsDesc'),
manual: gcli.lookup('jsbUnescapeStringsManual'),
defaultValue: false
manual: gcli.lookup('jsbUnescapeStringsManual')
}
],
exec: function(args, context) {
let opts = {
indent_size: args.indentSize,
indent_char: args.indentChar,
preserve_newlines: args.preserveNewlines,
max_preserve_newlines: args.preserveMaxNewlines == -1 ?
undefined : args.preserveMaxNewlines,
jslint_happy: args.jslintHappy,
brace_style: args.braceStyle,
space_before_conditional: args.spaceBeforeConditional,
unescape_strings: args.unescapeStrings
}
let opts = {
indent_size: args.indentSize,
indent_char: args.indentChar,
preserve_newlines: args.preserveNewlines,
max_preserve_newlines: args.preserveMaxNewlines == -1 ?
undefined : args.preserveMaxNewlines,
jslint_happy: args.jslintHappy,
brace_style: args.braceStyle,
space_before_conditional: args.spaceBeforeConditional,
unescape_strings: args.unescapeStrings
}
let xhr = new XMLHttpRequest();
let xhr = new XMLHttpRequest();
try {
xhr.open("GET", args.url, true);
} catch(e) {
return gcli.lookup('jsbInvalidURL');
}
try {
xhr.open("GET", args.url, true);
} catch(e) {
return gcli.lookup('jsbInvalidURL');
}
let promise = context.createPromise();
let promise = context.createPromise();
xhr.onreadystatechange = function(aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 0) {
let browserDoc = context.environment.chromeDocument;
let browserWindow = browserDoc.defaultView;
let browser = browserWindow.gBrowser;
browser.selectedTab = browser.addTab("data:text/plain;base64," +
browserWindow.btoa(js_beautify(xhr.responseText, opts)));
promise.resolve();
}
else {
promise.resolve("Unable to load page to beautify: " + args.url + " " +
xhr.status + " " + xhr.statusText);
}
};
}
xhr.send(null);
return promise;
xhr.onreadystatechange = function(aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 0) {
let browserDoc = context.environment.chromeDocument;
let browserWindow = browserDoc.defaultView;
let browser = browserWindow.gBrowser;
browser.selectedTab = browser.addTab("data:text/plain;base64," +
browserWindow.btoa(js_beautify(xhr.responseText, opts)));
promise.resolve();
}
else {
promise.resolve("Unable to load page to beautify: " + args.url + " " +
xhr.status + " " + xhr.statusText);
}
};
}
xhr.send(null);
return promise;
}
});

View File

@ -28,7 +28,6 @@ gcli.addCommand({
{
name: "nocache",
type: "boolean",
defaultValue: false,
description: gcli.lookup("restartFirefoxNocacheDesc")
}
],

View File

@ -37,7 +37,6 @@ gcli.addCommand({
{
name: "fullpage",
type: "boolean",
defaultValue: false,
description: gcli.lookup("screenshotFullPageDesc"),
manual: gcli.lookup("screenshotFullPageManual")
},

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@ MOCHITEST_BROWSER_FILES = \
browser_cmd_settings.js \
browser_gcli_web.js \
head.js \
helper.js \
helpers.js \
$(NULL)
MOCHITEST_BROWSER_FILES += \

View File

@ -8,43 +8,87 @@ function test() {
}
function GAT_test() {
var GAT_ready = DeveloperToolbarTest.checkCalled(function() {
Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false);
helpers.setInput('addon list dictionary');
helpers.check({
input: 'addon list dictionary',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
helpers.setInput('addon list extension');
helpers.check({
input: 'addon list extension',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
helpers.setInput('addon list locale');
helpers.check({
input: 'addon list locale',
hints: '',
markup: 'VVVVVVVVVVVVVVVVV',
status: 'VALID'
});
helpers.setInput('addon list plugin');
helpers.check({
input: 'addon list plugin',
hints: '',
markup: 'VVVVVVVVVVVVVVVVV',
status: 'VALID'
});
helpers.setInput('addon list theme');
helpers.check({
input: 'addon list theme',
hints: '',
markup: 'VVVVVVVVVVVVVVVV',
status: 'VALID'
});
helpers.setInput('addon list all');
helpers.check({
input: 'addon list all',
hints: '',
markup: 'VVVVVVVVVVVVVV',
status: 'VALID'
});
helpers.setInput('addon disable Test_Plug-in_1.0.0.0');
helpers.check({
input: 'addon disable Test_Plug-in_1.0.0.0',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
helpers.setInput('addon disable WRONG');
helpers.check({
input: 'addon disable WRONG',
hints: '',
markup: 'VVVVVVVVVVVVVVEEEEE',
status: 'ERROR'
});
helpers.setInput('addon enable Test_Plug-in_1.0.0.0');
helpers.check({
input: 'addon enable Test_Plug-in_1.0.0.0',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
command: { name: 'addon enable' },
name: { value: 'Test Plug-in', status: 'VALID' },
}
});
DeveloperToolbarTest.exec({ completed: false });
});
Services.obs.addObserver(GAT_ready, "gcli_addon_commands_ready", false);
}
var GAT_ready = DeveloperToolbarTest.checkCalled(function() {
Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false);
DeveloperToolbarTest.checkInputStatus({
typed: "addon list dictionary",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list extension",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list locale",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list plugin",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list theme",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list all",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon disable Test_Plug-in_1.0.0.0",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon enable Test_Plug-in_1.0.0.0",
status: "VALID"
});
DeveloperToolbarTest.exec({ completed: false });
});

View File

@ -13,21 +13,28 @@ function test() {
}
function testCallLogStatus() {
DeveloperToolbarTest.checkInputStatus({
typed: "calllog",
status: "ERROR"
helpers.setInput('calllog');
helpers.check({
input: 'calllog',
hints: '',
markup: 'IIIIIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "calllog start",
status: "VALID",
emptyParameters: [ ]
helpers.setInput('calllog start');
helpers.check({
input: 'calllog start',
hints: '',
markup: 'VVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.checkInputStatus({
typed: "calllog start",
status: "VALID",
emptyParameters: [ ]
helpers.setInput('calllog stop');
helpers.check({
input: 'calllog stop',
hints: '',
markup: 'VVVVVVVVVVVV',
status: 'VALID'
});
}
@ -39,7 +46,7 @@ function testCallLogExec() {
});
let hud = null;
function onWebConsoleOpen(aSubject) {
var onWebConsoleOpen = DeveloperToolbarTest.checkCalled(function(aSubject) {
Services.obs.removeObserver(onWebConsoleOpen, "web-console-created");
aSubject.QueryInterface(Ci.nsISupportsString);
@ -66,7 +73,7 @@ function testCallLogExec() {
args: {},
blankOutput: true,
});
}
});
Services.obs.addObserver(onWebConsoleOpen, "web-console-created", false);

View File

@ -6,40 +6,73 @@
const TEST_URI = "data:text/html;charset=utf-8,gcli-cookie";
function test() {
DeveloperToolbarTest.test(TEST_URI, [ testCookieCommands ]);
DeveloperToolbarTest.test(TEST_URI, [ testCookieCheck, testCookieExec ]);
}
function testCookieCommands() {
DeveloperToolbarTest.checkInputStatus({
typed: "cook",
directTabText: "ie",
status: "ERROR"
function testCookieCheck() {
helpers.setInput('cookie');
helpers.check({
input: 'cookie',
hints: '',
markup: 'IIIIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie l",
directTabText: "ist",
status: "ERROR"
helpers.setInput('cookie lis');
helpers.check({
input: 'cookie lis',
hints: 't',
markup: 'IIIIIIVIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie list",
status: "VALID",
emptyParameters: [ ]
helpers.setInput('cookie list');
helpers.check({
input: 'cookie list',
hints: '',
markup: 'VVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie remove",
status: "ERROR",
emptyParameters: [ " <key>" ]
helpers.setInput('cookie remove');
helpers.check({
input: 'cookie remove',
hints: ' <key>',
markup: 'VVVVVVVVVVVVV',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie set",
status: "ERROR",
emptyParameters: [ " <key>", " <value>", " [options]" ],
helpers.setInput('cookie set');
helpers.check({
input: 'cookie set',
hints: ' <key> <value> [options]',
markup: 'VVVVVVVVVV',
status: 'ERROR'
});
helpers.setInput('cookie set fruit');
helpers.check({
input: 'cookie set fruit',
hints: ' <value> [options]',
markup: 'VVVVVVVVVVVVVVVV',
status: 'ERROR'
});
helpers.setInput('cookie set fruit ban');
helpers.check({
input: 'cookie set fruit ban',
hints: ' [options]',
markup: 'VVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
key: { value: 'fruit' },
value: { value: 'ban' },
secure: { value: false },
}
});
}
function testCookieExec() {
DeveloperToolbarTest.exec({
typed: "cookie set fruit banana",
args: {

View File

@ -7,13 +7,16 @@ const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
"test/browser_cmd_jsb_script.jsi";
function test() {
DeveloperToolbarTest.test("about:blank", [ GJT_test ]);
DeveloperToolbarTest.test("about:blank", [ /*GJT_test*/ ]);
}
function GJT_test() {
DeveloperToolbarTest.exec({
typed: "jsb AAA",
outputMatch: /valid/
helpers.setInput('jsb');
helpers.check({
input: 'jsb',
hints: ' <url> [indentSize] [indentChar] [preserveNewlines] [preserveMaxNewlines] [jslintHappy] [braceStyle] [spaceBeforeConditional] [unescapeStrings]',
markup: 'VVV',
status: 'ERROR'
});
gBrowser.addTabsProgressListener({
@ -26,26 +29,29 @@ function GJT_test() {
result = result.replace(/[\r\n]]/g, "\n");
checkResult(result);
let correct = "function somefunc() {\n" +
" for (let n = 0; n < 500; n++) {\n" +
" if (n % 2 == 1) {\n" +
" console.log(n);\n" +
" console.log(n + 1);\n" +
" }\n" +
" }\n" +
"}";
is(result, correct, "JS has been correctly prettified");
})
});
info("Checking beautification");
DeveloperToolbarTest.checkInputStatus({
typed: "jsb " + TEST_URI + " 4 space true -1 false collapse true false",
status: "VALID"
});
DeveloperToolbarTest.exec({ completed: false });
function checkResult(aResult) {
let correct = "function somefunc() {\n" +
" for (let n = 0; n < 500; n++) {\n" +
" if (n % 2 == 1) {\n" +
" console.log(n);\n" +
" console.log(n + 1);\n" +
" }\n" +
" }\n" +
"}";
is(aResult, correct, "JS has been correctly prettified");
}
helpers.setInput('jsb ' + TEST_URI);
/*
helpers.check({
input: 'jsb',
hints: ' [options]',
markup: 'VVV',
status: 'VALID'
});
*/
DeveloperToolbarTest.exec({ completed: false });
}

View File

@ -22,9 +22,12 @@ function test() {
}
function testExportHtml() {
DeveloperToolbarTest.checkInputStatus({
typed: "export html",
status: "VALID"
helpers.setInput('export html');
helpers.check({
input: 'export html',
hints: '',
markup: 'VVVVVVVVVVV',
status: 'VALID'
});
let oldOpen = content.open;
@ -53,33 +56,36 @@ function test() {
}
function testPageModReplace() {
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace",
emptyParameters: [" <search>", " <replace>", " [ignoreCase]",
" [selector]", " [root]", " [attrOnly]",
" [contentOnly]", " [attributes]"],
status: "ERROR"
helpers.setInput('pagemod replace');
helpers.check({
input: 'pagemod replace',
hints: ' <search> <replace> [ignoreCase] [selector] [root] [attrOnly] [contentOnly] [attributes]',
markup: 'VVVVVVVVVVVVVVV',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace some foo",
emptyParameters: [" [ignoreCase]", " [selector]", " [root]",
" [attrOnly]", " [contentOnly]", " [attributes]"],
status: "VALID"
helpers.setInput('pagemod replace some foo');
helpers.check({
input: 'pagemod replace some foo',
hints: ' [ignoreCase] [selector] [root] [attrOnly] [contentOnly] [attributes]',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace some foo true",
emptyParameters: [" [selector]", " [root]", " [attrOnly]",
" [contentOnly]", " [attributes]"],
status: "VALID"
helpers.setInput('pagemod replace some foo true');
helpers.check({
input: 'pagemod replace some foo true',
hints: ' [selector] [root] [attrOnly] [contentOnly] [attributes]',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace some foo true --attrOnly",
emptyParameters: [" [selector]", " [root]", " [contentOnly]",
" [attributes]"],
status: "VALID"
helpers.setInput('pagemod replace some foo true --attrOnly');
helpers.check({
input: 'pagemod replace some foo true --attrOnly',
hints: ' [selector] [root] [contentOnly] [attributes]',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec({
@ -146,21 +152,28 @@ function test() {
}
function testPageModRemoveElement() {
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove",
status: "ERROR"
helpers.setInput('pagemod remove');
helpers.check({
input: 'pagemod remove',
hints: '',
markup: 'IIIIIIIVIIIIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove element",
emptyParameters: [" <search>", " [root]", " [stripOnly]", " [ifEmptyOnly]"],
status: "ERROR"
helpers.setInput('pagemod remove element');
helpers.check({
input: 'pagemod remove element',
hints: ' <search> [root] [stripOnly] [ifEmptyOnly]',
markup: 'VVVVVVVVVVVVVVVVVVVVVV',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove element foo",
emptyParameters: [" [root]", " [stripOnly]", " [ifEmptyOnly]"],
status: "VALID"
helpers.setInput('pagemod remove element foo');
helpers.check({
input: 'pagemod remove element foo',
hints: ' [root] [stripOnly] [ifEmptyOnly]',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec({
@ -215,16 +228,32 @@ function test() {
}
function testPageModRemoveAttribute() {
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove attribute",
emptyParameters: [" <searchAttributes>", " <searchElements>", " [root]", " [ignoreCase]"],
status: "ERROR"
helpers.setInput('pagemod remove attribute ');
helpers.check({
input: 'pagemod remove attribute ',
hints: '<searchAttributes> <searchElements> [root] [ignoreCase]',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'ERROR',
args: {
searchAttributes: { value: undefined, status: 'INCOMPLETE' },
searchElements: { value: undefined, status: 'INCOMPLETE' },
root: { value: undefined },
ignoreCase: { value: false },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove attribute foo bar",
emptyParameters: [" [root]", " [ignoreCase]"],
status: "VALID"
helpers.setInput('pagemod remove attribute foo bar');
helpers.check({
input: 'pagemod remove attribute foo bar',
hints: ' [root] [ignoreCase]',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
searchAttributes: { value: 'foo' },
searchElements: { value: 'bar' },
root: { value: undefined },
ignoreCase: { value: false },
}
});
DeveloperToolbarTest.exec({

View File

@ -69,92 +69,103 @@ function shutdown() {
}
function testPrefStatus() {
DeveloperToolbarTest.checkInputStatus({
typed: "pref s",
markup: "IIIIVI",
status: "ERROR",
directTabText: "et"
helpers.setInput('pref');
helpers.check({
input: 'pref',
hints: '',
markup: 'IIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref show",
markup: "VVVVVVVVV",
status: "ERROR",
emptyParameters: [ " <setting>" ]
helpers.setInput('pref s');
helpers.check({
input: 'pref s',
hints: 'et',
markup: 'IIIIVI',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref show tempTBo",
markup: "VVVVVVVVVVEEEEEEE",
status: "ERROR",
emptyParameters: [ ]
helpers.setInput('pref sh');
helpers.check({
input: 'pref sh',
hints: 'ow',
markup: 'IIIIVII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref show devtools.toolbar.ena",
markup: "VVVVVVVVVVIIIIIIIIIIIIIIIIIIII",
directTabText: "bled",
status: "ERROR",
emptyParameters: [ ]
helpers.setInput('pref show ');
helpers.check({
input: 'pref show ',
markup: 'VVVVVVVVVV',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref show hideIntro",
markup: "VVVVVVVVVVIIIIIIIII",
directTabText: "",
arrowTabText: "devtools.gcli.hideIntro",
status: "ERROR",
emptyParameters: [ ]
helpers.setInput('pref show usetexttospeech');
helpers.check({
input: 'pref show usetexttospeech',
hints: ' -> accessibility.usetexttospeech',
markup: 'VVVVVVVVVVIIIIIIIIIIIIIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref show devtools.toolbar.enabled",
markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
status: "VALID",
emptyParameters: [ ]
helpers.setInput('pref show devtools.til');
helpers.check({
input: 'pref show devtools.til',
hints: 't.enabled',
markup: 'VVVVVVVVVVIIIIIIIIIIII',
status: 'ERROR',
tooltipState: 'true:importantFieldFlag',
args: {
setting: { value: undefined, status: 'INCOMPLETE' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref show devtools.tilt.enabled 4",
markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVE",
directTabText: "",
status: "ERROR",
emptyParameters: [ ]
helpers.setInput('pref reset devtools.tilt.enabled');
helpers.check({
input: 'pref reset devtools.tilt.enabled',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref show devtools.tilt.enabled",
markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
status: "VALID",
emptyParameters: [ ]
helpers.setInput('pref show devtools.tilt.enabled 4');
helpers.check({
input: 'pref show devtools.tilt.enabled 4',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVE',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref reset devtools.tilt.enabled",
markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
status: "VALID",
emptyParameters: [ ]
helpers.setInput('pref set devtools.tilt.enabled 4');
helpers.check({
input: 'pref set devtools.tilt.enabled 4',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVE',
status: 'ERROR',
args: {
setting: { arg: ' devtools.tilt.enabled' },
value: { status: 'ERROR', message: 'Can\'t use \'4\'.' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref set devtools.tilt.enabled 4",
markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVE",
status: "ERROR",
emptyParameters: [ ]
helpers.setInput('pref set devtools.editor.tabsize 4');
helpers.check({
input: 'pref set devtools.editor.tabsize 4',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
setting: { arg: ' devtools.editor.tabsize' },
value: { value: 4 },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref set devtools.editor.tabsize 4",
markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
status: "VALID",
emptyParameters: [ ]
});
DeveloperToolbarTest.checkInputStatus({
typed: "pref list",
markup: "EEEEVEEEE",
status: "ERROR",
emptyParameters: [ ]
helpers.setInput('pref list');
helpers.check({
input: 'pref list',
hints: '',
markup: 'EEEEVEEEE',
status: 'ERROR'
});
}

View File

@ -10,36 +10,23 @@ function test() {
}
function testRestart() {
DeveloperToolbarTest.checkInputStatus({
typed: "restart",
markup: "VVVVVVV",
status: "VALID",
emptyParameters: [ " [nocache]" ],
helpers.setInput('restart');
helpers.check({
input: 'restart',
markup: 'VVVVVVV',
status: 'VALID',
args: {
nocache: { value: false },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart ",
markup: "VVVVVVVV",
status: "VALID",
directTabText: "false"
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart t",
markup: "VVVVVVVVI",
status: "ERROR",
directTabText: "rue"
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart --nocache",
markup: "VVVVVVVVVVVVVVVVV",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart --noca",
markup: "VVVVVVVVEEEEEE",
status: "ERROR",
helpers.setInput('restart --nocache');
helpers.check({
input: 'restart --nocache',
markup: 'VVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
nocache: { value: true },
}
});
}

View File

@ -11,35 +11,28 @@ function test() {
}
function testBreakCommands() {
info('###################################################');
info('###################################################');
info('###################################################');
info('###################################################');
info('###################################################');
info('###################################################');
info(content.document.documentElement.innerHTML + '\n');
DeveloperToolbarTest.checkInputStatus({
typed: "brea",
directTabText: "k",
status: "ERROR"
helpers.setInput('break');
helpers.check({
input: 'break',
hints: '',
markup: 'IIIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "break",
status: "ERROR"
helpers.setInput('break add');
helpers.check({
input: 'break add',
hints: '',
markup: 'IIIIIVIII',
status: 'ERROR'
});
DeveloperToolbarTest.checkInputStatus({
typed: "break add",
status: "ERROR"
});
DeveloperToolbarTest.checkInputStatus({
typed: "break add line",
emptyParameters: [ " <file>", " <line>" ],
status: "ERROR"
helpers.setInput('break add line');
helpers.check({
input: 'break add line',
hints: ' <file> <line>',
markup: 'VVVVVVVVVVVVVV',
status: 'ERROR'
});
let pane = DebuggerUI.toggleDebugger();
@ -53,10 +46,16 @@ function testBreakCommands() {
var resumed = DeveloperToolbarTest.checkCalled(function() {
var framesAdded = DeveloperToolbarTest.checkCalled(function() {
DeveloperToolbarTest.checkInputStatus({
typed: "break add line " + TEST_URI + " " + content.wrappedJSObject.line0,
status: "VALID"
helpers.setInput('break add line ' + TEST_URI + ' ' + content.wrappedJSObject.line0);
helpers.check({
hints: '',
status: 'VALID',
args: {
file: { value: TEST_URI },
line: { value: content.wrappedJSObject.line0 },
}
});
DeveloperToolbarTest.exec({
args: {
type: 'line',
@ -66,17 +65,39 @@ function testBreakCommands() {
completed: false
});
DeveloperToolbarTest.checkInputStatus({
typed: "break list",
status: "VALID"
helpers.setInput('break list');
helpers.check({
input: 'break list',
hints: '',
markup: 'VVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec();
var cleanup = DeveloperToolbarTest.checkCalled(function() {
DeveloperToolbarTest.checkInputStatus({
typed: "break del 0",
status: "VALID"
helpers.setInput('break del 9');
helpers.check({
input: 'break del 9',
hints: '',
markup: 'VVVVVVVVVVE',
status: 'ERROR',
args: {
breakid: { status: 'ERROR', message: '9 is greater than maximum allowed: 0.' },
}
});
helpers.setInput('break del 0');
helpers.check({
input: 'break del 0',
hints: '',
markup: 'VVVVVVVVVVV',
status: 'VALID',
args: {
breakid: { value: 0 },
}
});
DeveloperToolbarTest.exec({
args: { breakid: 0 },
completed: false

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ let console = (function() {
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
/**
* Open a new tab at a URL and call a callback on load

View File

@ -1,459 +0,0 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};

View File

@ -0,0 +1,881 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/*
* Use as a JSM
* ------------
* helpers._createDebugCheck() and maybe other functions in this file can be
* useful at runtime, so it is possible to use helpers.js as a JSM.
* Copy commandline/test/helpers.js to shared/helpers.jsm, and then add to
* DeveloperToolbar.jsm the following:
*
* XPCOMUtils.defineLazyModuleGetter(this, "helpers",
* "resource:///modules/devtools/helpers.jsm");
*
* At the bottom of DeveloperToolbar.prototype._onload add this:
*
* var options = { display: this.display };
* this._input.onkeypress = function(ev) {
* helpers.setup(options);
* dump(helpers._createDebugCheck() + '\n\n');
* };
*
* Now GCLI will emit output on every keypress that both explains the state
* of GCLI and can be run as a test case.
*/
var EXPORTED_SYMBOLS = [ 'helpers' ];
var test = { };
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
else {
info('Expected: [ \"' + actualParams.join('", "') + '" ]');
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (helpers) {
helpers.setup({ display: DeveloperToolbar.display });
}
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
info('_checkFinish. ' + DeveloperToolbarTest._outstanding.length + ' outstanding');
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
info('Finish');
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};
///////////////////////////////////////////////////////////////////////////////
var helpers = {};
helpers._display = undefined;
helpers.setup = function(options) {
helpers._display = options.display;
if (typeof ok !== 'undefined') {
test.ok = ok;
test.is = is;
test.log = info;
}
};
helpers.shutdown = function(options) {
helpers._display = undefined;
};
/**
* Various functions to return the actual state of the command line
*/
helpers._actual = {
input: function() {
return helpers._display.inputter.element.value;
},
hints: function() {
var templateData = helpers._display.completer._getCompleterTemplateData();
var actualHints = templateData.directTabText +
templateData.emptyParameters.join('') +
templateData.arrowTabText;
return actualHints.replace(/\u00a0/g, ' ')
.replace(/\u21E5/, '->')
.replace(/ $/, '');
},
markup: function() {
var cursor = helpers._display.inputter.element.selectionStart;
var statusMarkup = helpers._display.requisition.getInputStatusMarkup(cursor);
return statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
},
cursor: function() {
return helpers._display.inputter.element.selectionStart;
},
current: function() {
return helpers._display.requisition.getAssignmentAt(helpers._actual.cursor()).param.name;
},
status: function() {
return helpers._display.requisition.getStatus().toString();
},
outputState: function() {
var outputData = helpers._display.focusManager._shouldShowOutput();
return outputData.visible + ':' + outputData.reason;
},
tooltipState: function() {
var tooltipData = helpers._display.focusManager._shouldShowTooltip();
return tooltipData.visible + ':' + tooltipData.reason;
}
};
helpers._directToString = [ 'boolean', 'undefined', 'number' ];
helpers._createDebugCheck = function() {
var requisition = helpers._display.requisition;
var command = requisition.commandAssignment.value;
var input = helpers._actual.input();
var padding = Array(input.length + 1).join(' ');
var output = '';
output += 'helpers.setInput(\'' + input + '\');\n';
output += 'helpers.check({\n';
output += ' input: \'' + input + '\',\n';
output += ' hints: ' + padding + '\'' + helpers._actual.hints() + '\',\n';
output += ' markup: \'' + helpers._actual.markup() + '\',\n';
output += ' cursor: ' + helpers._actual.cursor() + ',\n';
output += ' current: \'' + helpers._actual.current() + '\',\n';
output += ' status: \'' + helpers._actual.status() + '\',\n';
output += ' outputState: \'' + helpers._actual.outputState() + '\',\n';
if (command) {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\',\n';
output += ' args: {\n';
output += ' command: { name: \'' + command.name + '\' },\n';
requisition.getAssignments().forEach(function(assignment) {
output += ' ' + assignment.param.name + ': { ';
if (typeof assignment.value === 'string') {
output += 'value: \'' + assignment.value + '\', ';
}
else if (helpers._directToString.indexOf(typeof assignment.value) !== -1) {
output += 'value: ' + assignment.value + ', ';
}
else if (assignment.value === null) {
output += 'value: ' + assignment.value + ', ';
}
else {
output += '/*value:' + assignment.value + ',*/ ';
}
output += 'arg: \'' + assignment.arg + '\', ';
output += 'status: \'' + assignment.getStatus().toString() + '\', ';
output += 'message: \'' + assignment.getMessage() + '\'';
output += ' },\n';
});
output += ' }\n';
}
else {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\'\n';
}
output += '});';
return output;
};
/**
* We're splitting status into setup() which alters the state of the system
* and check() which ensures that things are in the right place afterwards.
*/
helpers.setInput = function(typed, cursor) {
helpers._display.inputter.setInput(typed);
if (cursor) {
helpers._display.inputter.setCursor({ start: cursor, end: cursor });
}
helpers._display.focusManager.onInputChange();
test.log('setInput("' + typed + '"' + (cursor == null ? '' : ', ' + cursor) + ')');
};
/**
* Simulate focusing the input field
*/
helpers.focusInput = function() {
helpers._display.inputter.focus();
};
/**
* Simulate pressing TAB in the input field
*/
helpers.pressTab = function() {
helpers.pressKey(9 /*KeyEvent.DOM_VK_TAB*/);
};
/**
* Simulate pressing RETURN in the input field
*/
helpers.pressReturn = function() {
helpers.pressKey(13 /*KeyEvent.DOM_VK_RETURN*/);
};
/**
* Simulate pressing a key by keyCode in the input field
*/
helpers.pressKey = function(keyCode) {
var fakeEvent = {
keyCode: keyCode,
preventDefault: function() { },
timeStamp: new Date().getTime()
};
helpers._display.inputter.onKeyDown(fakeEvent);
helpers._display.inputter.onKeyUp(fakeEvent);
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* hints: The hint text, i.e. a concatenation of the directTabText, the
* emptyParameters and the arrowTabText. The text as inserted into the UI
* will include NBSP and Unicode RARR characters, these should be
* represented using normal space and '->' for the arrow
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
helpers.check = function(checks) {
if ('input' in checks) {
test.is(helpers._actual.input(), checks.input, 'input');
}
if ('cursor' in checks) {
test.is(helpers._actual.cursor(), checks.cursor, 'cursor');
}
if ('current' in checks) {
test.is(helpers._actual.current(), checks.current, 'current');
}
if ('status' in checks) {
test.is(helpers._actual.status(), checks.status, 'status');
}
if ('markup' in checks) {
test.is(helpers._actual.markup(), checks.markup, 'markup');
}
if ('hints' in checks) {
test.is(helpers._actual.hints(), checks.hints, 'hints');
}
if ('tooltipState' in checks) {
test.is(helpers._actual.tooltipState(), checks.tooltipState, 'tooltipState');
}
if ('outputState' in checks) {
test.is(helpers._actual.outputState(), checks.outputState, 'outputState');
}
if (checks.args != null) {
var requisition = helpers._display.requisition;
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
test.ok(false, 'Unknown arg: ' + paramName);
return;
}
if ('value' in check) {
test.is(assignment.value,
check.value,
'arg.' + paramName + '.value');
}
if ('name' in check) {
test.is(assignment.value.name,
check.name,
'arg.' + paramName + '.name');
}
if ('type' in check) {
test.is(assignment.arg.type,
check.type,
'arg.' + paramName + '.type');
}
if ('arg' in check) {
test.is(assignment.arg.toString(),
check.arg,
'arg.' + paramName + '.arg');
}
if ('status' in check) {
test.is(assignment.getStatus().toString(),
check.status,
'arg.' + paramName + '.status');
}
if ('message' in check) {
test.is(assignment.getMessage(),
check.message,
'arg.' + paramName + '.message');
}
});
}
};
/**
* Execute a command:
*
* helpers.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // Regex to test against textContent of output
* blankOutput: true, // Special checks when there is no output
* });
*/
helpers.exec = function(tests) {
var requisition = helpers._display.requisition;
var inputter = helpers._display.inputter;
tests = tests || {};
if (tests.typed) {
inputter.setInput(tests.typed);
}
var typed = inputter.getInputState().typed;
var output = requisition.exec({ hidden: true });
test.is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
test.ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// test.ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
test.is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
var expectedArg = tests.args[arg];
var actualArg = output.args[arg];
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
test.ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
test.is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (var i = 0; i < expectedArg.length; i++) {
test.is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
test.is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
});
}
if (!options.window.document.createElement) {
test.log('skipping output tests (missing doc.createElement) for ' + typed);
return;
}
var div = options.window.document.createElement('div');
output.toDom(div);
var displayed = div.textContent.trim();
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
test.ok(false, "html output for " + typed + " against " + match.source);
console.log("Actual textContent");
console.log(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
test.ok(false, "html for " + typed + " (textContent sent to info)");
console.log("Actual textContent");
console.log(displayed);
}
}
};

View File

@ -40,7 +40,7 @@ _BROWSER_FILES = \
browser_inspector_cmd_inspect.js \
browser_inspector_cmd_inspect.html \
head.js \
helper.js \
helpers.js \
$(NULL)
libs:: $(_BROWSER_FILES)

View File

@ -11,55 +11,110 @@ function test() {
}
function testInspect() {
DeveloperToolbarTest.checkInputStatus({
typed: "inspec",
directTabText: "t",
status: "ERROR"
helpers.setInput('inspect');
helpers.check({
input: 'inspect',
hints: ' <node>',
markup: 'VVVVVVV',
status: 'ERROR',
args: {
node: { message: '' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect",
emptyParameters: [ " <node>" ],
status: "ERROR"
helpers.setInput('inspect h1');
helpers.check({
input: 'inspect h1',
hints: '',
markup: 'VVVVVVVVII',
status: 'ERROR',
args: {
node: { message: 'No matches' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect h1",
status: "ERROR"
helpers.setInput('inspect span');
helpers.check({
input: 'inspect span',
hints: '',
markup: 'VVVVVVVVEEEE',
status: 'ERROR',
args: {
node: { message: 'Too many matches (2)' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect span",
status: "ERROR"
helpers.setInput('inspect div');
helpers.check({
input: 'inspect div',
hints: '',
markup: 'VVVVVVVVVVV',
status: 'VALID',
args: {
node: { message: '' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect div",
status: "VALID"
helpers.setInput('inspect .someclas');
helpers.check({
input: 'inspect .someclas',
hints: '',
markup: 'VVVVVVVVIIIIIIIII',
status: 'ERROR',
args: {
node: { message: 'No matches' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect .someclass",
status: "VALID"
helpers.setInput('inspect .someclass');
helpers.check({
input: 'inspect .someclass',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
node: { message: '' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect #someid",
status: "VALID"
helpers.setInput('inspect #someid');
helpers.check({
input: 'inspect #someid',
hints: '',
markup: 'VVVVVVVVVVVVVVV',
status: 'VALID',
args: {
node: { message: '' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect button[disabled]",
status: "VALID"
helpers.setInput('inspect button[disabled]');
helpers.check({
input: 'inspect button[disabled]',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
node: { message: '' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect p>strong",
status: "VALID"
helpers.setInput('inspect p>strong');
helpers.check({
input: 'inspect p>strong',
hints: '',
markup: 'VVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
node: { message: '' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "inspect :root",
status: "VALID"
helpers.setInput('inspect :root');
helpers.check({
input: 'inspect :root',
hints: '',
markup: 'VVVVVVVVVVVVV',
status: 'VALID'
});
}

View File

@ -9,7 +9,7 @@ let LayoutHelpers = tempScope.LayoutHelpers;
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
// Clear preferences that may be set during the course of tests.
function clearUserPrefs()

View File

@ -1,459 +0,0 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};

View File

@ -0,0 +1,881 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/*
* Use as a JSM
* ------------
* helpers._createDebugCheck() and maybe other functions in this file can be
* useful at runtime, so it is possible to use helpers.js as a JSM.
* Copy commandline/test/helpers.js to shared/helpers.jsm, and then add to
* DeveloperToolbar.jsm the following:
*
* XPCOMUtils.defineLazyModuleGetter(this, "helpers",
* "resource:///modules/devtools/helpers.jsm");
*
* At the bottom of DeveloperToolbar.prototype._onload add this:
*
* var options = { display: this.display };
* this._input.onkeypress = function(ev) {
* helpers.setup(options);
* dump(helpers._createDebugCheck() + '\n\n');
* };
*
* Now GCLI will emit output on every keypress that both explains the state
* of GCLI and can be run as a test case.
*/
var EXPORTED_SYMBOLS = [ 'helpers' ];
var test = { };
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
else {
info('Expected: [ \"' + actualParams.join('", "') + '" ]');
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (helpers) {
helpers.setup({ display: DeveloperToolbar.display });
}
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
info('_checkFinish. ' + DeveloperToolbarTest._outstanding.length + ' outstanding');
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
info('Finish');
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};
///////////////////////////////////////////////////////////////////////////////
var helpers = {};
helpers._display = undefined;
helpers.setup = function(options) {
helpers._display = options.display;
if (typeof ok !== 'undefined') {
test.ok = ok;
test.is = is;
test.log = info;
}
};
helpers.shutdown = function(options) {
helpers._display = undefined;
};
/**
* Various functions to return the actual state of the command line
*/
helpers._actual = {
input: function() {
return helpers._display.inputter.element.value;
},
hints: function() {
var templateData = helpers._display.completer._getCompleterTemplateData();
var actualHints = templateData.directTabText +
templateData.emptyParameters.join('') +
templateData.arrowTabText;
return actualHints.replace(/\u00a0/g, ' ')
.replace(/\u21E5/, '->')
.replace(/ $/, '');
},
markup: function() {
var cursor = helpers._display.inputter.element.selectionStart;
var statusMarkup = helpers._display.requisition.getInputStatusMarkup(cursor);
return statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
},
cursor: function() {
return helpers._display.inputter.element.selectionStart;
},
current: function() {
return helpers._display.requisition.getAssignmentAt(helpers._actual.cursor()).param.name;
},
status: function() {
return helpers._display.requisition.getStatus().toString();
},
outputState: function() {
var outputData = helpers._display.focusManager._shouldShowOutput();
return outputData.visible + ':' + outputData.reason;
},
tooltipState: function() {
var tooltipData = helpers._display.focusManager._shouldShowTooltip();
return tooltipData.visible + ':' + tooltipData.reason;
}
};
helpers._directToString = [ 'boolean', 'undefined', 'number' ];
helpers._createDebugCheck = function() {
var requisition = helpers._display.requisition;
var command = requisition.commandAssignment.value;
var input = helpers._actual.input();
var padding = Array(input.length + 1).join(' ');
var output = '';
output += 'helpers.setInput(\'' + input + '\');\n';
output += 'helpers.check({\n';
output += ' input: \'' + input + '\',\n';
output += ' hints: ' + padding + '\'' + helpers._actual.hints() + '\',\n';
output += ' markup: \'' + helpers._actual.markup() + '\',\n';
output += ' cursor: ' + helpers._actual.cursor() + ',\n';
output += ' current: \'' + helpers._actual.current() + '\',\n';
output += ' status: \'' + helpers._actual.status() + '\',\n';
output += ' outputState: \'' + helpers._actual.outputState() + '\',\n';
if (command) {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\',\n';
output += ' args: {\n';
output += ' command: { name: \'' + command.name + '\' },\n';
requisition.getAssignments().forEach(function(assignment) {
output += ' ' + assignment.param.name + ': { ';
if (typeof assignment.value === 'string') {
output += 'value: \'' + assignment.value + '\', ';
}
else if (helpers._directToString.indexOf(typeof assignment.value) !== -1) {
output += 'value: ' + assignment.value + ', ';
}
else if (assignment.value === null) {
output += 'value: ' + assignment.value + ', ';
}
else {
output += '/*value:' + assignment.value + ',*/ ';
}
output += 'arg: \'' + assignment.arg + '\', ';
output += 'status: \'' + assignment.getStatus().toString() + '\', ';
output += 'message: \'' + assignment.getMessage() + '\'';
output += ' },\n';
});
output += ' }\n';
}
else {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\'\n';
}
output += '});';
return output;
};
/**
* We're splitting status into setup() which alters the state of the system
* and check() which ensures that things are in the right place afterwards.
*/
helpers.setInput = function(typed, cursor) {
helpers._display.inputter.setInput(typed);
if (cursor) {
helpers._display.inputter.setCursor({ start: cursor, end: cursor });
}
helpers._display.focusManager.onInputChange();
test.log('setInput("' + typed + '"' + (cursor == null ? '' : ', ' + cursor) + ')');
};
/**
* Simulate focusing the input field
*/
helpers.focusInput = function() {
helpers._display.inputter.focus();
};
/**
* Simulate pressing TAB in the input field
*/
helpers.pressTab = function() {
helpers.pressKey(9 /*KeyEvent.DOM_VK_TAB*/);
};
/**
* Simulate pressing RETURN in the input field
*/
helpers.pressReturn = function() {
helpers.pressKey(13 /*KeyEvent.DOM_VK_RETURN*/);
};
/**
* Simulate pressing a key by keyCode in the input field
*/
helpers.pressKey = function(keyCode) {
var fakeEvent = {
keyCode: keyCode,
preventDefault: function() { },
timeStamp: new Date().getTime()
};
helpers._display.inputter.onKeyDown(fakeEvent);
helpers._display.inputter.onKeyUp(fakeEvent);
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* hints: The hint text, i.e. a concatenation of the directTabText, the
* emptyParameters and the arrowTabText. The text as inserted into the UI
* will include NBSP and Unicode RARR characters, these should be
* represented using normal space and '->' for the arrow
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
helpers.check = function(checks) {
if ('input' in checks) {
test.is(helpers._actual.input(), checks.input, 'input');
}
if ('cursor' in checks) {
test.is(helpers._actual.cursor(), checks.cursor, 'cursor');
}
if ('current' in checks) {
test.is(helpers._actual.current(), checks.current, 'current');
}
if ('status' in checks) {
test.is(helpers._actual.status(), checks.status, 'status');
}
if ('markup' in checks) {
test.is(helpers._actual.markup(), checks.markup, 'markup');
}
if ('hints' in checks) {
test.is(helpers._actual.hints(), checks.hints, 'hints');
}
if ('tooltipState' in checks) {
test.is(helpers._actual.tooltipState(), checks.tooltipState, 'tooltipState');
}
if ('outputState' in checks) {
test.is(helpers._actual.outputState(), checks.outputState, 'outputState');
}
if (checks.args != null) {
var requisition = helpers._display.requisition;
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
test.ok(false, 'Unknown arg: ' + paramName);
return;
}
if ('value' in check) {
test.is(assignment.value,
check.value,
'arg.' + paramName + '.value');
}
if ('name' in check) {
test.is(assignment.value.name,
check.name,
'arg.' + paramName + '.name');
}
if ('type' in check) {
test.is(assignment.arg.type,
check.type,
'arg.' + paramName + '.type');
}
if ('arg' in check) {
test.is(assignment.arg.toString(),
check.arg,
'arg.' + paramName + '.arg');
}
if ('status' in check) {
test.is(assignment.getStatus().toString(),
check.status,
'arg.' + paramName + '.status');
}
if ('message' in check) {
test.is(assignment.getMessage(),
check.message,
'arg.' + paramName + '.message');
}
});
}
};
/**
* Execute a command:
*
* helpers.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // Regex to test against textContent of output
* blankOutput: true, // Special checks when there is no output
* });
*/
helpers.exec = function(tests) {
var requisition = helpers._display.requisition;
var inputter = helpers._display.inputter;
tests = tests || {};
if (tests.typed) {
inputter.setInput(tests.typed);
}
var typed = inputter.getInputState().typed;
var output = requisition.exec({ hidden: true });
test.is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
test.ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// test.ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
test.is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
var expectedArg = tests.args[arg];
var actualArg = output.args[arg];
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
test.ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
test.is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (var i = 0; i < expectedArg.length; i++) {
test.is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
test.is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
});
}
if (!options.window.document.createElement) {
test.log('skipping output tests (missing doc.createElement) for ' + typed);
return;
}
var div = options.window.document.createElement('div');
output.toDom(div);
var displayed = div.textContent.trim();
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
test.ok(false, "html output for " + typed + " against " + match.source);
console.log("Actual textContent");
console.log(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
test.ok(false, "html for " + typed + " (textContent sent to info)");
console.log("Actual textContent");
console.log(displayed);
}
}
};

View File

@ -50,7 +50,7 @@ _BROWSER_FILES = \
browser_responsive_cmd.js \
browser_responsivecomputedview.js \
head.js \
helper.js \
helpers.js \
$(NULL)

View File

@ -14,47 +14,73 @@ function isClosed() {
}
function GAT_test() {
DeveloperToolbarTest.checkInputStatus({
typed: "resize toggle",
status: "VALID"
helpers.setInput('resize toggle');
helpers.check({
input: 'resize toggle',
hints: '',
markup: 'VVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec();
ok(isOpen(), "responsive mode is open");
DeveloperToolbarTest.checkInputStatus({
typed: "resize toggle",
status: "VALID"
helpers.setInput('resize toggle');
helpers.check({
input: 'resize toggle',
hints: '',
markup: 'VVVVVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec();
ok(isClosed(), "responsive mode is closed");
DeveloperToolbarTest.checkInputStatus({
typed: "resize on",
status: "VALID"
helpers.setInput('resize on');
helpers.check({
input: 'resize on',
hints: '',
markup: 'VVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec();
ok(isOpen(), "responsive mode is open");
DeveloperToolbarTest.checkInputStatus({
typed: "resize off",
status: "VALID"
helpers.setInput('resize off');
helpers.check({
input: 'resize off',
hints: '',
markup: 'VVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec();
ok(isClosed(), "responsive mode is closed");
DeveloperToolbarTest.checkInputStatus({
typed: "resize to 400 400",
status: "VALID"
helpers.setInput('resize to 400 400');
helpers.check({
input: 'resize to 400 400',
hints: '',
markup: 'VVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
width: { value: 400 },
height: { value: 400 },
}
});
DeveloperToolbarTest.exec();
ok(isOpen(), "responsive mode is open");
DeveloperToolbarTest.checkInputStatus({
typed: "resize off",
status: "VALID"
helpers.setInput('resize off');
helpers.check({
input: 'resize off',
hints: '',
markup: 'VVVVVVVVVV',
status: 'VALID'
});
DeveloperToolbarTest.exec();
ok(isClosed(), "responsive mode is closed");
// executeSoon(finish);
}

View File

@ -5,4 +5,4 @@
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);

View File

@ -1,459 +0,0 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};

View File

@ -0,0 +1,881 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/*
* Use as a JSM
* ------------
* helpers._createDebugCheck() and maybe other functions in this file can be
* useful at runtime, so it is possible to use helpers.js as a JSM.
* Copy commandline/test/helpers.js to shared/helpers.jsm, and then add to
* DeveloperToolbar.jsm the following:
*
* XPCOMUtils.defineLazyModuleGetter(this, "helpers",
* "resource:///modules/devtools/helpers.jsm");
*
* At the bottom of DeveloperToolbar.prototype._onload add this:
*
* var options = { display: this.display };
* this._input.onkeypress = function(ev) {
* helpers.setup(options);
* dump(helpers._createDebugCheck() + '\n\n');
* };
*
* Now GCLI will emit output on every keypress that both explains the state
* of GCLI and can be run as a test case.
*/
var EXPORTED_SYMBOLS = [ 'helpers' ];
var test = { };
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
else {
info('Expected: [ \"' + actualParams.join('", "') + '" ]');
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (helpers) {
helpers.setup({ display: DeveloperToolbar.display });
}
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
info('_checkFinish. ' + DeveloperToolbarTest._outstanding.length + ' outstanding');
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
info('Finish');
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};
///////////////////////////////////////////////////////////////////////////////
var helpers = {};
helpers._display = undefined;
helpers.setup = function(options) {
helpers._display = options.display;
if (typeof ok !== 'undefined') {
test.ok = ok;
test.is = is;
test.log = info;
}
};
helpers.shutdown = function(options) {
helpers._display = undefined;
};
/**
* Various functions to return the actual state of the command line
*/
helpers._actual = {
input: function() {
return helpers._display.inputter.element.value;
},
hints: function() {
var templateData = helpers._display.completer._getCompleterTemplateData();
var actualHints = templateData.directTabText +
templateData.emptyParameters.join('') +
templateData.arrowTabText;
return actualHints.replace(/\u00a0/g, ' ')
.replace(/\u21E5/, '->')
.replace(/ $/, '');
},
markup: function() {
var cursor = helpers._display.inputter.element.selectionStart;
var statusMarkup = helpers._display.requisition.getInputStatusMarkup(cursor);
return statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
},
cursor: function() {
return helpers._display.inputter.element.selectionStart;
},
current: function() {
return helpers._display.requisition.getAssignmentAt(helpers._actual.cursor()).param.name;
},
status: function() {
return helpers._display.requisition.getStatus().toString();
},
outputState: function() {
var outputData = helpers._display.focusManager._shouldShowOutput();
return outputData.visible + ':' + outputData.reason;
},
tooltipState: function() {
var tooltipData = helpers._display.focusManager._shouldShowTooltip();
return tooltipData.visible + ':' + tooltipData.reason;
}
};
helpers._directToString = [ 'boolean', 'undefined', 'number' ];
helpers._createDebugCheck = function() {
var requisition = helpers._display.requisition;
var command = requisition.commandAssignment.value;
var input = helpers._actual.input();
var padding = Array(input.length + 1).join(' ');
var output = '';
output += 'helpers.setInput(\'' + input + '\');\n';
output += 'helpers.check({\n';
output += ' input: \'' + input + '\',\n';
output += ' hints: ' + padding + '\'' + helpers._actual.hints() + '\',\n';
output += ' markup: \'' + helpers._actual.markup() + '\',\n';
output += ' cursor: ' + helpers._actual.cursor() + ',\n';
output += ' current: \'' + helpers._actual.current() + '\',\n';
output += ' status: \'' + helpers._actual.status() + '\',\n';
output += ' outputState: \'' + helpers._actual.outputState() + '\',\n';
if (command) {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\',\n';
output += ' args: {\n';
output += ' command: { name: \'' + command.name + '\' },\n';
requisition.getAssignments().forEach(function(assignment) {
output += ' ' + assignment.param.name + ': { ';
if (typeof assignment.value === 'string') {
output += 'value: \'' + assignment.value + '\', ';
}
else if (helpers._directToString.indexOf(typeof assignment.value) !== -1) {
output += 'value: ' + assignment.value + ', ';
}
else if (assignment.value === null) {
output += 'value: ' + assignment.value + ', ';
}
else {
output += '/*value:' + assignment.value + ',*/ ';
}
output += 'arg: \'' + assignment.arg + '\', ';
output += 'status: \'' + assignment.getStatus().toString() + '\', ';
output += 'message: \'' + assignment.getMessage() + '\'';
output += ' },\n';
});
output += ' }\n';
}
else {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\'\n';
}
output += '});';
return output;
};
/**
* We're splitting status into setup() which alters the state of the system
* and check() which ensures that things are in the right place afterwards.
*/
helpers.setInput = function(typed, cursor) {
helpers._display.inputter.setInput(typed);
if (cursor) {
helpers._display.inputter.setCursor({ start: cursor, end: cursor });
}
helpers._display.focusManager.onInputChange();
test.log('setInput("' + typed + '"' + (cursor == null ? '' : ', ' + cursor) + ')');
};
/**
* Simulate focusing the input field
*/
helpers.focusInput = function() {
helpers._display.inputter.focus();
};
/**
* Simulate pressing TAB in the input field
*/
helpers.pressTab = function() {
helpers.pressKey(9 /*KeyEvent.DOM_VK_TAB*/);
};
/**
* Simulate pressing RETURN in the input field
*/
helpers.pressReturn = function() {
helpers.pressKey(13 /*KeyEvent.DOM_VK_RETURN*/);
};
/**
* Simulate pressing a key by keyCode in the input field
*/
helpers.pressKey = function(keyCode) {
var fakeEvent = {
keyCode: keyCode,
preventDefault: function() { },
timeStamp: new Date().getTime()
};
helpers._display.inputter.onKeyDown(fakeEvent);
helpers._display.inputter.onKeyUp(fakeEvent);
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* hints: The hint text, i.e. a concatenation of the directTabText, the
* emptyParameters and the arrowTabText. The text as inserted into the UI
* will include NBSP and Unicode RARR characters, these should be
* represented using normal space and '->' for the arrow
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
helpers.check = function(checks) {
if ('input' in checks) {
test.is(helpers._actual.input(), checks.input, 'input');
}
if ('cursor' in checks) {
test.is(helpers._actual.cursor(), checks.cursor, 'cursor');
}
if ('current' in checks) {
test.is(helpers._actual.current(), checks.current, 'current');
}
if ('status' in checks) {
test.is(helpers._actual.status(), checks.status, 'status');
}
if ('markup' in checks) {
test.is(helpers._actual.markup(), checks.markup, 'markup');
}
if ('hints' in checks) {
test.is(helpers._actual.hints(), checks.hints, 'hints');
}
if ('tooltipState' in checks) {
test.is(helpers._actual.tooltipState(), checks.tooltipState, 'tooltipState');
}
if ('outputState' in checks) {
test.is(helpers._actual.outputState(), checks.outputState, 'outputState');
}
if (checks.args != null) {
var requisition = helpers._display.requisition;
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
test.ok(false, 'Unknown arg: ' + paramName);
return;
}
if ('value' in check) {
test.is(assignment.value,
check.value,
'arg.' + paramName + '.value');
}
if ('name' in check) {
test.is(assignment.value.name,
check.name,
'arg.' + paramName + '.name');
}
if ('type' in check) {
test.is(assignment.arg.type,
check.type,
'arg.' + paramName + '.type');
}
if ('arg' in check) {
test.is(assignment.arg.toString(),
check.arg,
'arg.' + paramName + '.arg');
}
if ('status' in check) {
test.is(assignment.getStatus().toString(),
check.status,
'arg.' + paramName + '.status');
}
if ('message' in check) {
test.is(assignment.getMessage(),
check.message,
'arg.' + paramName + '.message');
}
});
}
};
/**
* Execute a command:
*
* helpers.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // Regex to test against textContent of output
* blankOutput: true, // Special checks when there is no output
* });
*/
helpers.exec = function(tests) {
var requisition = helpers._display.requisition;
var inputter = helpers._display.inputter;
tests = tests || {};
if (tests.typed) {
inputter.setInput(tests.typed);
}
var typed = inputter.getInputState().typed;
var output = requisition.exec({ hidden: true });
test.is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
test.ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// test.ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
test.is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
var expectedArg = tests.args[arg];
var actualArg = output.args[arg];
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
test.ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
test.is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (var i = 0; i < expectedArg.length; i++) {
test.is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
test.is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
});
}
if (!options.window.document.createElement) {
test.log('skipping output tests (missing doc.createElement) for ' + typed);
return;
}
var div = options.window.document.createElement('div');
output.toDom(div);
var displayed = div.textContent.trim();
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
test.ok(false, "html output for " + typed + " against " + match.source);
console.log("Actual textContent");
console.log(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
test.ok(false, "html for " + typed + " (textContent sent to info)");
console.log("Actual textContent");
console.log(displayed);
}
}
};

View File

@ -21,7 +21,7 @@ MOCHITEST_BROWSER_FILES = \
browser_toolbar_webconsole_errors_count.js \
browser_layoutHelpers.js \
head.js \
helper.js \
helpers.js \
leakhunt.js \
$(NULL)

View File

@ -10,7 +10,7 @@ let console = (function() {
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
/**
* Open a new tab at a URL and call a callback on load

View File

@ -1,459 +0,0 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};

View File

@ -0,0 +1,881 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/*
* Use as a JSM
* ------------
* helpers._createDebugCheck() and maybe other functions in this file can be
* useful at runtime, so it is possible to use helpers.js as a JSM.
* Copy commandline/test/helpers.js to shared/helpers.jsm, and then add to
* DeveloperToolbar.jsm the following:
*
* XPCOMUtils.defineLazyModuleGetter(this, "helpers",
* "resource:///modules/devtools/helpers.jsm");
*
* At the bottom of DeveloperToolbar.prototype._onload add this:
*
* var options = { display: this.display };
* this._input.onkeypress = function(ev) {
* helpers.setup(options);
* dump(helpers._createDebugCheck() + '\n\n');
* };
*
* Now GCLI will emit output on every keypress that both explains the state
* of GCLI and can be run as a test case.
*/
var EXPORTED_SYMBOLS = [ 'helpers' ];
var test = { };
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
else {
info('Expected: [ \"' + actualParams.join('", "') + '" ]');
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (helpers) {
helpers.setup({ display: DeveloperToolbar.display });
}
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
info('_checkFinish. ' + DeveloperToolbarTest._outstanding.length + ' outstanding');
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
info('Finish');
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};
///////////////////////////////////////////////////////////////////////////////
var helpers = {};
helpers._display = undefined;
helpers.setup = function(options) {
helpers._display = options.display;
if (typeof ok !== 'undefined') {
test.ok = ok;
test.is = is;
test.log = info;
}
};
helpers.shutdown = function(options) {
helpers._display = undefined;
};
/**
* Various functions to return the actual state of the command line
*/
helpers._actual = {
input: function() {
return helpers._display.inputter.element.value;
},
hints: function() {
var templateData = helpers._display.completer._getCompleterTemplateData();
var actualHints = templateData.directTabText +
templateData.emptyParameters.join('') +
templateData.arrowTabText;
return actualHints.replace(/\u00a0/g, ' ')
.replace(/\u21E5/, '->')
.replace(/ $/, '');
},
markup: function() {
var cursor = helpers._display.inputter.element.selectionStart;
var statusMarkup = helpers._display.requisition.getInputStatusMarkup(cursor);
return statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
},
cursor: function() {
return helpers._display.inputter.element.selectionStart;
},
current: function() {
return helpers._display.requisition.getAssignmentAt(helpers._actual.cursor()).param.name;
},
status: function() {
return helpers._display.requisition.getStatus().toString();
},
outputState: function() {
var outputData = helpers._display.focusManager._shouldShowOutput();
return outputData.visible + ':' + outputData.reason;
},
tooltipState: function() {
var tooltipData = helpers._display.focusManager._shouldShowTooltip();
return tooltipData.visible + ':' + tooltipData.reason;
}
};
helpers._directToString = [ 'boolean', 'undefined', 'number' ];
helpers._createDebugCheck = function() {
var requisition = helpers._display.requisition;
var command = requisition.commandAssignment.value;
var input = helpers._actual.input();
var padding = Array(input.length + 1).join(' ');
var output = '';
output += 'helpers.setInput(\'' + input + '\');\n';
output += 'helpers.check({\n';
output += ' input: \'' + input + '\',\n';
output += ' hints: ' + padding + '\'' + helpers._actual.hints() + '\',\n';
output += ' markup: \'' + helpers._actual.markup() + '\',\n';
output += ' cursor: ' + helpers._actual.cursor() + ',\n';
output += ' current: \'' + helpers._actual.current() + '\',\n';
output += ' status: \'' + helpers._actual.status() + '\',\n';
output += ' outputState: \'' + helpers._actual.outputState() + '\',\n';
if (command) {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\',\n';
output += ' args: {\n';
output += ' command: { name: \'' + command.name + '\' },\n';
requisition.getAssignments().forEach(function(assignment) {
output += ' ' + assignment.param.name + ': { ';
if (typeof assignment.value === 'string') {
output += 'value: \'' + assignment.value + '\', ';
}
else if (helpers._directToString.indexOf(typeof assignment.value) !== -1) {
output += 'value: ' + assignment.value + ', ';
}
else if (assignment.value === null) {
output += 'value: ' + assignment.value + ', ';
}
else {
output += '/*value:' + assignment.value + ',*/ ';
}
output += 'arg: \'' + assignment.arg + '\', ';
output += 'status: \'' + assignment.getStatus().toString() + '\', ';
output += 'message: \'' + assignment.getMessage() + '\'';
output += ' },\n';
});
output += ' }\n';
}
else {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\'\n';
}
output += '});';
return output;
};
/**
* We're splitting status into setup() which alters the state of the system
* and check() which ensures that things are in the right place afterwards.
*/
helpers.setInput = function(typed, cursor) {
helpers._display.inputter.setInput(typed);
if (cursor) {
helpers._display.inputter.setCursor({ start: cursor, end: cursor });
}
helpers._display.focusManager.onInputChange();
test.log('setInput("' + typed + '"' + (cursor == null ? '' : ', ' + cursor) + ')');
};
/**
* Simulate focusing the input field
*/
helpers.focusInput = function() {
helpers._display.inputter.focus();
};
/**
* Simulate pressing TAB in the input field
*/
helpers.pressTab = function() {
helpers.pressKey(9 /*KeyEvent.DOM_VK_TAB*/);
};
/**
* Simulate pressing RETURN in the input field
*/
helpers.pressReturn = function() {
helpers.pressKey(13 /*KeyEvent.DOM_VK_RETURN*/);
};
/**
* Simulate pressing a key by keyCode in the input field
*/
helpers.pressKey = function(keyCode) {
var fakeEvent = {
keyCode: keyCode,
preventDefault: function() { },
timeStamp: new Date().getTime()
};
helpers._display.inputter.onKeyDown(fakeEvent);
helpers._display.inputter.onKeyUp(fakeEvent);
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* hints: The hint text, i.e. a concatenation of the directTabText, the
* emptyParameters and the arrowTabText. The text as inserted into the UI
* will include NBSP and Unicode RARR characters, these should be
* represented using normal space and '->' for the arrow
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
helpers.check = function(checks) {
if ('input' in checks) {
test.is(helpers._actual.input(), checks.input, 'input');
}
if ('cursor' in checks) {
test.is(helpers._actual.cursor(), checks.cursor, 'cursor');
}
if ('current' in checks) {
test.is(helpers._actual.current(), checks.current, 'current');
}
if ('status' in checks) {
test.is(helpers._actual.status(), checks.status, 'status');
}
if ('markup' in checks) {
test.is(helpers._actual.markup(), checks.markup, 'markup');
}
if ('hints' in checks) {
test.is(helpers._actual.hints(), checks.hints, 'hints');
}
if ('tooltipState' in checks) {
test.is(helpers._actual.tooltipState(), checks.tooltipState, 'tooltipState');
}
if ('outputState' in checks) {
test.is(helpers._actual.outputState(), checks.outputState, 'outputState');
}
if (checks.args != null) {
var requisition = helpers._display.requisition;
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
test.ok(false, 'Unknown arg: ' + paramName);
return;
}
if ('value' in check) {
test.is(assignment.value,
check.value,
'arg.' + paramName + '.value');
}
if ('name' in check) {
test.is(assignment.value.name,
check.name,
'arg.' + paramName + '.name');
}
if ('type' in check) {
test.is(assignment.arg.type,
check.type,
'arg.' + paramName + '.type');
}
if ('arg' in check) {
test.is(assignment.arg.toString(),
check.arg,
'arg.' + paramName + '.arg');
}
if ('status' in check) {
test.is(assignment.getStatus().toString(),
check.status,
'arg.' + paramName + '.status');
}
if ('message' in check) {
test.is(assignment.getMessage(),
check.message,
'arg.' + paramName + '.message');
}
});
}
};
/**
* Execute a command:
*
* helpers.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // Regex to test against textContent of output
* blankOutput: true, // Special checks when there is no output
* });
*/
helpers.exec = function(tests) {
var requisition = helpers._display.requisition;
var inputter = helpers._display.inputter;
tests = tests || {};
if (tests.typed) {
inputter.setInput(tests.typed);
}
var typed = inputter.getInputState().typed;
var output = requisition.exec({ hidden: true });
test.is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
test.ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// test.ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
test.is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
var expectedArg = tests.args[arg];
var actualArg = output.args[arg];
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
test.ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
test.is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (var i = 0; i < expectedArg.length; i++) {
test.is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
test.is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
});
}
if (!options.window.document.createElement) {
test.log('skipping output tests (missing doc.createElement) for ' + typed);
return;
}
var div = options.window.document.createElement('div');
output.toDom(div);
var displayed = div.textContent.trim();
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
test.ok(false, "html output for " + typed + " against " + match.source);
console.log("Actual textContent");
console.log(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
test.ok(false, "html for " + typed + " (textContent sent to info)");
console.log("Actual textContent");
console.log(displayed);
}
}
};

View File

@ -28,7 +28,7 @@ _BROWSER_TEST_FILES = \
browser_styleeditor_sv_resize.js \
four.html \
head.js \
helper.js \
helpers.js \
media.html \
media-small.css \
minified.html \

View File

@ -13,95 +13,136 @@ function test() {
}
function testEditStatus(browser, tab) {
DeveloperToolbarTest.checkInputStatus({
typed: "edit",
markup: "VVVV",
status: "ERROR",
emptyParameters: [ " <resource>", " [line]" ],
helpers.setInput('edit');
helpers.check({
input: 'edit',
hints: ' <resource> [line]',
markup: 'VVVV',
status: 'ERROR',
args: {
resource: { status: 'INCOMPLETE' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit i",
markup: "VVVVVI",
status: "ERROR",
directTabText: "nline-css",
emptyParameters: [ " [line]" ],
helpers.setInput('edit i');
helpers.check({
input: 'edit i',
hints: 'nline-css [line]',
markup: 'VVVVVI',
status: 'ERROR',
args: {
resource: { arg: ' i', status: 'INCOMPLETE' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit c",
markup: "VVVVVI",
status: "ERROR",
directTabText: "ss#style2",
emptyParameters: [ " [line]" ],
helpers.setInput('edit c');
helpers.check({
input: 'edit c',
hints: 'ss#style2 [line]',
markup: 'VVVVVI',
status: 'ERROR',
args: {
resource: { arg: ' c', status: 'INCOMPLETE' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit http",
markup: "VVVVVIIII",
status: "ERROR",
directTabText: "://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css",
arrowTabText: "",
emptyParameters: [ " [line]" ],
helpers.setInput('edit http');
helpers.check({
input: 'edit http',
hints: '://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css [line]',
markup: 'VVVVVIIII',
status: 'ERROR',
args: {
resource: { arg: ' http', status: 'INCOMPLETE', message: '' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit page1",
markup: "VVVVVIIIII",
status: "ERROR",
directTabText: "",
arrowTabText: "http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css",
emptyParameters: [ " [line]" ],
helpers.setInput('edit page1');
helpers.check({
input: 'edit page1',
hints: ' [line] -> http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css',
markup: 'VVVVVIIIII',
status: 'ERROR',
args: {
resource: { arg: ' page1', status: 'INCOMPLETE', message: '' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit page2",
markup: "VVVVVIIIII",
status: "ERROR",
directTabText: "",
arrowTabText: "http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage2.css",
emptyParameters: [ " [line]" ],
helpers.setInput('edit page2');
helpers.check({
input: 'edit page2',
hints: ' [line] -> http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage2.css',
markup: 'VVVVVIIIII',
status: 'ERROR',
args: {
resource: { arg: ' page2', status: 'INCOMPLETE', message: '' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit stylez",
markup: "VVVVVEEEEEE",
status: "ERROR",
directTabText: "",
arrowTabText: "",
emptyParameters: [ " [line]" ],
helpers.setInput('edit stylez');
helpers.check({
input: 'edit stylez',
hints: ' [line]',
markup: 'VVVVVEEEEEE',
status: 'ERROR',
args: {
resource: { arg: ' stylez', status: 'ERROR', message: 'Can\'t use \'stylez\'.' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit css#style2",
markup: "VVVVVVVVVVVVVVV",
status: "VALID",
directTabText: "",
emptyParameters: [ " [line]" ],
helpers.setInput('edit css#style2');
helpers.check({
input: 'edit css#style2',
hints: ' [line]',
markup: 'VVVVVVVVVVVVVVV',
status: 'VALID',
args: {
resource: { arg: ' css#style2', status: 'VALID', message: '' },
line: { status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit css#style2 5",
markup: "VVVVVVVVVVVVVVVVV",
status: "VALID",
directTabText: "",
emptyParameters: [ ],
helpers.setInput('edit css#style2 5');
helpers.check({
input: 'edit css#style2 5',
hints: '',
markup: 'VVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
resource: { arg: ' css#style2', status: 'VALID', message: '' },
line: { value: 5, arg: ' 5', status: 'VALID' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit css#style2 0",
markup: "VVVVVVVVVVVVVVVVE",
status: "ERROR",
directTabText: "",
emptyParameters: [ ],
helpers.setInput('edit css#style2 0');
helpers.check({
input: 'edit css#style2 0',
hints: '',
markup: 'VVVVVVVVVVVVVVVVE',
status: 'ERROR',
args: {
resource: { arg: ' css#style2', status: 'VALID', message: '' },
line: { arg: ' 0', status: 'ERROR', message: '0 is smaller than minimum allowed: 1.' },
}
});
DeveloperToolbarTest.checkInputStatus({
typed: "edit css#style2 -1",
markup: "VVVVVVVVVVVVVVVVEE",
status: "ERROR",
directTabText: "",
emptyParameters: [ ],
helpers.setInput('edit css#style2 -1');
helpers.check({
input: 'edit css#style2 -1',
hints: '',
markup: 'VVVVVVVVVVVVVVVVEE',
status: 'ERROR',
args: {
resource: { arg: ' css#style2', status: 'VALID', message: '' },
line: { arg: ' -1', status: 'ERROR', message: '-1 is smaller than minimum allowed: 1.' },
}
});
}

View File

@ -9,7 +9,7 @@ let gChromeWindow; //StyleEditorChrome window
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
function cleanup()
{

View File

@ -1,459 +0,0 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};

View File

@ -0,0 +1,881 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*
* DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
* OF THIS FILE.
*
* UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
* THE NAUGHTY STEP.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* FOR A LONG TIME.
*
*/
/*
* Use as a JSM
* ------------
* helpers._createDebugCheck() and maybe other functions in this file can be
* useful at runtime, so it is possible to use helpers.js as a JSM.
* Copy commandline/test/helpers.js to shared/helpers.jsm, and then add to
* DeveloperToolbar.jsm the following:
*
* XPCOMUtils.defineLazyModuleGetter(this, "helpers",
* "resource:///modules/devtools/helpers.jsm");
*
* At the bottom of DeveloperToolbar.prototype._onload add this:
*
* var options = { display: this.display };
* this._input.onkeypress = function(ev) {
* helpers.setup(options);
* dump(helpers._createDebugCheck() + '\n\n');
* };
*
* Now GCLI will emit output on every keypress that both explains the state
* of GCLI and can be run as a test case.
*/
var EXPORTED_SYMBOLS = [ 'helpers' ];
var test = { };
/**
* Various functions for testing DeveloperToolbar.
* Parts of this code exist in:
* - browser/devtools/commandline/test/head.js
* - browser/devtools/shared/test/head.js
*/
let DeveloperToolbarTest = { };
/**
* Paranoid DeveloperToolbar.show();
*/
DeveloperToolbarTest.show = function DTT_show(aCallback) {
if (DeveloperToolbar.visible) {
ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
}
else {
DeveloperToolbar.show(true, aCallback);
}
};
/**
* Paranoid DeveloperToolbar.hide();
*/
DeveloperToolbarTest.hide = function DTT_hide() {
if (!DeveloperToolbar.visible) {
ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
}
else {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Test inputs
* typed: The text to type at the input
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
* directTabText: Simple completion text
* arrowTabText: When the completion is not an extension (without arrow)
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
if (!checks.emptyParameters) {
checks.emptyParameters = [];
}
if (!checks.directTabText) {
checks.directTabText = '';
}
if (!checks.arrowTabText) {
checks.arrowTabText = '';
}
var display = DeveloperToolbar.display;
if (checks.typed) {
info('Starting tests for ' + checks.typed);
display.inputter.setInput(checks.typed);
}
else {
ok(false, "Missing typed for " + JSON.stringify(checks));
return;
}
if (checks.cursor) {
display.inputter.setCursor(checks.cursor)
}
var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
var requisition = display.requisition;
var completer = display.completer;
var actual = completer._getCompleterTemplateData();
/*
if (checks.input) {
is(display.inputter.element.value,
checks.input,
'input');
}
if (checks.cursor) {
is(display.inputter.element.selectionStart,
checks.cursor,
'cursor');
}
*/
if (checks.status) {
is(requisition.getStatus().toString(),
checks.status,
'status');
}
if (checks.markup) {
var statusMarkup = requisition.getInputStatusMarkup(cursor);
var actualMarkup = statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
is(checks.markup,
actualMarkup,
'markup');
}
if (checks.emptyParameters) {
var actualParams = actual.emptyParameters;
is(actualParams.length,
checks.emptyParameters.length,
'emptyParameters.length');
if (actualParams.length === checks.emptyParameters.length) {
for (var i = 0; i < actualParams.length; i++) {
is(actualParams[i].replace(/\u00a0/g, ' '),
checks.emptyParameters[i],
'emptyParameters[' + i + ']');
}
}
else {
info('Expected: [ \"' + actualParams.join('", "') + '" ]');
}
}
if (checks.directTabText) {
is(actual.directTabText,
checks.directTabText,
'directTabText');
}
if (checks.arrowTabText) {
is(actual.arrowTabText,
' \u00a0\u21E5 ' + checks.arrowTabText,
'arrowTabText');
}
if (checks.args) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
ok(false, 'Unknown parameter: ' + paramName);
return;
}
if (check.value) {
is(assignment.value,
check.value,
'checkStatus value for ' + paramName);
}
if (check.name) {
is(assignment.value.name,
check.name,
'checkStatus name for ' + paramName);
}
if (check.type) {
is(assignment.arg.type,
check.type,
'checkStatus type for ' + paramName);
}
if (check.arg) {
is(assignment.arg.toString(),
check.arg,
'checkStatus arg for ' + paramName);
}
if (check.status) {
is(assignment.getStatus().toString(),
check.status,
'checkStatus status for ' + paramName);
}
if (check.message) {
is(assignment.getMessage(),
check.message,
'checkStatus message for ' + paramName);
}
});
}
};
/**
* Execute a command:
*
* DeveloperToolbarTest.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // RegExp to test against textContent of output
* // (can also be array of RegExps)
* blankOutput: true, // Special checks when there is no output
* });
*/
DeveloperToolbarTest.exec = function DTT_exec(tests) {
tests = tests || {};
if (tests.typed) {
DeveloperToolbar.display.inputter.setInput(tests.typed);
}
let typed = DeveloperToolbar.display.inputter.getInputState().typed;
let output = DeveloperToolbar.display.requisition.exec();
is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
let expectedArg = tests.args[arg];
let actualArg = output.args[arg];
if (typeof expectedArg === 'function') {
ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
}
else {
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (let i = 0; i < expectedArg.length; i++) {
is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
}
});
}
let displayed = DeveloperToolbar.outputPanel._div.textContent;
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
ok(false, "html output for " + typed + " against " + match.source +
" (textContent sent to info)");
info("Actual textContent");
info(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
ok(false, "html output for " + typed + " (textContent sent to info)");
info("Actual textContent");
info(displayed);
}
}
};
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param target Either a function or array of functions containing the tests
* to run. If an array of test function is passed then we will clear up after
* the tests have completed. If a single test function is passed then this
* function should arrange for 'finish()' to be called on completion.
*/
DeveloperToolbarTest.test = function DTT_test(uri, target) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) {
menuItem.hidden = true;
}
if (command) {
command.setAttribute("disabled", "true");
}
if (appMenuItem) {
appMenuItem.hidden = true;
}
// leakHunt({ DeveloperToolbar: DeveloperToolbar });
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) {
menuItem.hidden = false;
}
if (command) {
command.removeAttribute("disabled");
}
if (appMenuItem) {
appMenuItem.hidden = false;
}
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
content.location = uri;
let tab = gBrowser.selectedTab;
let browser = gBrowser.getBrowserForTab(tab);
var onTabLoad = function() {
browser.removeEventListener("load", onTabLoad, true);
DeveloperToolbarTest.show(function() {
if (helpers) {
helpers.setup({ display: DeveloperToolbar.display });
}
if (Array.isArray(target)) {
try {
target.forEach(function(func) {
func(browser, tab);
})
}
finally {
DeveloperToolbarTest._checkFinish();
}
}
else {
try {
target(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
DeveloperToolbarTest._finish();
throw ex;
}
}
});
}
browser.addEventListener("load", onTabLoad, true);
};
DeveloperToolbarTest._outstanding = [];
DeveloperToolbarTest._checkFinish = function() {
info('_checkFinish. ' + DeveloperToolbarTest._outstanding.length + ' outstanding');
if (DeveloperToolbarTest._outstanding.length == 0) {
DeveloperToolbarTest._finish();
}
}
DeveloperToolbarTest._finish = function() {
info('Finish');
DeveloperToolbarTest.closeAllTabs();
finish();
}
DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
var todo = function() {
var reply = aFunc.apply(aScope, arguments);
DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
return aJob != todo;
});
DeveloperToolbarTest._checkFinish();
return reply;
}
DeveloperToolbarTest._outstanding.push(todo);
return todo;
};
DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
return function() {
ok(false, aMsg);
return aFunc.apply(aScope, arguments);
}
};
/**
*
*/
DeveloperToolbarTest.closeAllTabs = function() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
};
///////////////////////////////////////////////////////////////////////////////
var helpers = {};
helpers._display = undefined;
helpers.setup = function(options) {
helpers._display = options.display;
if (typeof ok !== 'undefined') {
test.ok = ok;
test.is = is;
test.log = info;
}
};
helpers.shutdown = function(options) {
helpers._display = undefined;
};
/**
* Various functions to return the actual state of the command line
*/
helpers._actual = {
input: function() {
return helpers._display.inputter.element.value;
},
hints: function() {
var templateData = helpers._display.completer._getCompleterTemplateData();
var actualHints = templateData.directTabText +
templateData.emptyParameters.join('') +
templateData.arrowTabText;
return actualHints.replace(/\u00a0/g, ' ')
.replace(/\u21E5/, '->')
.replace(/ $/, '');
},
markup: function() {
var cursor = helpers._display.inputter.element.selectionStart;
var statusMarkup = helpers._display.requisition.getInputStatusMarkup(cursor);
return statusMarkup.map(function(s) {
return Array(s.string.length + 1).join(s.status.toString()[0]);
}).join('');
},
cursor: function() {
return helpers._display.inputter.element.selectionStart;
},
current: function() {
return helpers._display.requisition.getAssignmentAt(helpers._actual.cursor()).param.name;
},
status: function() {
return helpers._display.requisition.getStatus().toString();
},
outputState: function() {
var outputData = helpers._display.focusManager._shouldShowOutput();
return outputData.visible + ':' + outputData.reason;
},
tooltipState: function() {
var tooltipData = helpers._display.focusManager._shouldShowTooltip();
return tooltipData.visible + ':' + tooltipData.reason;
}
};
helpers._directToString = [ 'boolean', 'undefined', 'number' ];
helpers._createDebugCheck = function() {
var requisition = helpers._display.requisition;
var command = requisition.commandAssignment.value;
var input = helpers._actual.input();
var padding = Array(input.length + 1).join(' ');
var output = '';
output += 'helpers.setInput(\'' + input + '\');\n';
output += 'helpers.check({\n';
output += ' input: \'' + input + '\',\n';
output += ' hints: ' + padding + '\'' + helpers._actual.hints() + '\',\n';
output += ' markup: \'' + helpers._actual.markup() + '\',\n';
output += ' cursor: ' + helpers._actual.cursor() + ',\n';
output += ' current: \'' + helpers._actual.current() + '\',\n';
output += ' status: \'' + helpers._actual.status() + '\',\n';
output += ' outputState: \'' + helpers._actual.outputState() + '\',\n';
if (command) {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\',\n';
output += ' args: {\n';
output += ' command: { name: \'' + command.name + '\' },\n';
requisition.getAssignments().forEach(function(assignment) {
output += ' ' + assignment.param.name + ': { ';
if (typeof assignment.value === 'string') {
output += 'value: \'' + assignment.value + '\', ';
}
else if (helpers._directToString.indexOf(typeof assignment.value) !== -1) {
output += 'value: ' + assignment.value + ', ';
}
else if (assignment.value === null) {
output += 'value: ' + assignment.value + ', ';
}
else {
output += '/*value:' + assignment.value + ',*/ ';
}
output += 'arg: \'' + assignment.arg + '\', ';
output += 'status: \'' + assignment.getStatus().toString() + '\', ';
output += 'message: \'' + assignment.getMessage() + '\'';
output += ' },\n';
});
output += ' }\n';
}
else {
output += ' tooltipState: \'' + helpers._actual.tooltipState() + '\'\n';
}
output += '});';
return output;
};
/**
* We're splitting status into setup() which alters the state of the system
* and check() which ensures that things are in the right place afterwards.
*/
helpers.setInput = function(typed, cursor) {
helpers._display.inputter.setInput(typed);
if (cursor) {
helpers._display.inputter.setCursor({ start: cursor, end: cursor });
}
helpers._display.focusManager.onInputChange();
test.log('setInput("' + typed + '"' + (cursor == null ? '' : ', ' + cursor) + ')');
};
/**
* Simulate focusing the input field
*/
helpers.focusInput = function() {
helpers._display.inputter.focus();
};
/**
* Simulate pressing TAB in the input field
*/
helpers.pressTab = function() {
helpers.pressKey(9 /*KeyEvent.DOM_VK_TAB*/);
};
/**
* Simulate pressing RETURN in the input field
*/
helpers.pressReturn = function() {
helpers.pressKey(13 /*KeyEvent.DOM_VK_RETURN*/);
};
/**
* Simulate pressing a key by keyCode in the input field
*/
helpers.pressKey = function(keyCode) {
var fakeEvent = {
keyCode: keyCode,
preventDefault: function() { },
timeStamp: new Date().getTime()
};
helpers._display.inputter.onKeyDown(fakeEvent);
helpers._display.inputter.onKeyUp(fakeEvent);
};
/**
* check() is the new status. Similar API except that it doesn't attempt to
* alter the display/requisition at all, and it makes extra checks.
* Available checks:
* input: The text displayed in the input field
* cursor: The position of the start of the cursor
* status: One of "VALID", "ERROR", "INCOMPLETE"
* hints: The hint text, i.e. a concatenation of the directTabText, the
* emptyParameters and the arrowTabText. The text as inserted into the UI
* will include NBSP and Unicode RARR characters, these should be
* represented using normal space and '->' for the arrow
* markup: What state should the error markup be in. e.g. "VVVIIIEEE"
* args: Maps of checks to make against the arguments:
* value: i.e. assignment.value (which ignores defaultValue)
* type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
* Care should be taken with this since it's something of an
* implementation detail
* arg: The toString value of the argument
* status: i.e. assignment.getStatus
* message: i.e. assignment.getMessage
* name: For commands - checks assignment.value.name
*/
helpers.check = function(checks) {
if ('input' in checks) {
test.is(helpers._actual.input(), checks.input, 'input');
}
if ('cursor' in checks) {
test.is(helpers._actual.cursor(), checks.cursor, 'cursor');
}
if ('current' in checks) {
test.is(helpers._actual.current(), checks.current, 'current');
}
if ('status' in checks) {
test.is(helpers._actual.status(), checks.status, 'status');
}
if ('markup' in checks) {
test.is(helpers._actual.markup(), checks.markup, 'markup');
}
if ('hints' in checks) {
test.is(helpers._actual.hints(), checks.hints, 'hints');
}
if ('tooltipState' in checks) {
test.is(helpers._actual.tooltipState(), checks.tooltipState, 'tooltipState');
}
if ('outputState' in checks) {
test.is(helpers._actual.outputState(), checks.outputState, 'outputState');
}
if (checks.args != null) {
var requisition = helpers._display.requisition;
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
test.ok(false, 'Unknown arg: ' + paramName);
return;
}
if ('value' in check) {
test.is(assignment.value,
check.value,
'arg.' + paramName + '.value');
}
if ('name' in check) {
test.is(assignment.value.name,
check.name,
'arg.' + paramName + '.name');
}
if ('type' in check) {
test.is(assignment.arg.type,
check.type,
'arg.' + paramName + '.type');
}
if ('arg' in check) {
test.is(assignment.arg.toString(),
check.arg,
'arg.' + paramName + '.arg');
}
if ('status' in check) {
test.is(assignment.getStatus().toString(),
check.status,
'arg.' + paramName + '.status');
}
if ('message' in check) {
test.is(assignment.getMessage(),
check.message,
'arg.' + paramName + '.message');
}
});
}
};
/**
* Execute a command:
*
* helpers.exec({
* // Test inputs
* typed: "echo hi", // Optional, uses existing if undefined
*
* // Thing to check
* args: { message: "hi" }, // Check that the args were understood properly
* outputMatch: /^hi$/, // Regex to test against textContent of output
* blankOutput: true, // Special checks when there is no output
* });
*/
helpers.exec = function(tests) {
var requisition = helpers._display.requisition;
var inputter = helpers._display.inputter;
tests = tests || {};
if (tests.typed) {
inputter.setInput(tests.typed);
}
var typed = inputter.getInputState().typed;
var output = requisition.exec({ hidden: true });
test.is(typed, output.typed, 'output.command for: ' + typed);
if (tests.completed !== false) {
test.ok(output.completed, 'output.completed false for: ' + typed);
}
else {
// It is actually an error if we say something is async and it turns
// out not to be? For now we're saying 'no'
// test.ok(!output.completed, 'output.completed true for: ' + typed);
}
if (tests.args != null) {
test.is(Object.keys(tests.args).length, Object.keys(output.args).length,
'arg count for ' + typed);
Object.keys(output.args).forEach(function(arg) {
var expectedArg = tests.args[arg];
var actualArg = output.args[arg];
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
test.ok(false, 'actual is not an array. ' + typed + '/' + arg);
return;
}
test.is(expectedArg.length, actualArg.length,
'array length: ' + typed + '/' + arg);
for (var i = 0; i < expectedArg.length; i++) {
test.is(expectedArg[i], actualArg[i],
'member: "' + typed + '/' + arg + '/' + i);
}
}
else {
test.is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
}
});
}
if (!options.window.document.createElement) {
test.log('skipping output tests (missing doc.createElement) for ' + typed);
return;
}
var div = options.window.document.createElement('div');
output.toDom(div);
var displayed = div.textContent.trim();
if (tests.outputMatch) {
var doTest = function(match, against) {
if (!match.test(against)) {
test.ok(false, "html output for " + typed + " against " + match.source);
console.log("Actual textContent");
console.log(against);
}
}
if (Array.isArray(tests.outputMatch)) {
tests.outputMatch.forEach(function(match) {
doTest(match, displayed);
});
}
else {
doTest(tests.outputMatch, displayed);
}
}
if (tests.blankOutput != null) {
if (!/^$/.test(displayed)) {
test.ok(false, "html for " + typed + " (textContent sent to info)");
console.log("Actual textContent");
console.log(displayed);
}
}
};