diff --git a/browser/devtools/styleeditor/test/Makefile.in b/browser/devtools/styleeditor/test/Makefile.in new file mode 100644 index 00000000000..28db3bfd027 --- /dev/null +++ b/browser/devtools/styleeditor/test/Makefile.in @@ -0,0 +1,72 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Style Editor code. +# +# The Initial Developer of the Original Code is Mozilla Foundation. +# +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Cedric Vivier (original author) +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = browser/devtools/styleeditor/test + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_BROWSER_TEST_FILES = \ + browser_styleeditor_enabled.js \ + browser_styleeditor_import.js \ + browser_styleeditor_init.js \ + browser_styleeditor_loading.js \ + browser_styleeditor_new.js \ + browser_styleeditor_pretty.js \ + browser_styleeditor_readonly.js \ + browser_styleeditor_reopen.js \ + browser_styleeditor_sv_filter.js \ + browser_styleeditor_sv_keynav.js \ + browser_styleeditor_sv_resize.js \ + four.html \ + head.js \ + media.html \ + media-small.css \ + minified.html \ + simple.css \ + simple.css.gz \ + simple.css.gz^headers^ \ + simple.gz.html \ + simple.html \ + $(NULL) + +libs:: $(_BROWSER_TEST_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js b/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js new file mode 100644 index 00000000000..0a77e4bc4e9 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js @@ -0,0 +1,101 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// https rather than chrome to improve coverage +const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html"; + + +function test() +{ + waitForExplicitFinish(); + + let count = 0; + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onEditorAdded: function (aChrome, aEditor) { + count++; + if (count == 2) { + // we test against first stylesheet after all are ready + let editor = aChrome.editors[0]; + if (!editor.sourceEditor) { + editor.addActionListener({ + onAttach: function (aEditor) { + run(aChrome, aEditor); + } + }); + } else { + run(aChrome, editor); + } + } + } + }); + }); + + content.location = TESTCASE_URI; +} + +function run(aChrome, aEditor) +{ + testEnabledToggle(aChrome, aEditor); +} + +function testEnabledToggle(aChrome, aEditor) +{ + is(aEditor, aChrome.editors[0], + "stylesheet with index 0 is the first stylesheet listed in the UI"); + + let firstStyleSheetEditor = aEditor; + let firstStyleSheetUI = aChrome.getSummaryElementForEditor(aEditor); + let enabledToggle = firstStyleSheetUI.querySelector(".stylesheet-enabled"); + + is(firstStyleSheetEditor.contentDocument.styleSheets[0].disabled, false, + "first stylesheet is initially enabled"); + is(firstStyleSheetEditor.hasFlag("disabled"), false, + "first stylesheet is initially enabled, it does not have DISABLED flag"); + is(firstStyleSheetUI.classList.contains("disabled"), false, + "first stylesheet is initially enabled, UI does not have DISABLED class"); + + let disabledToggleCount = 0; + firstStyleSheetEditor.addActionListener({ + onFlagChange: function (aEditor, aFlagName) { + if (aFlagName != "disabled") { + return; + } + disabledToggleCount++; + + if (disabledToggleCount == 1) { + is(firstStyleSheetEditor, aEditor, + "FlagChange handler triggered for DISABLED flag on the first editor"); + is(firstStyleSheetEditor.styleSheet.disabled, true, + "first stylesheet is now disabled"); + is(firstStyleSheetEditor.hasFlag("disabled"), true, + "first stylesheet is now disabled, it has DISABLED flag"); + is(firstStyleSheetUI.classList.contains("disabled"), true, + "first stylesheet is now disabled, UI has DISABLED class"); + + // now toggle it back to enabled + waitForFocus(function () { + EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow); + }, gChromeWindow); + return; + } + + // disabledToggleCount == 2 + is(firstStyleSheetEditor, aEditor, + "FlagChange handler triggered for DISABLED flag on the first editor (2)"); + is(firstStyleSheetEditor.styleSheet.disabled, false, + "first stylesheet is now enabled again"); + is(firstStyleSheetEditor.hasFlag("disabled"), false, + "first stylesheet is now enabled again, it does not have DISABLED flag"); + is(firstStyleSheetUI.classList.contains("disabled"), false, + "first stylesheet is now enabled again, UI does not have DISABLED class"); + + finish(); + } + }); + + waitForFocus(function () { + EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow); + }, gChromeWindow); +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_import.js b/browser/devtools/styleeditor/test/browser_styleeditor_import.js new file mode 100644 index 00000000000..dc8ad16d57b --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_import.js @@ -0,0 +1,87 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// http rather than chrome to improve coverage +const TESTCASE_URI = TEST_BASE_HTTP + "simple.html"; + +Components.utils.import("resource://gre/modules/FileUtils.jsm"); +const FILENAME = "styleeditor-import-test.css"; +const SOURCE = "body{background:red;}"; + + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onContentAttach: run, + onEditorAdded: testEditorAdded + }); + if (aChrome.isContentAttached) { + run(aChrome); + } + }); + + content.location = TESTCASE_URI; +} + +function run(aChrome) +{ + is(aChrome.editors.length, 2, + "there is 2 stylesheets initially"); +} + +function testImport(aChrome, aEditor) +{ + // create file to import first + let file = FileUtils.getFile("ProfD", [FILENAME]); + let ostream = FileUtils.openSafeFileOutputStream(file); + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + let istream = converter.convertToInputStream(SOURCE); + NetUtil.asyncCopy(istream, ostream, function (status) { + FileUtils.closeSafeFileOutputStream(ostream); + + // click the import button now that the file to import is ready + aChrome._mockImportFile = file; + + waitForFocus(function () { + let document = gChromeWindow.document + let importButton = document.querySelector(".style-editor-importButton"); + EventUtils.synthesizeMouseAtCenter(importButton, {}, gChromeWindow); + }, gChromeWindow); + }); +} + +let gAddedCount = 0; +function testEditorAdded(aChrome, aEditor) +{ + if (++gAddedCount == 2) { + // test import after the 2 initial stylesheets have been loaded + if (!aChrome.editors[0].sourceEditor) { + aChrome.editors[0].addActionListener({ + onAttach: function () { + testImport(aChrome); + } + }); + } else { + testImport(aChrome); + } + } + + if (!aEditor.hasFlag("imported")) { + return; + } + + ok(!aEditor.hasFlag("inline"), + "imported stylesheet does not have INLINE flag"); + ok(aEditor.savedFile, + "imported stylesheet will be saved directly into the same file"); + is(aEditor.getFriendlyName(), FILENAME, + "imported stylesheet has the same name as the filename"); + + finish(); +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_init.js b/browser/devtools/styleeditor/test/browser_styleeditor_init.js new file mode 100644 index 00000000000..f810b85d6d4 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js @@ -0,0 +1,126 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; + + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onContentAttach: run, + onEditorAdded: testEditorAdded + }); + if (aChrome.isContentAttached) { + run(aChrome); + } + }); + + content.location = TESTCASE_URI; +} + +let gContentAttachHandled = false; +function run(aChrome) +{ + gContentAttachHandled = true; + is(aChrome.contentWindow.document.readyState, "complete", + "content document is complete"); + + let SEC = gChromeWindow.styleEditorChrome; + is(SEC, aChrome, "StyleEditorChrome object exists as new window property"); + + ok(gChromeWindow.document.title.indexOf("simple testcase") >= 0, + "the Style Editor window title contains the document's title"); + + // check editors are instantiated + is(SEC.editors.length, 2, + "there is two StyleEditor instances managed"); + ok(SEC.editors[0].styleSheetIndex < SEC.editors[1].styleSheetIndex, + "editors are ordered by styleSheetIndex"); + + // check StyleEditorChrome is a singleton wrt to the same DOMWindow + let chromeWindow = StyleEditor.openChrome(); + is(chromeWindow, gChromeWindow, + "attempt to edit the same document returns the same Style Editor window"); +} + +let gEditorAddedCount = 0; +function testEditorAdded(aChrome, aEditor) +{ + if (!gEditorAddedCount) { + is(gContentAttachHandled, true, + "ContentAttach event triggered before EditorAdded"); + } + + if (aEditor.styleSheetIndex == 0) { + gEditorAddedCount++; + testFirstStyleSheetEditor(aChrome, aEditor); + } + if (aEditor.styleSheetIndex == 1) { + gEditorAddedCount++; + testSecondStyleSheetEditor(aChrome, aEditor); + } + + if (gEditorAddedCount == 2) { + finish(); + } +} + +function testFirstStyleSheetEditor(aChrome, aEditor) +{ + //testing TESTCASE's simple.css stylesheet + is(aEditor.styleSheetIndex, 0, + "first stylesheet is at index 0"); + + is(aEditor, aChrome.editors[0], + "first stylesheet corresponds to StyleEditorChrome.editors[0]"); + + ok(!aEditor.hasFlag("inline"), + "first stylesheet does not have INLINE flag"); + + let summary = aChrome.getSummaryElementForEditor(aEditor); + ok(!summary.classList.contains("inline"), + "first stylesheet UI does not have INLINE class"); + + let name = summary.querySelector(".stylesheet-name").textContent; + is(name, "simple.css", + "first stylesheet's name is `simple.css`"); + + let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; + is(parseInt(ruleCount), 1, + "first stylesheet UI shows rule count as 1"); + + ok(summary.classList.contains("splitview-active"), + "first stylesheet UI is focused/active"); +} + +function testSecondStyleSheetEditor(aChrome, aEditor) +{ + //testing TESTCASE's inline stylesheet + is(aEditor.styleSheetIndex, 1, + "second stylesheet is at index 1"); + + is(aEditor, aChrome.editors[1], + "second stylesheet corresponds to StyleEditorChrome.editors[1]"); + + ok(aEditor.hasFlag("inline"), + "second stylesheet has INLINE flag"); + + let summary = aChrome.getSummaryElementForEditor(aEditor); + ok(summary.classList.contains("inline"), + "second stylesheet UI has INLINE class"); + + let name = summary.querySelector(".stylesheet-name").textContent; + ok(/^<.*>$/.test(name), + "second stylesheet's name is surrounded by `<>`"); + + let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; + is(parseInt(ruleCount), 3, + "second stylesheet UI shows rule count as 3"); + + ok(!summary.classList.contains("splitview-active"), + "second stylesheet UI is NOT focused/active"); +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_loading.js b/browser/devtools/styleeditor/test/browser_styleeditor_loading.js new file mode 100644 index 00000000000..01cb48201d5 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_loading.js @@ -0,0 +1,39 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; + + +function test() +{ + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + + // launch Style Editor right when the tab is created (before load) + // this checks that the Style Editor still launches correctly when it is opened + // *while* the page is still loading + launchStyleEditorChrome(function (aChrome) { + isnot(gBrowser.selectedBrowser.contentWindow.document.readyState, "complete", + "content document is still loading"); + + if (!aChrome.isContentAttached) { + aChrome.addChromeListener({ + onContentAttach: run + }); + } else { + run(aChrome); + } + }); + + content.location = TESTCASE_URI; +} + +function run(aChrome) +{ + is(aChrome.contentWindow.document.readyState, "complete", + "content document is complete"); + + finish(); +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_new.js b/browser/devtools/styleeditor/test/browser_styleeditor_new.js new file mode 100644 index 00000000000..0509e5e6208 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js @@ -0,0 +1,117 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; + + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onContentAttach: run, + onEditorAdded: testEditorAdded + }); + if (aChrome.isContentAttached) { + run(aChrome); + } + }); + + content.location = TESTCASE_URI; +} + +function run(aChrome) +{ + is(aChrome.editors.length, 2, + "there is 2 stylesheets initially"); +} + +let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets +let gNewEditor; // to make sure only one new stylesheet got created +let gCommitCount = 0; // to make sure only one Commit event is triggered + +function testEditorAdded(aChrome, aEditor) +{ + gAddedCount++; + if (gAddedCount == 2) { + waitForFocus(function () { // create a new style sheet + let newButton = gChromeWindow.document.querySelector(".style-editor-newButton"); + EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow); + }, gChromeWindow); + } + if (gAddedCount != 3) { + return; + } + + ok(!gNewEditor, "creating a new stylesheet triggers one EditorAdded event"); + gNewEditor = aEditor; // above test will fail if we get a duplicate event + + is(aChrome.editors.length, 3, + "creating a new stylesheet added a new StyleEditor instance"); + + let listener = { + onAttach: function (aEditor) { + waitForFocus(function () { + ok(aEditor.isLoaded, + "new editor is loaded when attached"); + ok(aEditor.hasFlag("new"), + "new editor has NEW flag"); + ok(!aEditor.hasFlag("unsaved"), + "new editor does not have UNSAVED flag"); + + ok(aEditor.inputElement, + "new editor has an input element attached"); + + ok(aEditor.sourceEditor.hasFocus(), + "new editor has focus"); + + let summary = aChrome.getSummaryElementForEditor(aEditor); + let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; + is(parseInt(ruleCount), 0, + "new editor initially shows 0 rules"); + + let computedStyle = content.getComputedStyle(content.document.body, null); + is(computedStyle.backgroundColor, "rgb(255, 255, 255)", + "content's background color is initially white"); + + for each (let c in "body{background-color:red;}") { + EventUtils.synthesizeKey(c, {}, gChromeWindow); + } + }, gChromeWindow) ; + }, + + onCommit: function (aEditor) { + gCommitCount++; + + ok(aEditor.hasFlag("new"), + "new editor still has NEW flag"); + ok(aEditor.hasFlag("unsaved"), + "new editor has UNSAVED flag after modification"); + + let summary = aChrome.getSummaryElementForEditor(aEditor); + let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; + is(parseInt(ruleCount), 1, + "new editor shows 1 rule after modification"); + + let computedStyle = content.getComputedStyle(content.document.body, null); + is(computedStyle.backgroundColor, "rgb(255, 0, 0)", + "content's background color has been updated to red"); + + executeSoon(function () { + is(gCommitCount, 1, "received only one Commit event (throttle)"); + + aEditor.removeActionListener(listener); + + gNewEditor = null; + finish(); + }); + } + }; + + aEditor.addActionListener(listener); + if (aEditor.sourceEditor) { + listener.onAttach(aEditor); + } +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_pretty.js b/browser/devtools/styleeditor/test/browser_styleeditor_pretty.js new file mode 100644 index 00000000000..0651a77c343 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_pretty.js @@ -0,0 +1,56 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "minified.html"; + + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onEditorAdded: function (aChrome, aEditor) { + if (aEditor.sourceEditor) { + run(aEditor); // already attached to input element + } else { + aEditor.addActionListener({ + onAttach: run + }); + } + } + }); + }); + + content.location = TESTCASE_URI; +} + +let editorTestedCount = 0; +function run(aEditor) +{ + if (aEditor.styleSheetIndex == 0) { + let prettifiedSource = "body\{\r?\n\tbackground\:white;\r?\n\}\r?\n\r?\ndiv\{\r?\n\tfont\-size\:4em;\r?\n\tcolor\:red\r?\n\}\r?\n"; + let prettifiedSourceRE = new RegExp(prettifiedSource); + + ok(prettifiedSourceRE.test(aEditor.sourceEditor.getText()), + "minified source has been prettified automatically"); + editorTestedCount++; + let chrome = gChromeWindow.styleEditorChrome; + let summary = chrome.getSummaryElementForEditor(chrome.editors[1]); + EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow); + } + + if (aEditor.styleSheetIndex == 1) { + let originalSource = "body \{ background\: red; \}\r?\ndiv \{\r?\nfont\-size\: 5em;\r?\ncolor\: red\r?\n\}"; + let originalSourceRE = new RegExp(originalSource); + + ok(originalSourceRE.test(aEditor.sourceEditor.getText()), + "non-minified source has been left untouched"); + editorTestedCount++; + } + + if (editorTestedCount == 2) { + finish(); + } +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_readonly.js b/browser/devtools/styleeditor/test/browser_styleeditor_readonly.js new file mode 100644 index 00000000000..d307cccbb30 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_readonly.js @@ -0,0 +1,74 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; + + +let gEditorAddedCount = 0; +let gEditorReadOnlyCount = 0; +let gChromeListener = { + onEditorAdded: function (aChrome, aEditor) { + gEditorAddedCount++; + if (aEditor.readOnly) { + gEditorReadOnlyCount++; + } + + if (gEditorAddedCount == aChrome.editors.length) { + // continue testing after all editors are ready + + is(gEditorReadOnlyCount, 0, + "all editors are NOT read-only initially"); + + // all editors have been loaded, queue closing the content tab + executeSoon(function () { + gBrowser.removeCurrentTab(); + }); + } + }, + onContentDetach: function (aChrome) { + // check that the UI has switched to read-only + run(aChrome); + } +}; + +function test() +{ + waitForExplicitFinish(); + + gBrowser.addTab(); // because we'll close the next one + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener(gChromeListener); + }); + + content.location = TESTCASE_URI; +} + +function run(aChrome) +{ + let document = gChromeWindow.document; + let disabledCount; + let elements; + + disabledCount = 0; + elements = document.querySelectorAll("button,input,select"); + for (let i = 0; i < elements.length; ++i) { + if (elements[i].hasAttribute("disabled")) { + disabledCount++; + } + } + ok(elements.length && disabledCount == elements.length, + "all buttons, input and select elements are disabled"); + + disabledCount = 0; + aChrome.editors.forEach(function (aEditor) { + if (aEditor.readOnly) { + disabledCount++; + } + }); + ok(aChrome.editors.length && disabledCount == aChrome.editors.length, + "all editors are read-only"); + + aChrome.removeChromeListener(gChromeListener); + finish(); +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js b/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js new file mode 100644 index 00000000000..65210b05ec2 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js @@ -0,0 +1,155 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// http rather than chrome to improve coverage +const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +Components.utils.import("resource://gre/modules/FileUtils.jsm"); + + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onEditorAdded: function (aChrome, aEditor) { + if (aEditor.styleSheetIndex != 0) { + return; // we want to test against the first stylesheet + } + + if (aEditor.sourceEditor) { + run(aEditor); // already attached to input element + } else { + aEditor.addActionListener({ + onAttach: run + }); + } + } + }); + + gChromeWindow.addEventListener("unload", function onClose() { + gChromeWindow.removeEventListener("unload", onClose, true); + gChromeWindow = null; + executeSoon(function () { + waitForFocus(function () { + // wait that browser has focus again + // open StyleEditorChrome again (a new one since we closed the previous one) + launchStyleEditorChrome(function (aChrome) { + is(gChromeWindow.document.documentElement.hasAttribute("data-marker"), + false, + "opened a completely new StyleEditorChrome window"); + + aChrome.addChromeListener({ + onEditorAdded: function (aChrome, aEditor) { + if (aEditor.styleSheetIndex != 0) { + return; // we want to test against the first stylesheet + } + + if (aEditor.sourceEditor) { + testNewChrome(aEditor); // already attached to input element + } else { + aEditor.addActionListener({ + onAttach: testNewChrome + }); + } + } + }); + }); + }); + }); + }, true); + }); + + content.location = TESTCASE_URI; +} + +let gFilename; + +function run(aEditor) +{ + gFilename = FileUtils.getFile("ProfD", ["styleeditor-test.css"]) + + aEditor.saveToFile(gFilename, function (aFile) { + ok(aFile, "file got saved successfully"); + + aEditor.addActionListener({ + onFlagChange: function (aEditor, aFlag) { + if (aFlag != "unsaved") { + return; + } + + ok(aEditor.hasFlag("unsaved"), + "first stylesheet has UNSAVED flag after making a change"); + + // marker used to check it does not exist when we reopen + // ie. the window we opened is indeed a new one + gChromeWindow.document.documentElement.setAttribute("data-marker", "true"); + gChromeWindow.close(); + } + }); + + waitForFocus(function () { + // insert char so that this stylesheet has the UNSAVED flag + EventUtils.synthesizeKey("x", {}, gChromeWindow); + }, gChromeWindow); + }); +} + +function testNewChrome(aEditor) +{ + ok(aEditor.savedFile, + "first stylesheet editor will save directly into the same file"); + + is(aEditor.getFriendlyName(), gFilename.leafName, + "first stylesheet still has the filename as it was saved"); + gFilename = null; + + ok(aEditor.hasFlag("unsaved"), + "first stylesheet still has UNSAVED flag at reopening"); + + ok(!aEditor.hasFlag("inline"), + "first stylesheet does not have INLINE flag"); + + ok(!aEditor.hasFlag("error"), + "editor does not have error flag initially"); + let hadError = false; + + let onSaveCallback = function (aFile) { + aEditor.addActionListener({ + onFlagChange: function (aEditor, aFlag) { + if (!hadError && aFlag == "error") { + ok(aEditor.hasFlag("error"), + "editor has ERROR flag after attempting to save with invalid path"); + hadError = true; + + // save using source editor key binding (previous successful path) + waitForFocus(function () { + EventUtils.synthesizeKey("S", {accelKey: true}, gChromeWindow); + }, gChromeWindow); + return; + } + + if (hadError && aFlag == "unsaved") { + executeSoon(function () { + ok(!aEditor.hasFlag("unsaved"), + "first stylesheet has no UNSAVED flag after successful save"); + ok(!aEditor.hasFlag("error"), + "ERROR flag has been removed since last operation succeeded"); + finish(); + }); + } + } + }); + } + + let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS; + if (os == "WINNT") { + aEditor.saveToFile("C:\\I_DO_NOT_EXIST_42\\bogus.css", onSaveCallback); + } else { + aEditor.saveToFile("/I_DO_NOT_EXIST_42/bogos.css", onSaveCallback); + } +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js b/browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js new file mode 100644 index 00000000000..c41feb6a9b7 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js @@ -0,0 +1,101 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; + + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onContentAttach: run + }); + if (aChrome.isContentAttached) { + run(aChrome); + } + }); + + content.location = TESTCASE_URI; +} + +function getFilteredItemsCount(nav) +{ + let matches = nav.querySelectorAll("*.splitview-filtered"); + return matches ? matches.length : 0; +} + +function run(aChrome) +{ + aChrome.editors[0].addActionListener({onAttach: onFirstEditorAttach}); + aChrome.editors[1].addActionListener({onAttach: onSecondEditorAttach}); +} + +function onFirstEditorAttach(aEditor) +{ + let filter = gChromeWindow.document.querySelector(".splitview-filter"); + // force the command event on input since it is not possible to disable + // the search textbox's timeout. + let forceCommandEvent = function forceCommandEvent() { + let evt = gChromeWindow.document.createEvent("XULCommandEvent"); + evt.initCommandEvent("command", true, true, gChromeWindow, 0, false, false, + false, false, null); + filter.dispatchEvent(evt); + } + filter.addEventListener("input", forceCommandEvent, false); + + let nav = gChromeWindow.document.querySelector(".splitview-nav"); + nav.focus(); + + is(getFilteredItemsCount(nav), 0, + "there is 0 filtered item initially"); + + waitForFocus(function () { + // Search [s] (type-on-search since we focused nav above - not filter directly) + EventUtils.synthesizeKey("s", {}, gChromeWindow); + + // the search space is "simple.css" and "inline stylesheet #1" (2 sheets) + is(getFilteredItemsCount(nav), 0, + "there is 0 filtered item if searching for 's'"); + + EventUtils.synthesizeKey("i", {}, gChromeWindow); // Search [si] + + is(getFilteredItemsCount(nav), 1, // inline stylesheet is filtered + "there is 1 filtered item if searching for 's'"); + + // use uppercase to check that filtering is case-insensitive + EventUtils.synthesizeKey("X", {}, gChromeWindow); // Search [siX] + is(getFilteredItemsCount(nav), 2, + "there is 2 filtered items if searching for 's'"); // no match + + // clear the search + EventUtils.synthesizeKey("VK_ESCAPE", {}, gChromeWindow); + + is(filter.value, "", + "filter is back to empty"); + is(getFilteredItemsCount(nav), 0, + "there is 0 filtered item when filter is empty again"); + + for each (let c in "inline") { + EventUtils.synthesizeKey(c, {}, gChromeWindow); + } + + is(getFilteredItemsCount(nav), 1, // simple.css is filtered + "there is 1 filtered item if searching for 'inline'"); + + // auto-select the only result (enter the editor) + EventUtils.synthesizeKey("VK_ENTER", {}, gChromeWindow); + + filter.removeEventListener("input", forceCommandEvent, false); + }, gChromeWindow); +} + +function onSecondEditorAttach(aEditor) +{ + ok(aEditor.sourceEditor.hasFocus(), + "second editor has been selected and focused automatically."); + + finish(); +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js b/browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js new file mode 100644 index 00000000000..e734f7d0237 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js @@ -0,0 +1,80 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "four.html"; + + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + aChrome.addChromeListener({ + onContentAttach: run + }); + if (aChrome.isContentAttached) { + run(aChrome); + } + }); + + content.location = TESTCASE_URI; +} + +let gChrome; + +function run(aChrome) +{ + gChrome = aChrome; + aChrome.editors[0].addActionListener({onAttach: onEditor0Attach}); + aChrome.editors[2].addActionListener({onAttach: onEditor2Attach}); +} + +function getStylesheetNameLinkFor(aEditor) +{ + return gChrome.getSummaryElementForEditor(aEditor).querySelector(".stylesheet-name"); +} + +function onEditor0Attach(aEditor) +{ + waitForFocus(function () { + let summary = gChrome.getSummaryElementForEditor(aEditor); + EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow); + + let item = getStylesheetNameLinkFor(gChrome.editors[0]); + is(gChromeWindow.document.activeElement, item, + "editor 0 item is the active element"); + + EventUtils.synthesizeKey("VK_DOWN", {}, gChromeWindow); + item = getStylesheetNameLinkFor(gChrome.editors[1]); + is(gChromeWindow.document.activeElement, item, + "editor 1 item is the active element"); + + EventUtils.synthesizeKey("VK_HOME", {}, gChromeWindow); + item = getStylesheetNameLinkFor(gChrome.editors[0]); + is(gChromeWindow.document.activeElement, item, + "fist editor item is the active element"); + + EventUtils.synthesizeKey("VK_END", {}, gChromeWindow); + item = getStylesheetNameLinkFor(gChrome.editors[3]); + is(gChromeWindow.document.activeElement, item, + "last editor item is the active element"); + + EventUtils.synthesizeKey("VK_UP", {}, gChromeWindow); + item = getStylesheetNameLinkFor(gChrome.editors[2]); + is(gChromeWindow.document.activeElement, item, + "editor 2 item is the active element"); + + EventUtils.synthesizeKey("VK_RETURN", {}, gChromeWindow); + // this will attach and give focus editor 2 + }, gChromeWindow); +} + +function onEditor2Attach(aEditor) +{ + ok(aEditor.sourceEditor.hasFocus(), + "editor 2 has focus"); + + gChrome = null; + finish(); +} diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js b/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js new file mode 100644 index 00000000000..4c2766363b7 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js @@ -0,0 +1,63 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; + +let gOriginalWidth; // these are set by run() when gChromeWindow is ready +let gOriginalHeight; + +function test() +{ + waitForExplicitFinish(); + + addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { + if (aChrome.isContentAttached) { + run(aChrome); + } else { + aChrome.addChromeListener({ + onContentAttach: run + }); + } + }); + + content.location = TESTCASE_URI; +} + +function run(aChrome) +{ + is(aChrome.editors.length, 2, + "there is 2 stylesheets initially"); + + aChrome.editors[0].addActionListener({ + onAttach: function onEditorAttached(aEditor) { + let originalSourceEditor = aEditor.sourceEditor; + aEditor.sourceEditor.setCaretOffset(4); // to check the caret is preserved + + // queue a resize to inverse aspect ratio + // this will trigger a detach and reattach (to workaround bug 254144) + executeSoon(function () { + waitForFocus(function () { + gOriginalWidth = gChromeWindow.outerWidth; + gOriginalHeight = gChromeWindow.outerHeight; + gChromeWindow.resizeTo(120, 480); + + executeSoon(function () { + is(aEditor.sourceEditor, originalSourceEditor, + "the editor still references the same SourceEditor instance"); + is(aEditor.sourceEditor.getCaretOffset(), 4, + "the caret position has been preserved"); + + // queue a resize to original aspect ratio + waitForFocus(function () { + gChromeWindow.resizeTo(gOriginalWidth, gOriginalHeight); + executeSoon(function () { + finish(); + }); + }, gChromeWindow); + }); + }, gChromeWindow); + }); + } + }); +} diff --git a/browser/devtools/styleeditor/test/four.html b/browser/devtools/styleeditor/test/four.html new file mode 100644 index 00000000000..c0d51d691c5 --- /dev/null +++ b/browser/devtools/styleeditor/test/four.html @@ -0,0 +1,25 @@ + + + + four stylesheets + + + + + + +
four stylesheets
+ + diff --git a/browser/devtools/styleeditor/test/head.js b/browser/devtools/styleeditor/test/head.js new file mode 100644 index 00000000000..f1bfbfbacd9 --- /dev/null +++ b/browser/devtools/styleeditor/test/head.js @@ -0,0 +1,46 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/styleeditor/test/"; +const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleeditor/test/"; +const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleeditor/test/"; + +let gChromeWindow; //StyleEditorChrome window + +function cleanup() +{ + if (gChromeWindow) { + gChromeWindow.close(); + gChromeWindow = null; + } + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +} + +function launchStyleEditorChrome(aCallback) +{ + gChromeWindow = StyleEditor.openChrome(); + if (gChromeWindow.document.readyState != "complete") { + gChromeWindow.addEventListener("load", function onChromeLoad() { + gChromeWindow.removeEventListener("load", onChromeLoad, true); + gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true; + aCallback(gChromeWindow.styleEditorChrome); + }, true); + } else { + gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true; + aCallback(gChromeWindow.styleEditorChrome); + } +} + +function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback) +{ + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function onLoad() { + gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); + launchStyleEditorChrome(aCallback); + }, true); +} + +registerCleanupFunction(cleanup); diff --git a/browser/devtools/styleeditor/test/media-small.css b/browser/devtools/styleeditor/test/media-small.css new file mode 100644 index 00000000000..d64756ddc7e --- /dev/null +++ b/browser/devtools/styleeditor/test/media-small.css @@ -0,0 +1,5 @@ +/* this stylesheet applies when min-width<400px */ +body { + background: red; +} + diff --git a/browser/devtools/styleeditor/test/media.html b/browser/devtools/styleeditor/test/media.html new file mode 100644 index 00000000000..ef05818c589 --- /dev/null +++ b/browser/devtools/styleeditor/test/media.html @@ -0,0 +1,11 @@ + + + + + + + +
test for media labels
+ + + diff --git a/browser/devtools/styleeditor/test/minified.html b/browser/devtools/styleeditor/test/minified.html new file mode 100644 index 00000000000..a7d3eec3267 --- /dev/null +++ b/browser/devtools/styleeditor/test/minified.html @@ -0,0 +1,17 @@ + + + + minified testcase + + + + +
minified testcase
+ + diff --git a/browser/devtools/styleeditor/test/simple.css b/browser/devtools/styleeditor/test/simple.css new file mode 100644 index 00000000000..eb22505d3ae --- /dev/null +++ b/browser/devtools/styleeditor/test/simple.css @@ -0,0 +1,8 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +body { + margin: 0; +} + diff --git a/browser/devtools/styleeditor/test/simple.css.gz b/browser/devtools/styleeditor/test/simple.css.gz new file mode 100644 index 00000000000..ee3b9efbc1d Binary files /dev/null and b/browser/devtools/styleeditor/test/simple.css.gz differ diff --git a/browser/devtools/styleeditor/test/simple.css.gz^headers^ b/browser/devtools/styleeditor/test/simple.css.gz^headers^ new file mode 100644 index 00000000000..092020ab00d --- /dev/null +++ b/browser/devtools/styleeditor/test/simple.css.gz^headers^ @@ -0,0 +1,4 @@ +Vary: Accept-Encoding +Content-Encoding: gzip +Content-Type: text/css + diff --git a/browser/devtools/styleeditor/test/simple.gz.html b/browser/devtools/styleeditor/test/simple.gz.html new file mode 100644 index 00000000000..d63362b8e00 --- /dev/null +++ b/browser/devtools/styleeditor/test/simple.gz.html @@ -0,0 +1,23 @@ + + + + simple testcase + + + + +
simple testcase
+ + diff --git a/browser/devtools/styleeditor/test/simple.html b/browser/devtools/styleeditor/test/simple.html new file mode 100644 index 00000000000..d586cf4d101 --- /dev/null +++ b/browser/devtools/styleeditor/test/simple.html @@ -0,0 +1,23 @@ + + + + simple testcase + + + + +
simple testcase
+ +