src/extensions/default/InlineColorEditor/unittests.js
/*
* Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/*global describe, it, expect, beforeEach, afterEach, waits, runs, waitsForDone, spyOn */
define(function (require, exports, module) {
"use strict";
// Modules from the SpecRunner window
var KeyEvent = brackets.getModule("utils/KeyEvent"),
PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils"),
testContentCSS = require("text!unittest-files/unittests.css"),
testContentHTML = require("text!unittest-files/unittests.html"),
provider = require("main").inlineColorEditorProvider,
InlineColorEditor = require("InlineColorEditor").InlineColorEditor,
ColorEditor = require("ColorEditor").ColorEditor,
tinycolor = require("thirdparty/tinycolor-min");
// Helper functions for testing cursor position / selection range
function fixPos(pos) {
if (!("sticky" in pos)) {
pos.sticky = null;
}
return pos;
}
function fixSel(sel) {
fixPos(sel.start);
fixPos(sel.end);
if (!("reversed" in sel)) {
sel.reversed = false;
}
return sel;
}
function fixSels(sels) {
sels.forEach(function (sel) {
fixSel(sel);
});
return sels;
}
describe("Inline Color Editor - unit", function () {
var testDocument, testEditor, inline;
/**
* Creates an inline color editor connected to the given cursor position in the test editor.
* Note that this does *not* actually open it as an inline editor in the test editor.
* Tests that use this must wrap their contents in a runs() block.
* @param {!{line:number, ch: number}} cursor Position for which to open the inline editor.
* if the provider did not create an inline editor.
*/
function makeColorEditor(cursor) {
runs(function () {
var promise = provider(testEditor, cursor);
if (promise) {
promise.done(function (inlineResult) {
inlineResult.onAdded();
inline = inlineResult;
});
waitsForDone(promise, "open color editor");
}
});
}
/**
* Expects an inline editor to be opened at the given cursor position and to have the
* given initial color (which should match the color at that position).
* @param {!{line:number, ch:number}} cursor The cursor position to try opening the inline at.
* @param {string} color The expected color.
*/
function testOpenColor(cursor, color) {
makeColorEditor(cursor);
runs(function () {
expect(inline).toBeTruthy();
expect(inline._color).toBe(color);
});
}
/**
* Simulate the given event with clientX/clientY specified by the given
* ratios of the item's actual width/height (offset by the left/top of the
* item).
* @param {string} event The name of the event to simulate.
* @param {object} $item A jQuery object to trigger the event on.
* @param {Array.<number>} ratios Numbers between 0 and 1 indicating the x and y positions of the
* event relative to the item's width and height.
*/
function eventAtRatio(event, $item, ratios) {
$item.trigger($.Event(event, {
clientX: $item.offset().left + (ratios[0] * $item.width()),
clientY: $item.offset().top + (ratios[1] * $item.height())
}));
}
describe("Inline editor - CSS", function () {
beforeEach(function () {
var mock = SpecRunnerUtils.createMockEditor(testContentCSS, "css");
testDocument = mock.doc;
testEditor = mock.editor;
});
afterEach(function () {
SpecRunnerUtils.destroyMockEditor(testDocument);
testEditor = null;
testDocument = null;
inline = null;
});
describe("simple open cases", function () {
it("should show the correct color when opened on an #rrggbb color", function () {
testOpenColor({line: 1, ch: 18}, "#abcdef");
});
it("should open when at the beginning of the color", function () {
testOpenColor({line: 1, ch: 16}, "#abcdef");
});
it("should open when at the end of the color", function () {
testOpenColor({line: 1, ch: 23}, "#abcdef");
});
it("should show the correct color when opened on an #rgb color", function () {
testOpenColor({line: 5, ch: 18}, "#abc");
});
it("should show the correct color when opened on an rgb() color", function () {
testOpenColor({line: 9, ch: 18}, "rgb(100, 200, 150)");
});
it("should show the correct color when opened on an rgba() color", function () {
testOpenColor({line: 13, ch: 18}, "rgba(100, 200, 150, 0.5)");
});
it("should show the correct color when opened on an hsl() color", function () {
testOpenColor({line: 17, ch: 18}, "hsl(180, 50%, 50%)");
});
it("should show the correct color when opened on an hsla() color", function () {
testOpenColor({line: 21, ch: 18}, "hsla(180, 50%, 50%, 0.5)");
});
it("should show the correct color when opened on an uppercase hex color", function () {
testOpenColor({line: 33, ch: 18}, "#DEFCBA");
});
it("should show the correct color when opened on a color in a shorthand property", function () {
testOpenColor({line: 41, ch: 27}, "#0f0f0f");
});
it("should show the correct color when opened on an rgba() color with a leading period in the alpha field", function () {
testOpenColor({line: 45, ch: 18}, "rgba(100, 200, 150, .5)");
});
it("should show the correct color when opened on an hsla() color with a leading period in the alpha field", function () {
testOpenColor({line: 49, ch: 18}, "hsla(180, 50%, 50%, .5)");
});
it("should not open when not on a color", function () {
makeColorEditor({line: 1, ch: 6});
runs(function () {
expect(inline).toEqual(null);
});
});
it("should not open when on an invalid color", function () {
makeColorEditor({line: 25, ch: 18});
runs(function () {
expect(inline).toEqual(null);
});
});
it("should not open when on an hsl color with missing percent signs", function () {
makeColorEditor({line: 37, ch: 18});
runs(function (inline) {
expect(inline).toEqual(null);
});
});
it("should open on the second color when there are two colors in the same line", function () {
testOpenColor({line: 29, ch: 48}, "#ddeeff");
});
it("should properly add/remove ref to document when opened/closed", function () {
runs(function () {
spyOn(testDocument, "addRef").andCallThrough();
spyOn(testDocument, "releaseRef").andCallThrough();
});
makeColorEditor({line: 1, ch: 18});
runs(function () {
expect(testDocument.addRef).toHaveBeenCalled();
expect(testDocument.addRef.callCount).toBe(1);
inline.onClosed();
expect(testDocument.releaseRef).toHaveBeenCalled();
expect(testDocument.releaseRef.callCount).toBe(1);
});
});
});
describe("update host document on edit in color editor", function () {
it("should update host document when change is committed in color editor", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
inline.colorEditor.setColorFromString("#c0c0c0");
expect(testDocument.getRange({line: 1, ch: 16}, {line: 1, ch: 23})).toBe("#c0c0c0");
});
});
it("should update correct range of host document with color format of different length", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
inline.colorEditor.setColorFromString("rgb(20, 20, 20)");
expect(testDocument.getRange({line: 1, ch: 16}, {line: 1, ch: 31})).toBe("rgb(20, 20, 20)");
});
});
it("should not invalidate range when change is committed", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
inline.colorEditor.setColorFromString("rgb(20, 20, 20)");
expect(inline.getCurrentRange()).toBeTruthy();
});
});
it("should update correct range of host document when the in-editor color string is invalid", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 24});
inline.colorEditor.setColorFromString("#c0c0c0");
expect(fixSel(inline.getCurrentRange())).toEqual(fixSel({start: {line: 1, ch: 16}, end: {line: 1, ch: 23}}));
expect(testDocument.getRange({line: 1, ch: 16}, {line: 1, ch: 23})).toBe("#c0c0c0");
});
});
});
describe("update color editor on edit in host editor", function () {
it("should update when edit is made to color range in host editor", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
spyOn(inline, "close");
testDocument.replaceRange("0", {line: 1, ch: 18}, {line: 1, ch: 19});
expect(inline._color).toBe("#a0cdef");
// TODO (#2201): this assumes getColor() is a tinycolor, but sometimes it's a string
expect(inline.colorEditor.getColor().toHexString().toLowerCase()).toBe("#a0cdef");
expect(inline.close).not.toHaveBeenCalled();
expect(fixSel(inline.getCurrentRange())).toEqual(fixSel({start: {line: 1, ch: 16}, end: {line: 1, ch: 23}}));
});
});
it("should close itself if edit is made that destroys end textmark and leaves color invalid", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
spyOn(inline, "close");
// Replace everything including the semicolon, so it crosses the textmark boundary.
testDocument.replaceRange("rgb(255, 25", {line: 1, ch: 16}, {line: 1, ch: 24});
expect(inline.close).toHaveBeenCalled();
});
});
it("should maintain the range if the user deletes the last character of the color and types a new one", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
spyOn(inline, "close");
testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 23});
testDocument.replaceRange("0", {line: 1, ch: 22}, {line: 1, ch: 22});
expect(inline._color).toBe("#abcde0");
expect(inline.close).not.toHaveBeenCalled();
expect(fixSel(inline.getCurrentRange())).toEqual(fixSel({start: {line: 1, ch: 16}, end: {line: 1, ch: 23}}));
});
});
it("should not update the end textmark and the color shown to a shorter valid match if the marker still exists and the color becomes invalid", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 23});
expect(inline._color).toBe("#abcdef");
expect(fixSel(inline.getCurrentRange())).toEqual(fixSel({start: {line: 1, ch: 16}, end: {line: 1, ch: 22}}));
});
});
it("should not update the end textmark and the color shown to a shorter valid match if the marker no longer exists and the color becomes invalid", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
testDocument.replaceRange("", {line: 1, ch: 22}, {line: 1, ch: 24});
expect(inline._color).toBe("#abcdef");
expect(fixSel(inline.getCurrentRange())).toEqual(fixSel({start: {line: 1, ch: 16}, end: {line: 1, ch: 22}}));
});
});
});
describe("edit batching", function () {
it("should combine multiple edits within the same inline editor into a single undo in the host editor", function () {
makeColorEditor({line: 1, ch: 18});
runs(function () {
inline.colorEditor.setColorFromString("#010101");
inline.colorEditor.setColorFromString("#123456");
inline.colorEditor.setColorFromString("#bdafe0");
testDocument._masterEditor._codeMirror.undo();
expect(testDocument.getRange({line: 1, ch: 16}, {line: 1, ch: 23})).toBe("#abcdef");
});
});
});
});
describe("Inline editor - HTML", function () {
beforeEach(function () {
var mock = SpecRunnerUtils.createMockEditor(testContentHTML, "html");
testDocument = mock.doc;
testEditor = mock.editor;
});
afterEach(function () {
SpecRunnerUtils.destroyMockEditor(testDocument);
testEditor = null;
testDocument = null;
});
it("should open on a color in an HTML file", function () {
testOpenColor({line: 4, ch: 30}, "#dead01");
});
});
describe("Inline editor - used colors processing", function () {
it("should trim the original array to the given length", function () {
var inline = new InlineColorEditor();
var result = inline._collateColors(["#abcdef", "#fedcba", "#aabbcc", "#bbccdd"], 2);
expect(result).toEqual([
{value: "#abcdef", count: 1},
{value: "#fedcba", count: 1}
]);
});
it("should remove duplicates from the original array and sort it by usage", function () {
var inline = new InlineColorEditor();
var result = inline._collateColors(["#abcdef", "#fedcba", "#123456", "#FEDCBA", "#123456", "#123456", "rgb(100, 100, 100)"], 100);
expect(result).toEqual([
{value: "#123456", count: 3},
{value: "#fedcba", count: 2},
{value: "#abcdef", count: 1},
{value: "rgb(100, 100, 100)", count: 1}
]);
});
});
describe("Color editor UI", function () {
var colorEditor,
defaultSwatches = [{value: "#abcdef", count: 3}, {value: "rgba(100, 200, 250, 0.5)", count: 2}];
/**
* Creates a hidden ColorEditor and appends it to the body. Note that this is a standalone
* ColorEditor, not inside an InlineColorEditor.
* @param {string} initialColor The color that should be initially set in the ColorEditor.
* @param {?function} callback An optional callback to be passed as the ColorEditor's callback. If
* none is supplied, a dummy function is passed.
* @param {?Array.<{value:string, count:number}>} swatches An optional array of swatches to display.
* If none is supplied, a default set of two swatches is passed.
* @param {boolean=} hide Whether to hide the color picker; default is true.
*/
function makeUI(initialColor, callback, swatches, hide) {
colorEditor = new ColorEditor($(window.document.body),
initialColor,
callback || function () { },
swatches || defaultSwatches);
if (hide !== false) {
colorEditor.getRootElement().css("display", "none");
}
}
afterEach(function () {
colorEditor.getRootElement().remove();
});
/**
* Checks whether the difference between val1 and val2 is within the given tolerance.
* (We can't use Jasmine's .toBeCloseTo() because that takes a precision in decimal places,
* whereas we often need to check an absolute distance.)
* @param {(number|string)} val1 The first value to check.
* @param {(number|string)} val2 The second value to check.
* @param {number} tolerance The desired tolerance.
*/
function checkNear(val1, val2, tolerance) {
expect(Math.abs(Number(val1) - Number(val2)) < (tolerance || 1.0)).toBe(true);
}
/**
* Checks whether the given percentage string is near the given value.
* @param {string} pct The percentage to check. Assumed to be a string ending in "%".
* @param {number} val The value to check against. Assumed to be a percentage number, but not ending in "%".
*/
function checkPercentageNear(pct, val) {
expect(checkNear(pct.substr(0, pct.length - 1), val));
}
/** Returns the colorEditor's current value as a string in its current format */
function getColorString() {
return tinycolor(colorEditor.getColor()).getOriginalInput();
}
describe("simple load/commit", function () {
it("should load the initial color correctly", function () {
var colorStr = "rgba(77, 122, 31, 0.5)";
var colorStrRgb = "rgb(77, 122, 31)";
runs(function () {
makeUI(colorStr);
expect(colorEditor.getColor().getOriginalInput()).toBe(colorStr);
expect(colorEditor.$colorValue.val()).toBe(colorStr);
expect(tinycolor.equals(colorEditor.$currentColor.css("background-color"), colorStr)).toBe(true);
// Not sure why the tolerances need to be larger for these.
checkNear(tinycolor(colorEditor.$selection.css("background-color")).toHsv().h, 90, 2.0);
checkNear(tinycolor(colorEditor.$hueBase.css("background-color")).toHsv().h, 90, 2.0);
expect(tinycolor.equals(colorEditor.$selectionBase.css("background-color"), colorStrRgb)).toBe(true);
});
// Need to do these on a timeout since we can't seem to read back CSS positions synchronously.
waits(1);
runs(function () {
checkPercentageNear(colorEditor.$hueSelector[0].style.bottom, 25);
checkPercentageNear(colorEditor.$opacitySelector[0].style.bottom, 50);
checkPercentageNear(colorEditor.$selectionBase[0].style.left, 74);
checkPercentageNear(colorEditor.$selectionBase[0].style.bottom, 47);
});
});
it("should load a committed color correctly", function () {
var colorStr = "rgba(77, 122, 31, 0.5)";
var colorStrRgb = "rgb(77, 122, 31)";
runs(function () {
makeUI("#0a0a0a");
colorEditor.setColorFromString(colorStr);
expect(colorEditor.getColor().getOriginalInput()).toBe(colorStr);
expect(colorEditor.$colorValue.val()).toBe(colorStr);
expect(tinycolor.equals(colorEditor.$currentColor.css("background-color"), colorStr)).toBe(true);
checkNear(tinycolor(colorEditor.$selection.css("background-color")).toHsv().h, tinycolor(colorStr).toHsv().h);
checkNear(tinycolor(colorEditor.$hueBase.css("background-color")).toHsv().h, tinycolor(colorStr).toHsv().h);
expect(tinycolor.equals(colorEditor.$selectionBase.css("background-color"), colorStrRgb)).toBe(true);
});
// Need to do these on a timeout since we can't seem to read back CSS positions synchronously.
waits(1);
runs(function () {
checkPercentageNear(colorEditor.$hueSelector[0].style.bottom, 25);
checkPercentageNear(colorEditor.$opacitySelector[0].style.bottom, 50);
checkPercentageNear(colorEditor.$selectionBase[0].style.left, 74);
checkPercentageNear(colorEditor.$selectionBase[0].style.bottom, 47);
});
});
it("should call the callback when a new color is committed", function () {
var lastColor;
makeUI("rgba(100, 100, 100, 0.5)", function (color) {
lastColor = color;
});
colorEditor.setColorFromString("#a0a0a0");
expect(lastColor).toBe("#a0a0a0");
});
});
/**
* Test whether converting the given color to the given mode results in the expected color.
* @param {string} initialColor The color to convert.
* @param {string} mode The mode to convert to: must be "rgba", "hsla", or "hex".
* @param {string} result The expected result of the conversion.
*/
function testConvert(initialColor, mode, result) {
makeUI(initialColor);
var buttonMap = {
"rgba": "$rgbaButton",
"hsla": "$hslButton",
"hex": "$hexButton"
};
colorEditor[buttonMap[mode]].trigger("click");
expect(colorEditor.getColor().getOriginalInput()).toBe(result);
}
describe("conversions in lower case", function () {
it("should convert a hex color to rgb when mode button clicked", function () {
testConvert("#112233", "rgba", "rgb(17, 34, 51)");
});
it("should convert a hex color to hsl when mode button clicked", function () {
testConvert("#112233", "hsla", "hsl(210, 50%, 13%)");
});
it("should convert an rgb color to hex when mode button clicked", function () {
testConvert("rgb(15, 160, 21)", "hex", "#0fa015");
});
it("should convert an rgba color to hex (dropping alpha) when mode button clicked", function () {
testConvert("rgba(15, 160, 21, 0.5)", "hex", "#0fa015");
});
it("should convert an rgb color to hsl when mode button clicked", function () {
testConvert("rgb(15, 160, 21)", "hsla", "hsl(122, 83%, 34%)");
});
it("should convert an rgba color to hsla when mode button clicked", function () {
testConvert("rgba(15, 160, 21, 0.3)", "hsla", "hsla(122, 83%, 34%, 0.3)");
});
it("should convert an hsl color to hex when mode button clicked", function () {
testConvert("hsl(152, 12%, 22%)", "hex", "#313f39");
});
it("should convert an hsla color to hex (dropping alpha) when mode button clicked", function () {
testConvert("hsla(152, 12%, 22%, 0.7)", "hex", "#313f39");
});
it("should convert an hsl color to rgb when mode button clicked", function () {
testConvert("hsl(152, 12%, 22%)", "rgba", "rgb(49, 63, 57)");
});
it("should convert an hsla color to rgba when mode button clicked", function () {
testConvert("hsla(152, 12%, 22%, 0.7)", "rgba", "rgba(49, 63, 57, 0.7)");
});
it("should convert a mixed case hsla color to rgba when mode button clicked", function () {
testConvert("HsLa(152, 12%, 22%, 0.7)", "rgba", "rgba(49, 63, 57, 0.7)");
});
it("should convert a mixed case hex color to rgb when mode button clicked", function () {
testConvert("#fFfFfF", "rgba", "rgb(255, 255, 255)");
});
});
describe("conversions in UPPER CASE", function () {
beforeEach(function () {
// Enable uppercase colors
PreferencesManager.set("uppercaseColors", true);
});
afterEach(function () {
// Re-disable uppercase colors
PreferencesManager.set("uppercaseColors", false);
});
it("should use uppercase colors", function () {
expect(PreferencesManager.get("uppercaseColors")).toBe(true);
});
it("should convert a hex color to rgb in uppercase when mode button clicked", function () {
testConvert("#112233", "rgba", "RGB(17, 34, 51)");
});
it("should convert a hex color to hsl in uppercase when mode button clicked", function () {
testConvert("#112233", "hsla", "HSL(210, 50%, 13%)");
});
it("should convert an rgb color to hex in uppercase when mode button clicked", function () {
testConvert("RGB(15, 160, 21)", "hex", "#0FA015");
});
it("should convert an rgba color to hex (dropping alpha) in uppercase when mode button clicked", function () {
testConvert("RGBA(15, 160, 21, 0.5)", "hex", "#0FA015");
});
it("should convert an rgb color to hsl in uppercase when mode button clicked", function () {
testConvert("RGB(15, 160, 21)", "hsla", "HSL(122, 83%, 34%)");
});
it("should convert an rgba color to hsla in uppercase when mode button clicked", function () {
testConvert("RGBA(15, 160, 21, 0.3)", "hsla", "HSLA(122, 83%, 34%, 0.3)");
});
it("should convert an hsl color to hex in uppercase when mode button clicked", function () {
testConvert("HSL(152, 12%, 22%)", "hex", "#313F39");
});
it("should convert an hsla color to hex (dropping alpha) in uppercase when mode button clicked", function () {
testConvert("HSLA(152, 12%, 22%, 0.7)", "hex", "#313F39");
});
it("should convert an hsl color to rgb in uppercase when mode button clicked", function () {
testConvert("HSL(152, 12%, 22%)", "rgba", "RGB(49, 63, 57)");
});
it("should convert an hsla color to rgba in uppercase when mode button clicked", function () {
testConvert("HSLA(152, 12%, 22%, 0.7)", "rgba", "RGBA(49, 63, 57, 0.7)");
});
it("should convert a mixed case hsla color to rgba in uppercase when mode button clicked", function () {
testConvert("HsLa(152, 12%, 22%, 0.7)", "rgba", "RGBA(49, 63, 57, 0.7)");
});
it("should convert a mixed case hex color to rgb in uppercase when mode button clicked", function () {
testConvert("#fFfFfF", "rgba", "RGB(255, 255, 255)");
});
});
describe("parameter editing with mouse", function () {
/**
* Test a mouse down event on the given UI element.
* @param {object} opts The parameters to test:
* item: The (string) name of the member of ColorEditor that references the element to test.
* clickAt: An [x, y] array specifying the simulated x/y mouse position as a fraction of the
* item's width/height. For example, [0.5, 0.5] would specify a click exactly in the
* center of the element.
* param: The (string) parameter whose value we're testing (h, s, v, or a).
* expected: The expected value for the parameter.
* tolerance: The tolerance in variation for the expected value.
*/
function testMousedown(opts) {
makeUI("#0000ff");
eventAtRatio("mousedown", colorEditor[opts.item], opts.clickAt);
checkNear(tinycolor(colorEditor.getColor()).toHsv()[opts.param], opts.expected, opts.tolerance);
colorEditor[opts.item].trigger("mouseup"); // clean up drag state
}
/**
* Test a drag event on the given UI element.
* @param {object} opts The parameters to test:
* item: The (string) name of the member of ColorEditor that references the element to test.
* clickAt: An [x, y] array specifying the simulated x/y mouse position for the initial mouse down
* as a fraction of the item's width/height. For example, [0.5, 0.5] would specify a click
* exactly in the center of the element.
* dragTo: An [x, y] array specifying the location to drag to, using the same convention as clickAt.
* param: The (string) parameter whose value we're testing (h, s, v, or a).
* expected: The expected value for the parameter.
* tolerance: The tolerance in variation for the expected value.
*/
function testDrag(opts) {
makeUI("#0000ff");
eventAtRatio("mousedown", colorEditor[opts.item], opts.clickAt);
eventAtRatio("mousemove", colorEditor[opts.item], opts.dragTo);
checkNear(tinycolor(colorEditor.getColor()).toHsv()[opts.param], opts.expected, opts.tolerance);
colorEditor[opts.item].trigger("mouseup"); // clean up drag state
}
it("should set saturation on mousedown", function () {
testMousedown({
item: "$selection",
clickAt: [0.25, 0], // x: saturation, y: 1.0 - value
param: "s",
expected: 0.25,
tolerance: 0.1
});
});
it("should set saturation on drag", function () {
testDrag({
item: "$selection",
clickAt: [0.25, 0], // x: saturation, y: 1.0 - value
dragTo: [0.75, 0],
param: "s",
expected: 0.75,
tolerance: 0.1
});
});
it("should clip saturation to min value", function () {
testDrag({
item: "$selection",
clickAt: [0.25, 0], // x: saturation, y: 1.0 - value
dragTo: [-0.25, 0],
param: "s",
expected: 0,
tolerance: 0.1
});
});
it("should clip saturation to max value", function () {
testDrag({
item: "$selection",
clickAt: [0.25, 0], // x: saturation, y: 1.0 - value
dragTo: [1.25, 0],
param: "s",
expected: 1,
tolerance: 0.1
});
});
it("should set value on mousedown", function () {
testMousedown({
item: "$selection",
clickAt: [1.0, 0.75], // x: saturation, y: 1.0 - value
param: "v",
expected: 0.25,
tolerance: 0.1
});
});
it("should set value on drag", function () {
testDrag({
item: "$selection",
clickAt: [1.0, 0.75], // x: saturation, y: 1.0 - value
dragTo: [1.0, 0.25],
param: "v",
expected: 0.75,
tolerance: 0.1
});
});
it("should clip value to min value", function () {
testDrag({
item: "$selection",
clickAt: [1.0, 0.75], // x: saturation, y: 1.0 - value
dragTo: [1.0, 1.25],
param: "v",
expected: 0,
tolerance: 0.1
});
});
it("should clip value to max value", function () {
testDrag({
item: "$selection",
clickAt: [1.0, 0.75],
dragTo: [1.0, -0.25],
param: "v",
expected: 1,
tolerance: 0.1
});
});
it("should set hue on mousedown", function () {
testMousedown({
item: "$hueSlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - (hue / 360)
param: "h",
expected: 90,
tolerance: 1
});
});
it("should set hue on drag", function () {
testDrag({
item: "$hueSlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - (hue / 360)
dragTo: [0, 0.25],
param: "h",
expected: 270,
tolerance: 1
});
});
it("should clip hue to min value", function () {
testDrag({
item: "$hueSlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - (hue / 360)
dragTo: [0, 1.25],
param: "h",
expected: 0,
tolerance: 1
});
});
it("should clip hue to max value", function () {
testDrag({
item: "$hueSlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - (hue / 360)
dragTo: [0, -0.25],
param: "h",
expected: 0,
tolerance: 1
});
});
it("should set opacity on mousedown", function () {
testMousedown({
item: "$opacitySlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - opacity
param: "a",
expected: 0.25,
tolerance: 0.1
});
});
it("should set opacity on drag", function () {
testDrag({
item: "$opacitySlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - opacity
dragTo: [0, 0.25],
param: "a",
expected: 0.75,
tolerance: 0.1
});
});
it("should clip opacity to min value", function () {
testDrag({
item: "$opacitySlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - opacity
dragTo: [0, 1.25],
param: "a",
expected: 0,
tolerance: 0.1
});
});
it("should clip opacity to max value", function () {
// A increases going up, so a clientY at -0.25 of the item's height corresponds to >100%.
testDrag({
item: "$opacitySlider",
clickAt: [0, 0.75], // x: unused, y: 1.0 - opacity
dragTo: [0, -0.25],
param: "a",
expected: 1,
tolerance: 0.1
});
});
});
describe("parameter editing with keyboard", function () {
function makeKeyEvent(opts) {
return $.Event("keydown", { keyCode: opts.key, shiftKey: !!opts.shift });
}
/**
* Test a key event on the given UI element.
* @param {object} opts The parameters to test:
* color: An optional initial value to set in the ColorEditor. Defaults to "hsla(50, 25%, 50%, 0.5)".
* item: The (string) name of the member of ColorEditor that references the element to test.
* key: The KeyEvent key code to simulate.
* shift: Optional boolean specifying whether to simulate the shift key being down (default false).
* param: The (string) parameter whose value we're testing (h, s, v, or a).
* delta: The expected change in value for the parameter.
* tolerance: The tolerance in variation for the expected value.
* exact: True to compare the actual values stored in the _hsv object, false (default) to
* compare tinycolor's normalization of the color value.
*/
function testKey(opts) {
function getParam() {
if (opts.exact) {
var result = colorEditor._hsv[opts.param];
// Because of #2201, this is sometimes a string with a percentage value.
if (typeof result === "string" && result.charAt(result.length - 1) === "%") {
result = Number(result.substr(0, result.length - 1));
}
return result;
} else {
return tinycolor(colorEditor.getColor()).toHsv()[opts.param];
}
}
makeUI(opts.color || "hsla(50, 25%, 50%, 0.5)");
var before = getParam();
colorEditor[opts.item].trigger(makeKeyEvent(opts));
var after = getParam();
checkNear(after, before + opts.delta, opts.tolerance);
}
/**
* Test whether the given event's default is or isn't prevented on a given key.
* @param {object} opts The parameters to test:
* color: An optional initial value to set in the ColorEditor. Defaults to "hsla(50, 25%, 50%, 0.5)".
* item: The (string) name of the member of ColorEditor that references the element to test.
* selection: An optional array ([start, end]) specifying the selection to set in the given element.
* key: The KeyEvent key code to simulate.
* shift: Optional boolean specifying whether to simulate the shift key being down (default false).
* expected: Whether the default is expected to be prevented.
*/
function testPreventDefault(opts) {
var event, $item;
// The color picker needs to be displayed for this test; otherwise the
// selection won't be properly set, because you can only set the selection
// when the text field has focus.
makeUI(opts.color || "hsla(50, 25%, 50%, 0.5)", function () { }, defaultSwatches, false);
$item = colorEditor[opts.item];
$item.focus();
if (opts.selection) {
$item[0].setSelectionRange(opts.selection[0], opts.selection[1]);
}
event = makeKeyEvent(opts);
$item.trigger(event);
expect(event.isDefaultPrevented()).toBe(opts.expected);
}
it("should increase saturation by 1.5% on right arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_RIGHT,
param: "s",
delta: 0.015,
tolerance: 0.01
});
});
it("should clip max saturation on right arrow", function () {
testKey({
color: "hsla(50, 100%, 50%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_RIGHT,
param: "s",
delta: 0,
tolerance: 0.01
});
});
it("should increase saturation by 7.5% on shift right arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_RIGHT,
shift: true,
param: "s",
delta: 0.075,
tolerance: 0.01
});
});
it("should clip max saturation on shift right arrow", function () {
testKey({
color: "hsla(50, 100%, 50%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_RIGHT,
shift: true,
param: "s",
delta: 0,
tolerance: 0.01
});
});
it("should decrease saturation by 1.5% on left arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_LEFT,
param: "s",
delta: -0.015,
tolerance: 0.01
});
});
it("should clip min saturation on left arrow", function () {
testKey({
color: "hsla(50, 0%, 50%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_LEFT,
param: "s",
delta: 0,
tolerance: 0.01
});
});
it("should decrease saturation by 7.5% on shift left arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_LEFT,
shift: true,
param: "s",
delta: -0.075,
tolerance: 0.01
});
});
it("should clip min saturation on shift left arrow", function () {
testKey({
color: "hsla(50, 0%, 50%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_LEFT,
shift: true,
param: "s",
delta: 0,
tolerance: 0.01
});
});
it("should increase value by 1.5% on up arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_UP,
param: "v",
delta: 0.015,
tolerance: 0.01
});
});
it("should clip max value on up arrow", function () {
testKey({
color: "hsla(50, 25%, 100%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_UP,
param: "v",
delta: 0,
tolerance: 0.01
});
});
it("should increase value by 7.5% on shift up arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "v",
delta: 0.075,
tolerance: 0.01
});
});
it("should clip max value on shift up arrow", function () {
testKey({
color: "hsla(50, 25%, 100%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "v",
delta: 0,
tolerance: 0.01
});
});
it("should decrease value by 1.5% on down arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_DOWN,
param: "v",
delta: -0.015,
tolerance: 0.01
});
});
it("should clip min value on down arrow", function () {
testKey({
color: "hsla(50, 25%, 0%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_DOWN,
param: "v",
delta: 0,
tolerance: 0.01
});
});
it("should decrease value by 7.5% on shift down arrow", function () {
testKey({
item: "$selectionBase",
key: KeyEvent.DOM_VK_DOWN,
shift: true,
param: "v",
delta: -0.075,
tolerance: 0.01
});
});
it("should clip min value on shift down arrow", function () {
testKey({
color: "hsla(50, 25%, 0%, 0.5)",
item: "$selectionBase",
key: KeyEvent.DOM_VK_DOWN,
shift: true,
param: "v",
delta: 0,
tolerance: 0.01
});
});
it("should increase hue by 3.6 on up arrow", function () {
testKey({
item: "$hueBase",
key: KeyEvent.DOM_VK_UP,
param: "h",
delta: 3.6,
tolerance: 1
});
});
it("should wrap around max hue on up arrow", function () {
testKey({
color: "hsla(359, 25%, 50%, 0.5)",
item: "$hueBase",
key: KeyEvent.DOM_VK_UP,
param: "h",
delta: -359 + 3.6,
tolerance: 1
});
});
it("should increase hue by 18 on shift up arrow", function () {
testKey({
item: "$hueBase",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "h",
delta: 18,
tolerance: 1
});
});
it("should wrap around max hue on shift up arrow", function () {
testKey({
color: "hsla(359, 25%, 50%, 0.5)",
item: "$hueBase",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "h",
delta: -359 + 18,
tolerance: 1
});
});
it("should decrease hue by 3.6 on down arrow", function () {
testKey({
item: "$hueBase",
key: KeyEvent.DOM_VK_DOWN,
param: "h",
delta: -3.6,
tolerance: 1
});
});
it("should wrap around min hue on down arrow", function () {
testKey({
color: "hsla(0, 25%, 50%, 0.5)",
item: "$hueBase",
key: KeyEvent.DOM_VK_DOWN,
param: "h",
delta: 360 - 3.6,
tolerance: 1
});
});
it("should decrease hue by 18 on shift down arrow", function () {
testKey({
item: "$hueBase",
key: KeyEvent.DOM_VK_DOWN,
shift: true,
param: "h",
delta: -18,
tolerance: 1
});
});
it("should wrap around min hue on shift down arrow", function () {
testKey({
color: "hsla(0, 25%, 50%, 0.5)",
item: "$hueBase",
key: KeyEvent.DOM_VK_DOWN,
shift: true,
param: "h",
delta: 360 - 18,
tolerance: 1
});
});
it("should increase opacity by 0.01 on up arrow", function () {
testKey({
item: "$opacitySelector",
key: KeyEvent.DOM_VK_UP,
param: "a",
delta: 0.01,
tolerance: 0.005
});
});
it("should clip max opacity on up arrow", function () {
testKey({
color: "hsla(90, 25%, 50%, 1.0)",
item: "$opacitySelector",
key: KeyEvent.DOM_VK_UP,
param: "a",
delta: 0,
tolerance: 0.005
});
});
it("should increase opacity by 0.05 on shift up arrow", function () {
testKey({
item: "$opacitySelector",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "a",
delta: 0.05,
tolerance: 0.005
});
});
it("should clip max opacity on shift up arrow", function () {
testKey({
color: "hsla(90, 25%, 50%, 1.0)",
item: "$opacitySelector",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "a",
delta: 0,
tolerance: 0.005
});
});
it("should decrease opacity by 0.01 on down arrow", function () {
testKey({
item: "$opacitySelector",
key: KeyEvent.DOM_VK_DOWN,
param: "a",
delta: -0.01,
tolerance: 0.005
});
});
it("should clip min opacity on down arrow", function () {
testKey({
color: "hsla(90, 25%, 50%, 0)",
item: "$opacitySelector",
key: KeyEvent.DOM_VK_DOWN,
param: "a",
delta: 0,
tolerance: 0.005
});
});
it("should decrease opacity by 0.05 on shift down arrow", function () {
testKey({
item: "$opacitySelector",
key: KeyEvent.DOM_VK_DOWN,
shift: true,
param: "a",
delta: -0.05,
tolerance: 0.005
});
});
it("should clip min opacity on shift down arrow", function () {
testKey({
color: "hsla(90, 25%, 50%, 0)",
item: "$opacitySelector",
key: KeyEvent.DOM_VK_DOWN,
shift: true,
param: "a",
delta: 0,
tolerance: 0.005
});
});
// For #2138
it("should increase hue by 18 on shift up arrow even if saturation is 0", function () {
testKey({
color: "hsl(180, 0, 0)",
item: "$hueBase",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "h",
delta: 18,
tolerance: 1,
exact: true
});
});
it("should increase hue by 18 on shift up arrow for a near-gray hex color", function () {
testKey({
color: "#5c5b56",
item: "$hueBase",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "h",
delta: 18,
tolerance: 1,
exact: true
});
});
it("should not change value when hue changes", function () {
testKey({
color: "#8e8247",
item: "$hueBase",
key: KeyEvent.DOM_VK_UP,
shift: true,
param: "v",
delta: 0,
tolerance: 0.01,
exact: true
});
});
// For #2193 and #2229
it("should prevent default on the key event for an unhandled arrow key on non-text-field", function () {
testPreventDefault({
color: "#8e8247",
item: "$hueBase",
key: KeyEvent.DOM_VK_RIGHT,
expected: true
});
});
it("should prevent default on left arrow at the start of the text field", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [0, 0],
key: KeyEvent.DOM_VK_LEFT,
expected: true
});
});
it("should not prevent default on left arrow in the middle of the text field", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [3, 3],
key: KeyEvent.DOM_VK_LEFT,
expected: false
});
});
it("should not prevent default on left arrow at the end of the text field", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [7, 7],
key: KeyEvent.DOM_VK_LEFT,
expected: false
});
});
it("should not prevent default on left arrow with a range selection", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [0, 7],
key: KeyEvent.DOM_VK_LEFT,
expected: false
});
});
it("should not prevent default on right arrow at the start of the text field", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [0, 0],
key: KeyEvent.DOM_VK_RIGHT,
expected: false
});
});
it("should not prevent default on right arrow in the middle of the text field", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [3, 3],
key: KeyEvent.DOM_VK_RIGHT,
expected: false
});
});
it("should prevent default on right arrow at the end of the text field", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [7, 7],
key: KeyEvent.DOM_VK_RIGHT,
expected: true
});
});
it("should not prevent default on right arrow with a range selection", function () {
testPreventDefault({
color: "#8e8247",
item: "$colorValue",
selection: [0, 7],
key: KeyEvent.DOM_VK_RIGHT,
expected: false
});
});
});
describe("color swatches and original color", function () {
it("should restore to original color when clicked on", function () {
makeUI("#abcdef");
colorEditor.setColorFromString("#0000ff");
colorEditor.$originalColor.trigger("click");
expect(tinycolor(colorEditor.getColor()).toHexString()).toBe("#abcdef");
});
it("should create swatches", function () {
makeUI("#abcdef");
expect($(".swatch").length).toBe(2);
});
it("should set color to a swatch when clicked on", function () {
makeUI("#fedcba");
$($(".swatch")[0]).trigger("click");
expect(tinycolor(colorEditor.getColor()).toHexString()).toBe("#abcdef");
});
});
describe("input text field syncing", function () {
it("should commit valid changes made in the input field on the input event", function () {
makeUI("#abcdef");
colorEditor.$colorValue.val("#fedcba");
colorEditor.$colorValue.trigger("input");
expect(tinycolor(colorEditor.getColor()).toHexString()).toBe("#fedcba");
});
it("should commit valid changes made in the input field on the change event", function () {
makeUI("#abcdef");
colorEditor.$colorValue.val("#fedcba");
colorEditor.$colorValue.trigger("change");
expect(tinycolor(colorEditor.getColor()).toHexString()).toBe("#fedcba");
});
it("should not commit changes on the input event while the value is invalid, but should keep them in the text field", function () {
makeUI("#abcdef");
colorEditor.$colorValue.val("rgb(0, 0, 0");
colorEditor.$colorValue.trigger("input");
expect(tinycolor(colorEditor.getColor()).toHexString()).toBe("#abcdef");
expect(colorEditor.$colorValue.val()).toBe("rgb(0, 0, 0");
});
it("should revert to the previous value on the change event while the value is invalid", function () {
makeUI("#abcdef");
colorEditor.$colorValue.val("rgb(0, 0, 0");
colorEditor.$colorValue.trigger("change");
expect(tinycolor(colorEditor.getColor()).toHexString()).toBe("#abcdef");
expect(colorEditor.$colorValue.val()).toBe("#abcdef");
});
it("should convert percentage RGB values to normal values", function () {
makeUI("#abcdef");
expect(colorEditor._convertToNormalRGB("rgb(25%, 50%, 75%)")).toBe("rgb(64, 128, 191)");
});
it("should normalize a string to match tinycolor's format", function () {
makeUI("#abcdef");
//Percentage based colors are now supported: the following test is obsolete
//expect(colorEditor._normalizeColorString("rgb(25%,50%,75%)")).toBe("rgb(64, 128, 191)");
expect(colorEditor._normalizeColorString("rgb(10,20, 30)")).toBe("rgb(10, 20, 30)");
});
});
describe("undo/redo", function () {
function triggerCtrlKey($element, key, shift) {
var ctrlKeyProperty = (brackets.platform === "win" ? "ctrlKey" : "metaKey"),
eventProps = {keyCode: key, shiftKey: shift};
eventProps[ctrlKeyProperty] = true;
$element.trigger($.Event("keydown", eventProps));
}
it("should undo when Ctrl-Z is pressed on a focused element in the color editor", function () {
makeUI("#abcdef");
runs(function () {
colorEditor.setColorFromString("#a0a0a0");
colorEditor.$hueBase.focus();
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("#abcdef");
});
});
it("should redo when Ctrl-Shift-Z is pressed on a focused element in the color editor", function () {
makeUI("#abcdef");
runs(function () {
colorEditor._commitColor("#a0a0a0", true);
colorEditor.$hueBase.focus();
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z, true);
expect(getColorString()).toBe("#a0a0a0");
});
});
it("should redo when Ctrl-Y is pressed on a focused element in the color editor", function () {
makeUI("#abcdef");
runs(function () {
colorEditor._commitColor("#a0a0a0", true);
colorEditor.$hueBase.focus();
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Y);
expect(getColorString()).toBe("#a0a0a0");
});
});
it("should redo when Ctrl-Y is pressed after two Ctrl-Zs (only one Ctrl-Z should take effect)", function () {
makeUI("#abcdef");
runs(function () {
colorEditor._commitColor("#a0a0a0", true);
colorEditor.$hueBase.focus();
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Y);
expect(getColorString()).toBe("#a0a0a0");
});
});
it("should undo when Ctrl-Z is pressed after two Ctrl-Ys (only one Ctrl-Y should take effect)", function () {
makeUI("#abcdef");
runs(function () {
colorEditor._commitColor("#a0a0a0", true);
colorEditor.$hueBase.focus();
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Y);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Y);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("#abcdef");
});
});
it("should undo an rgba conversion", function () {
makeUI("#abcdef");
runs(function () {
colorEditor.$rgbaButton.click();
triggerCtrlKey(colorEditor.$rgbaButton, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("#abcdef");
});
});
it("should undo an hsla conversion", function () {
makeUI("#abcdef");
runs(function () {
colorEditor.$hslButton.click();
triggerCtrlKey(colorEditor.$hslButton, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("#abcdef");
});
});
it("should undo a hex conversion", function () {
makeUI("rgba(12, 32, 65, 0.2)");
runs(function () {
colorEditor.$hexButton.trigger("click");
triggerCtrlKey(colorEditor.$hexButton, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("rgba(12, 32, 65, 0.2)");
});
});
it("should undo a saturation/value change", function () {
makeUI("rgba(100, 150, 200, 0.3)");
runs(function () {
eventAtRatio("mousedown", colorEditor.$selectionBase, [0.5, 0.5]);
triggerCtrlKey(colorEditor.$selectionBase, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("rgba(100, 150, 200, 0.3)");
colorEditor.$selectionBase.trigger("mouseup"); // clean up drag state
});
});
it("should undo a hue change", function () {
makeUI("rgba(100, 150, 200, 0.3)");
runs(function () {
eventAtRatio("mousedown", colorEditor.$hueBase, [0, 0.5]);
triggerCtrlKey(colorEditor.$hueBase, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("rgba(100, 150, 200, 0.3)");
colorEditor.$hueBase.trigger("mouseup"); // clean up drag state
});
});
it("should undo an opacity change", function () {
makeUI("rgba(100, 150, 200, 0.3)");
runs(function () {
eventAtRatio("mousedown", colorEditor.$opacitySelector, [0, 0.5]);
triggerCtrlKey(colorEditor.$opacitySelector, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("rgba(100, 150, 200, 0.3)");
colorEditor.$opacitySelector.trigger("mouseup"); // clean up drag state
});
});
it("should undo a text field change", function () {
makeUI("rgba(100, 150, 200, 0.3)");
runs(function () {
colorEditor.$colorValue.val("rgba(50, 50, 50, 0.9)");
triggerCtrlKey(colorEditor.$colorValue, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("rgba(100, 150, 200, 0.3)");
});
});
it("should undo a swatch click", function () {
makeUI("rgba(100, 150, 200, 0.3)");
runs(function () {
var $swatch = $(colorEditor.$swatches.find("li")[0]);
$swatch.trigger("click");
triggerCtrlKey($swatch, KeyEvent.DOM_VK_Z);
expect(getColorString()).toBe("rgba(100, 150, 200, 0.3)");
});
});
});
});
});
});