// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information.
//
//
///
module WinJSTests {
var Keys = WinJS.Utilities.Key;
var highlightStyle = document.createElement("style");
highlightStyle.innerHTML = "button:focus { background-color: red; }";
function createAndAppendFocusableElement(x: number, y: number, container: HTMLElement, textContent?: string, tagName = "button", width = 150, height = 150) {
var e = document.createElement(tagName);
e.style.position = "absolute";
e.style.width = width + "px";
e.style.height = height + "px";
e.style.minWidth = e.style.minHeight = "0px";
e.style.left = x + "px";
e.style.top = y + "px";
e.tabIndex = 0;
e["width"] = width;
e["height"] = height;
if (textContent) {
e.textContent = textContent;
}
container.appendChild(e);
return e;
}
function createCrossLayout(container?: HTMLElement, tagName = "button") {
/*
* 1
* 2 3 4
* 5
*/
if (!container) {
container = document.createElement("div");
container.style.position = "absolute";
container.style.width = container.style.height = "600px";
}
return [
container,
createAndAppendFocusableElement(250, 50, container, "1", tagName),
createAndAppendFocusableElement(50, 250, container, "2", tagName),
createAndAppendFocusableElement(250, 250, container, "3", tagName),
createAndAppendFocusableElement(450, 250, container, "4", tagName),
createAndAppendFocusableElement(250, 450, container, "5", tagName)
];
}
function spinWait(evaluator: () => boolean) {
return new WinJS.Promise(c => {
var count = 0;
var handle = setInterval(() => {
if (++count === 100) {
clearInterval(handle);
evaluator(); // Step into this call to debug the evaluator
throw "test timeout";
}
if (evaluator()) {
c();
clearInterval(handle);
}
}, 50);
});
}
function waitForFocus(w: Window, e: HTMLElement) {
return spinWait(() => w.document.activeElement === e);
}
export class XYFocusTests {
rootContainer: HTMLDivElement;
setUp() {
document.body.appendChild(highlightStyle);
this.rootContainer = document.createElement("div");
this.rootContainer.style.position = "relative";
document.body.appendChild(this.rootContainer);
WinJS.UI.XYFocus.focusRoot = this.rootContainer;
}
tearDown() {
document.body.removeChild(highlightStyle);
this.rootContainer.parentElement.removeChild(this.rootContainer);
this.rootContainer = null;
WinJS.UI.XYFocus.focusRoot = null;
// Clear event listeners
WinJS.UI.XYFocus["_listeners"].focuschanging = [];
WinJS.UI.XYFocus["_listeners"].focuschanged = [];
}
testFindNextFocusElement() {
var layout = createCrossLayout(this.rootContainer);
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
var target = WinJS.UI.XYFocus.findNextFocusElement("left");
LiveUnit.Assert.areEqual(layout[2], target);
target = WinJS.UI.XYFocus.findNextFocusElement("right");
LiveUnit.Assert.areEqual(layout[4], target);
target = WinJS.UI.XYFocus.findNextFocusElement("up");
LiveUnit.Assert.areEqual(layout[1], target);
target = WinJS.UI.XYFocus.findNextFocusElement("down");
LiveUnit.Assert.areEqual(layout[5], target);
}
testFindNextFocusElementWithUnfocusableReferenceElementAndInitialFocus() {
var layout = [
this.rootContainer,
createAndAppendFocusableElement(50, 50, this.rootContainer, "1", "div", 150, 150),
createAndAppendFocusableElement(200, 50, this.rootContainer, "2", "button", 150, 150),
];
layout[1].setAttribute("tabIndex", "");
layout[2].focus();
var target = WinJS.UI.XYFocus.findNextFocusElement("right", { referenceElement: layout[1] });
LiveUnit.Assert.areEqual(layout[2], target);
}
testFindNextFocusElementWithReferenceElement() {
var layout = createCrossLayout(this.rootContainer);
var target = WinJS.UI.XYFocus.findNextFocusElement("left", { referenceElement: layout[4] });
LiveUnit.Assert.areEqual(layout[3], target);
target = WinJS.UI.XYFocus.findNextFocusElement("right", { referenceElement: layout[2] });
LiveUnit.Assert.areEqual(layout[3], target);
target = WinJS.UI.XYFocus.findNextFocusElement("up", { referenceElement: layout[5] });
LiveUnit.Assert.areEqual(layout[3], target);
target = WinJS.UI.XYFocus.findNextFocusElement("down", { referenceElement: layout[1] });
LiveUnit.Assert.areEqual(layout[3], target);
}
testFindNextFocusElementWithNoInitialFocus() {
var layout = createCrossLayout(this.rootContainer);
document.body.focus();
LiveUnit.Assert.areEqual(document.body, document.activeElement);
var target = WinJS.UI.XYFocus.findNextFocusElement("right");
LiveUnit.Assert.isNotNull(target);
}
testMoveFocus() {
var layout = createCrossLayout(this.rootContainer);
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
var target = WinJS.UI.XYFocus.moveFocus("left");
LiveUnit.Assert.areEqual(document.activeElement, target);
LiveUnit.Assert.areEqual(layout[2], document.activeElement);
target = WinJS.UI.XYFocus.moveFocus("right");
LiveUnit.Assert.areEqual(document.activeElement, target);
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
target = WinJS.UI.XYFocus.moveFocus("up");
LiveUnit.Assert.areEqual(document.activeElement, target);
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
target = WinJS.UI.XYFocus.moveFocus("down");
LiveUnit.Assert.areEqual(document.activeElement, target);
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
}
testFocusRoot() {
var left = createCrossLayout();
var right = createCrossLayout();
right[0].style.top = "0px";
right[0].style.left = "700px";
this.rootContainer.appendChild(left[0]);
this.rootContainer.appendChild(right[0]);
left[3].focus();
LiveUnit.Assert.areEqual(left[3], document.activeElement);
WinJS.UI.XYFocus.moveFocus("right");
LiveUnit.Assert.areEqual(left[4], document.activeElement);
// Moving right should NOT move out of the left container
var target = WinJS.UI.XYFocus.moveFocus("right", { focusRoot: left[0] });
LiveUnit.Assert.areEqual(left[4], document.activeElement);
LiveUnit.Assert.isNull(target);
// Try the same as above using global focus root settings
WinJS.UI.XYFocus.focusRoot = left[0];
target = WinJS.UI.XYFocus.moveFocus("right");
LiveUnit.Assert.areEqual(left[4], document.activeElement);
LiveUnit.Assert.isNull(target);
// Focus should move across containers w/o focus root settings
WinJS.UI.XYFocus.focusRoot = null;
target = WinJS.UI.XYFocus.moveFocus("right");
LiveUnit.Assert.areEqual(right[2], document.activeElement);
}
testXYFocusHistory() {
/**
* ??????????????? ???????????????
* ? 1 ? ? ?
* ??????????????? ? ?
* ??????????????? ? ?
* ? ? ? 3 ?
* ? 2 ? ? ?
* ? ? ? ?
* ??????????????? ???????????????
*
* Normally, if focus was on 3, left would resolve to 2 since 2 occupies a bigger portion of 3's shadow.
* However, if focus initially was on 1, then was moved right to 3, then a following left should resolve to 1.
**/
var layout = [
this.rootContainer,
createAndAppendFocusableElement(50, 50, this.rootContainer, "1", "button", 200, 100),
createAndAppendFocusableElement(50, 200, this.rootContainer, "2", "button", 200, 300),
createAndAppendFocusableElement(350, 50, this.rootContainer, "3", "button", 200, 450)
];
// Move focus left from 3
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
WinJS.UI.XYFocus._xyFocus("left");
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
WinJS.UI.XYFocus._xyFocus("down");
LiveUnit.Assert.areEqual(layout[2], document.activeElement);
// Move focus right from 2, then left and we should end up at 2 again
WinJS.UI.XYFocus._xyFocus("right");
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
WinJS.UI.XYFocus._xyFocus("left");
LiveUnit.Assert.areEqual(layout[2], document.activeElement);
}
testXYFocusHistoryWithFractionalPixels() {
/**
* ??????????????????????????????
* ? ?? ?
* ? ?? 2 ?
* ? ?? ?
* ? 1 ????????????????
* ? ?? ?
* ? ?? 3 ?
* ? ?? ?
* ??????????????????????????????
*
* Normally, if focus was on 3, left would resolve to 2 since 2 occupies a bigger portion of 3's shadow.
* However, if focus initially was on 1, then was moved right to 3, then a following left should resolve to 1.
**/
var layout = [
this.rootContainer,
createAndAppendFocusableElement(50, 50.25, this.rootContainer, "1", "button", 428, 212),
createAndAppendFocusableElement(480, 50.25, this.rootContainer, "2", "button", 104, 104),
createAndAppendFocusableElement(480, 158.25, this.rootContainer, "3", "button", 104, 104)
];
// Move focus left from 3 to 1
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
WinJS.UI.XYFocus._xyFocus("left");
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
// Move focus right from 1 should land us on 3 again
WinJS.UI.XYFocus._xyFocus("right");
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
}
testPreventXYFocusViaFocusChangingEvent() {
var eventReceived = false;
WinJS.UI.XYFocus.addEventListener("focuschanging", (e: WinJS.UI.XYFocus.XYFocusEvent) => {
LiveUnit.Assert.areEqual(layout[1], e.detail.nextFocusElement);
e.preventDefault();
eventReceived = true;
});
var layout = createCrossLayout(this.rootContainer);
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
WinJS.UI.XYFocus.moveFocus("up");
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
LiveUnit.Assert.isTrue(eventReceived);
}
testPreventXYFocusViaKeyDownPreventDefault() {
var layout = createCrossLayout(this.rootContainer);
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
var eventReceived = false;
layout[3].addEventListener("keydown", e => {
eventReceived = true;
e.preventDefault()
});
Helper.keydown(layout[3], Keys.GamepadDPadUp);
LiveUnit.Assert.isTrue(eventReceived);
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
}
testOverrideAttribute() {
var layout = createCrossLayout(this.rootContainer);
for (var i = 1; i < layout.length; i++) {
layout[i].id = "btn" + i;
}
layout[3].setAttribute("data-win-focus", "{ left: '#btn4', right: '#btn2', up: '#btn5', down: '#btn1' }");
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
var target = WinJS.UI.XYFocus.findNextFocusElement("up");
LiveUnit.Assert.areEqual(layout[5], target);
target = WinJS.UI.XYFocus.findNextFocusElement("down");
LiveUnit.Assert.areEqual(layout[1], target);
target = WinJS.UI.XYFocus.findNextFocusElement("left");
LiveUnit.Assert.areEqual(layout[4], target);
target = WinJS.UI.XYFocus.findNextFocusElement("right");
LiveUnit.Assert.areEqual(layout[2], target);
}
testXYFocusWithDisabledElements() {
var layout = createCrossLayout(this.rootContainer);
layout[3].disabled = true;
layout[5].focus();
LiveUnit.Assert.areEqual(layout[5], document.activeElement);
WinJS.UI.XYFocus.moveFocus("up");
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
}
testXYFocusWithTabIndex() {
var layout = createCrossLayout(this.rootContainer, "div");
layout[3].tabIndex = -1;
layout[5].focus();
LiveUnit.Assert.areEqual(layout[5], document.activeElement);
WinJS.UI.XYFocus.moveFocus("up");
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
layout[3].tabIndex = 0;
WinJS.UI.XYFocus.moveFocus("down");
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
}
testXYFocusDefaultMappings(complete) {
function doKeydown(targetElement: HTMLElement, keyCode: number, expNextEl: HTMLElement) {
expectedKeyCode = keyCode;
expectedNextElement = expNextEl;
Helper.keydown(targetElement, keyCode);
}
var numEventsReceived = 0;
var expectedKeyCode = -1;
var expectedNextElement: HTMLElement;
WinJS.UI.XYFocus.addEventListener("focuschanging", (e: WinJS.UI.XYFocus.XYFocusEvent) => {
LiveUnit.Assert.areEqual(expectedKeyCode, e.detail.keyCode);
LiveUnit.Assert.areEqual(expectedNextElement, e.detail.nextFocusElement);
numEventsReceived++;
});
var layout = createCrossLayout(this.rootContainer);
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
doKeydown(layout[3], Keys.GamepadLeftThumbstickUp, layout[1]);
waitForFocus(window, layout[1])
.then(() => {
doKeydown(layout[1], Keys.GamepadLeftThumbstickDown, layout[3]);
return waitForFocus(window, layout[3]);
}).then(() => {
doKeydown(layout[3], Keys.GamepadLeftThumbstickLeft, layout[2]);
return waitForFocus(window, layout[2]);
}).then(() => {
doKeydown(layout[2], Keys.GamepadLeftThumbstickRight, layout[3]);
return waitForFocus(window, layout[3]);
}).then(() => {
doKeydown(layout[3], Keys.GamepadDPadUp, layout[1]);
return waitForFocus(window, layout[1]);
}).then(() => {
doKeydown(layout[1], Keys.GamepadDPadDown, layout[3]);
return waitForFocus(window, layout[3]);
}).then(() => {
doKeydown(layout[3], Keys.GamepadDPadLeft, layout[2]);
return waitForFocus(window, layout[2]);
}).then(() => {
doKeydown(layout[2], Keys.GamepadDPadRight, layout[3]);
return waitForFocus(window, layout[3]);
}).then(() => {
doKeydown(layout[3], Keys.NavigationUp, layout[1]);
return waitForFocus(window, layout[1]);
}).then(() => {
doKeydown(layout[1], Keys.NavigationDown, layout[3]);
return waitForFocus(window, layout[3]);
}).then(() => {
doKeydown(layout[3], Keys.NavigationLeft, layout[2]);
return waitForFocus(window, layout[2]);
}).then(() => {
doKeydown(layout[2], Keys.NavigationRight, layout[3]);
return waitForFocus(window, layout[3]);
}).done(() => {
LiveUnit.Assert.areEqual(12, numEventsReceived);
complete();
});
}
testXYFocusWithCustomKeyMappings(complete) {
function doKeydown(targetElement: HTMLElement, keyCode: number, expNextEl: HTMLElement) {
expectedKeyCode = keyCode;
expectedNextElement = expNextEl;
Helper.keydown(targetElement, keyCode);
}
var numEventsReceived = 0;
var expectedKeyCode = -1;
var expectedNextElement: HTMLElement;
WinJS.UI.XYFocus.addEventListener("focuschanging", (e: WinJS.UI.XYFocus.XYFocusEvent) => {
LiveUnit.Assert.areEqual(expectedKeyCode, e.detail.keyCode);
LiveUnit.Assert.areEqual(expectedNextElement, e.detail.nextFocusElement);
numEventsReceived++;
});
WinJS.UI.XYFocus.keyCodeMap["up"].push(Keys.w);
WinJS.UI.XYFocus.keyCodeMap["down"].push(Keys.s);
WinJS.UI.XYFocus.keyCodeMap["left"].push(Keys.a);
WinJS.UI.XYFocus.keyCodeMap["right"].push(Keys.d);
var layout = createCrossLayout(this.rootContainer);
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
doKeydown(layout[3], Keys.w, layout[1]);
waitForFocus(window, layout[1])
.then(() => {
doKeydown(layout[1], Keys.s, layout[3]);
return waitForFocus(window, layout[3]);
}).then(() => {
doKeydown(layout[3], Keys.a, layout[2]);
return waitForFocus(window, layout[2]);
}).then(() => {
doKeydown(layout[2], Keys.d, layout[3]);
return waitForFocus(window, layout[3]);
}).done(() => {
LiveUnit.Assert.areEqual(4, numEventsReceived);
complete();
});
}
testFocusChangedEvent(complete) {
WinJS.UI.XYFocus.addEventListener("focuschanged", (e: WinJS.UI.XYFocus.XYFocusEvent) => {
LiveUnit.Assert.areEqual(layout[3], e.detail.previousFocusElement);
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
LiveUnit.Assert.areEqual(Keys.GamepadDPadUp, e.detail.keyCode);
complete();
});
var layout = createCrossLayout(this.rootContainer);
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
Helper.keydown(layout[3], Keys.GamepadDPadUp);
}
testXYFocusInIFrame(complete) {
/**
* 1 2
* ???????
* 8 ? 0 1 ? 3
* 7 ? 2 3 ? 4
* ???????
* 6 5
**/
var layout = [
this.rootContainer,
createAndAppendFocusableElement(125, 25, this.rootContainer, "1", "button", 50, 50),
createAndAppendFocusableElement(200, 25, this.rootContainer, "2", "button", 50, 50),
createAndAppendFocusableElement(300, 125, this.rootContainer, "3", "button", 50, 50),
createAndAppendFocusableElement(300, 200, this.rootContainer, "4", "button", 50, 50),
createAndAppendFocusableElement(200, 300, this.rootContainer, "5", "button", 50, 50),
createAndAppendFocusableElement(125, 300, this.rootContainer, "6", "button", 50, 50),
createAndAppendFocusableElement(25, 200, this.rootContainer, "7", "button", 50, 50),
createAndAppendFocusableElement(25, 125, this.rootContainer, "8", "button", 50, 50),
];
var iframeEl = createAndAppendFocusableElement(100, 100, this.rootContainer, null, "iframe", 175, 175);
iframeEl.src = "XYFocusPage.html";
var iframeWin = (iframeEl).contentWindow;
var iframeLayout: Array;
window.addEventListener("message", function ready(e: MessageEvent) {
// The first crossframe message indicates that the iframe has loaded.
window.removeEventListener("message", ready);
iframeLayout = iframeWin.document.querySelectorAll("button");
layout[1].focus();
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
WinJS.UI.XYFocus._xyFocus("down");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
waitForFocus(iframeWin, iframeLayout[0])
.then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("right");
return waitForFocus(iframeWin, iframeLayout[1]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("up");
return waitForFocus(window, layout[2]);
}).then(() => {
WinJS.UI.XYFocus._xyFocus("down");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
return waitForFocus(iframeWin, iframeLayout[1]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("right");
return waitForFocus(window, layout[3]);
}).then(() => {
WinJS.UI.XYFocus._xyFocus("left");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
return waitForFocus(iframeWin, iframeLayout[1]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("down");
return waitForFocus(iframeWin, iframeLayout[3]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("right");
return waitForFocus(window, layout[4]);
}).then(() => {
WinJS.UI.XYFocus._xyFocus("left");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
return waitForFocus(iframeWin, iframeLayout[3]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("down");
return waitForFocus(window, layout[5]);
}).then(() => {
WinJS.UI.XYFocus._xyFocus("up");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
return waitForFocus(iframeWin, iframeLayout[3]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("left");
return waitForFocus(iframeWin, iframeLayout[2]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("down");
return waitForFocus(window, layout[6]);
}).then(() => {
WinJS.UI.XYFocus._xyFocus("up");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
return waitForFocus(iframeWin, iframeLayout[2]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("left");
return waitForFocus(window, layout[7]);
}).then(() => {
WinJS.UI.XYFocus._xyFocus("right");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
return waitForFocus(iframeWin, iframeLayout[2]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("up");
return waitForFocus(iframeWin, iframeLayout[0]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("left");
return waitForFocus(window, layout[8]);
}).then(() => {
WinJS.UI.XYFocus._xyFocus("right");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
return waitForFocus(iframeWin, iframeLayout[0]);
}).then(() => {
iframeWin["WinJS"].UI.XYFocus._xyFocus("up");
return waitForFocus(window, layout[1]);
}).done(complete);
});
}
testSuspended(complete) {
var expectDefaultPrevented = true;
var completeTest = false;
document.addEventListener("keydown", function testSuspendedKeyDownHandler(e: KeyboardEvent) {
LiveUnit.Assert.areEqual(expectDefaultPrevented, e.defaultPrevented);
LiveUnit.Assert.areEqual(expectedActiveElement, document.activeElement);
if (completeTest) {
document.removeEventListener("keydown", testSuspendedKeyDownHandler);
complete();
}
});
var layout = createCrossLayout(this.rootContainer);
layout[3].classList.add("win-xyfocus-suspended");
layout[2].focus();
LiveUnit.Assert.areEqual(layout[2], document.activeElement);
var expectedActiveElement = layout[3];
Helper.keydown(document.activeElement, Keys.GamepadDPadRight);
waitForFocus(window, layout[3]).done(() => {
expectDefaultPrevented = false;
completeTest = true;
Helper.keydown(document.activeElement, Keys.GamepadDPadRight);
});
}
testSuspendedContainer(complete) {
document.addEventListener("keydown", function testSuspendedKeyDownHandler(e: KeyboardEvent) {
LiveUnit.Assert.isFalse(e.defaultPrevented);
LiveUnit.Assert.areEqual(expectedActiveElement, document.activeElement);
document.removeEventListener("keydown", testSuspendedKeyDownHandler);
complete();
});
var layout = createCrossLayout(this.rootContainer);
layout[0].classList.add("win-xyfocus-suspended");
layout[2].focus();
LiveUnit.Assert.areEqual(layout[2], document.activeElement);
var expectedActiveElement = layout[2];
Helper.keydown(document.activeElement, Keys.GamepadDPadRight);
}
testSuspendedToggleMode(complete) {
var completeTest = false;
document.addEventListener("keydown", function testSuspendedKeyDownHandler(e: KeyboardEvent) {
LiveUnit.Assert.isFalse(e.defaultPrevented);
LiveUnit.Assert.isFalse(layout[2].classList.contains("win-xyfocus-togglemode-active"));
if (completeTest) {
document.removeEventListener("keydown", testSuspendedKeyDownHandler);
complete();
}
});
var layout = createCrossLayout(this.rootContainer);
layout[2].classList.add("win-xyfocus-togglemode");
layout[2].focus();
LiveUnit.Assert.areEqual(layout[2], document.activeElement);
// Assert that a suspended toggle mode element does not toggle
layout[2].classList.add("win-xyfocus-suspended");
Helper.keydown(document.activeElement, Keys.GamepadA);
// Assert that a toggle mode element in a suspended container does not toggle
layout[2].classList.remove("win-xyfocus-suspended");
layout[0].classList.add("win-xyfocus-suspended");
completeTest = true;
Helper.keydown(document.activeElement, Keys.GamepadA);
}
testToggleMode(complete) {
var expectDefaultPrevented = true;
var completeTest = false;
var callbackSignal: WinJS._Signal = null;
document.addEventListener("keydown", function testToggleModeKeyDownHandler(e: KeyboardEvent) {
LiveUnit.Assert.areEqual(expectDefaultPrevented, e.defaultPrevented);
callbackSignal && callbackSignal.complete();
if (completeTest) {
document.removeEventListener("keydown", testToggleModeKeyDownHandler);
complete();
}
});
var layout = createCrossLayout(this.rootContainer);
layout[3].classList.add("win-xyfocus-togglemode");
layout[3].focus();
LiveUnit.Assert.areEqual(layout[3], document.activeElement);
Helper.keydown(document.activeElement, Keys.GamepadDPadRight);
waitForFocus(window, layout[4])
.then(() => {
Helper.keydown(document.activeElement, Keys.GamepadDPadLeft);
return waitForFocus(window, layout[3]);
}).then(() => {
callbackSignal = new WinJS._Signal();
Helper.keydown(document.activeElement, Keys.GamepadA);
return callbackSignal.promise;
}).then(() => {
LiveUnit.Assert.isTrue(layout[3].classList.contains("win-xyfocus-togglemode-active"));
expectDefaultPrevented = false;
callbackSignal = new WinJS._Signal();
Helper.keydown(document.activeElement, Keys.GamepadDPadRight);
return callbackSignal.promise;
}).then(() => {
expectDefaultPrevented = true;
callbackSignal = new WinJS._Signal();
Helper.keydown(document.activeElement, Keys.GamepadB);
return callbackSignal.promise;
}).done(() => {
completeTest = true;
callbackSignal = null;
LiveUnit.Assert.isFalse(layout[3].classList.contains("win-xyfocus-togglemode-active"));
Helper.keydown(document.activeElement, Keys.GamepadDPadRight);
});
}
testIFrameRemovalUnregistersWithXYFocus(complete) {
var iframeEl = createAndAppendFocusableElement(100, 100, this.rootContainer, null, "iframe", 200, 200);
iframeEl.src = "XYFocusPage.html";
var that = this;
window.addEventListener("message", function windowMessage(e: MessageEvent) {
if (e.data["msWinJSXYFocusControlMessage"] && e.data["msWinJSXYFocusControlMessage"].type === "register") {
var origCount = WinJS.UI.XYFocus._iframeHelper.count();
window.removeEventListener("message", windowMessage);
iframeEl.contentWindow.addEventListener("unload", () => {
LiveUnit.Assert.areEqual(origCount - 1, WinJS.UI.XYFocus._iframeHelper.count());
complete();
});
iframeEl.parentElement.removeChild(iframeEl);
}
});
}
testXYFocusWorksWithElementsThatSpanTheCurrentViewport(complete) {
var layout: HTMLElement[] = [
this.rootContainer,
createAndAppendFocusableElement(100, 100, this.rootContainer, null, "button", 200, 200),
createAndAppendFocusableElement(300, -100, this.rootContainer, null, "button", 200, 100000)
];
layout[1].focus();
LiveUnit.Assert.areEqual(layout[1], document.activeElement);
Helper.keydown(layout[1], Keys.GamepadDPadRight);
waitForFocus(window, layout[2]).done(complete);
}
testXYFocusWithNoActiveElementDoesNotCauseExceptions() {
var body = document.body;
document.body.parentElement.removeChild(body);
Helper.keydown(document.documentElement, Keys.GamepadDPadUp);
document.documentElement.appendChild(body);
}
testIFrameWithFocusableBody(complete) {
/*
[BUTTON] [IFRAME] [BUTTON]
*/
var leftButton = createAndAppendFocusableElement(0, 0, this.rootContainer, null, "button", 200, 200);
var iframeEl = createAndAppendFocusableElement(210, 0, this.rootContainer, null, "iframe", 200, 200);
iframeEl.src = "BlankXYFocusPageWithFocusableBody.html";
var iframeWin = (iframeEl).contentWindow;
var rightButton = createAndAppendFocusableElement(420, 0, this.rootContainer, null, "button", 200, 200);
window.addEventListener("message", function ready(e: MessageEvent) {
// The first crossframe message indicates that the iframe has loaded.
window.removeEventListener("message", ready);
leftButton.focus();
LiveUnit.Assert.areEqual(leftButton, document.activeElement);
WinJS.UI.XYFocus._xyFocus("right");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
WinJS.Promise.timeout(500).then(function () {
LiveUnit.Assert.areEqual(iframeEl, document.activeElement, "Focus should not be automatically exiting the iframe");
iframeWin["WinJS"].UI.XYFocus._xyFocus("right");
return waitForFocus(window, rightButton);
}).done(complete);
});
}
testIFrameWithUnfocusableBody(complete) {
/*
[BUTTON] [IFRAME] [BUTTON]
*/
var leftButton = createAndAppendFocusableElement(0, 0, this.rootContainer, null, "button", 200, 200);
var iframeEl = createAndAppendFocusableElement(210, 0, this.rootContainer, null, "iframe", 200, 200);
iframeEl.src = "BlankXYFocusPageWithFocusableBody.html";
var iframeWin = (iframeEl).contentWindow;
var rightButton = createAndAppendFocusableElement(420, 0, this.rootContainer, null, "button", 200, 200);
window.addEventListener("message", function ready(e: MessageEvent) {
// The first crossframe message indicates that the iframe has loaded.
window.removeEventListener("message", ready);
// Make the body inside the iframe unfocusable
iframeWin.document.body.tabIndex = -1;
leftButton.focus();
LiveUnit.Assert.areEqual(leftButton, document.activeElement);
// Going right from the left button should focus the iframe but the iframe should immediately
// signal back a dFocusExit to the right which gets us to the expected right button.
WinJS.UI.XYFocus._xyFocus("right");
LiveUnit.Assert.areEqual(iframeEl, document.activeElement);
waitForFocus(window, rightButton).done(complete);
});
}
}
}
LiveUnit.registerTestClass("WinJSTests.XYFocusTests");