blob: caae126f0c5b4ac83bdf56280139458e65cccdf1 [file]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
window.onerror = e => chrome.test.fail(e.stack);
window.onunhandledrejection = e => chrome.test.fail(e.reason);
function animationFrame() {
return new Promise(resolve => requestAnimationFrame(resolve));
}
// Async spin until predicate() returns true.
function waitFor(predicate) {
if (predicate()) {
return;
}
return new Promise(resolve => setTimeout(() => {
resolve(waitFor(predicate));
}, 0));
}
function contentElement() {
return document.elementFromPoint(innerWidth / 2, innerHeight / 2);
}
function isAnnotationMode() {
return document.querySelector('#toolbar').annotationMode;
}
async function testAsync(f) {
try {
await f();
chrome.test.succeed();
} catch (e) {
chrome.test.fail(e.stack);
}
}
chrome.test.runTests([
function testAnnotationsEnabled() {
const toolbar = document.body.querySelector('#toolbar');
chrome.test.assertTrue(toolbar.pdfAnnotationsEnabled);
chrome.test.assertTrue(
toolbar.shadowRoot.querySelector('#annotate') != null);
chrome.test.succeed();
},
function testEnterAnnotationMode() {
testAsync(async () => {
chrome.test.assertEq('EMBED', contentElement().tagName);
// Enter annotation mode.
$('toolbar').toggleAnnotation();
await viewer.loaded;
chrome.test.assertEq(
'VIEWER-INK-HOST', contentElement().tagName);
});
},
function testViewportToCameraConversion() {
testAsync(async () => {
chrome.test.assertTrue(isAnnotationMode());
const inkHost = contentElement();
const cameras = [];
inkHost.ink_.setCamera = camera => cameras.push(camera);
viewer.viewport_.setZoom(1);
viewer.viewport_.setZoom(2);
chrome.test.assertEq(2, cameras.length);
window.scrollTo(100, 100);
await animationFrame();
chrome.test.assertEq(3, cameras.length);
const expectations = [
{top: 44.25, left: -106.5, right: 718.5, bottom: -442.5},
{top: 23.25, left: -3.75, right: 408.75, bottom: -220.125},
{top: -14.25, left: 33.75, right: 446.25, bottom: -257.625},
];
for (const expectation of expectations) {
const actual = cameras.shift();
chrome.test.assertEq(expectation.top, actual.top);
chrome.test.assertEq(expectation.left, actual.left);
chrome.test.assertEq(expectation.bottom, actual.bottom);
chrome.test.assertEq(expectation.right, actual.right);
}
});
},
function testPenOptions() {
testAsync(async () => {
chrome.test.assertTrue(isAnnotationMode());
const inkHost = contentElement();
let tool = null;
inkHost.ink_.setAnnotationTool = value => tool = value;
// Pen defaults.
const viewerPdfToolbar = document.querySelector('viewer-pdf-toolbar');
const pen = viewerPdfToolbar.$$('#pen');
pen.click();
chrome.test.assertEq('pen', tool.tool);
chrome.test.assertEq(0.1429, tool.size);
chrome.test.assertEq('#000000', tool.color);
// Selected size and color.
const penOptions = viewerPdfToolbar.$$('#pen viewer-pen-options');
penOptions.$$('#sizes [value="1"]').click();
penOptions.$$('#colors [value="#00b0ff"]').click();
await animationFrame();
chrome.test.assertEq('pen', tool.tool);
chrome.test.assertEq(1, tool.size);
chrome.test.assertEq('#00b0ff', tool.color);
// Eraser defaults.
viewerPdfToolbar.$$('#eraser').click();
chrome.test.assertEq('eraser', tool.tool);
chrome.test.assertEq(1, tool.size);
chrome.test.assertEq(null, tool.color);
// Pen keeps previous settings.
pen.click();
chrome.test.assertEq('pen', tool.tool);
chrome.test.assertEq(1, tool.size);
chrome.test.assertEq('#00b0ff', tool.color);
// Highlighter defaults.
viewerPdfToolbar.$$('#highlighter').click();
chrome.test.assertEq('highlighter', tool.tool);
chrome.test.assertEq(0.7143, tool.size);
chrome.test.assertEq('#ffbc00', tool.color);
// Need to expand to use this color.
const highlighterOptions =
viewerPdfToolbar.$$('#highlighter viewer-pen-options');
highlighterOptions.$$('#colors [value="#d1c4e9"]').click();
chrome.test.assertEq('#ffbc00', tool.color);
// Selected size and expanded color.
highlighterOptions.$$('#sizes [value="1"]').click();
highlighterOptions.$$('#colors #expand').click();
highlighterOptions.$$('#colors [value="#d1c4e9"]').click();
chrome.test.assertEq('highlighter', tool.tool);
chrome.test.assertEq(1, tool.size);
chrome.test.assertEq('#d1c4e9', tool.color);
});
},
function testStrokeUndoRedo() {
testAsync(async () => {
const inkHost = contentElement();
const viewerPdfToolbar = document.querySelector('viewer-pdf-toolbar');
const undo = viewerPdfToolbar.$$('#undo');
const redo = viewerPdfToolbar.$$('#redo');
const pen = {
pointerId: 2,
pointerType: 'pen',
pressure: 0.5,
clientX: inkHost.offsetWidth / 2,
clientY: inkHost.offsetHeight / 2,
buttons: 0,
};
// Initial state.
chrome.test.assertEq(undo.disabled, true);
chrome.test.assertEq(redo.disabled, true);
// Draw a stroke.
inkHost.dispatchEvent(new PointerEvent('pointerdown', pen));
inkHost.dispatchEvent(new PointerEvent('pointermove', pen));
inkHost.dispatchEvent(new PointerEvent('pointerup', pen));
await waitFor(() => undo.disabled == false);
chrome.test.assertEq(redo.disabled, true);
undo.click();
await waitFor(() => undo.disabled == true);
chrome.test.assertEq(redo.disabled, false);
redo.click();
await waitFor(() => undo.disabled == false);
chrome.test.assertEq(redo.disabled, true);
});
},
function testPointerEvents() {
testAsync(async () => {
chrome.test.assertTrue(isAnnotationMode());
const inkHost = contentElement();
inkHost.resetPenMode();
const events = [];
inkHost.ink_.dispatchPointerEvent = (type, init) =>
events.push({type: type, init: init});
const mouse = {pointerId: 1, pointerType: 'mouse', buttons: 1};
const pen = {
pointerId: 2,
pointerType: 'pen',
pressure: 0.5,
clientX: 3,
clientY: 4,
buttons: 0,
};
const touch1 = {pointerId: 11, pointerType: 'touch'};
const touch2 = {pointerId: 22, pointerType: 'touch'};
function checkExpectations(expectations) {
chrome.test.assertEq(expectations.length, events.length);
while (expectations.length) {
const event = events.shift();
const expectation = expectations.shift();
chrome.test.assertEq(expectation.type, event.type);
for (const key of Object.keys(expectation.init)) {
chrome.test.assertEq(expectation.init[key], event.init[key]);
}
}
}
// Normal sequence.
inkHost.dispatchEvent(new PointerEvent('pointerdown', pen));
inkHost.dispatchEvent(new PointerEvent('pointermove', pen));
inkHost.dispatchEvent(new PointerEvent('pointerup', pen));
checkExpectations([
{type: 'pointerdown', init: pen},
{type: 'pointermove', init: pen},
{type: 'pointerup', init: pen},
]);
// Multi-touch gesture should cancel and suppress first pointer.
inkHost.resetPenMode();
inkHost.dispatchEvent(new PointerEvent('pointerdown', touch1));
inkHost.dispatchEvent(new PointerEvent('pointerdown', touch2));
inkHost.dispatchEvent(new PointerEvent('pointermove', touch1));
inkHost.dispatchEvent(new PointerEvent('pointerup', touch1));
checkExpectations([
{type: 'pointerdown', init: touch1},
{type: 'pointercancel', init: touch1},
]);
// Pointers which are not active should be suppressed.
inkHost.resetPenMode();
inkHost.dispatchEvent(new PointerEvent('pointerdown', mouse));
inkHost.dispatchEvent(new PointerEvent('pointerdown', pen));
inkHost.dispatchEvent(new PointerEvent('pointerdown', touch1));
inkHost.dispatchEvent(new PointerEvent('pointermove', mouse));
inkHost.dispatchEvent(new PointerEvent('pointermove', pen));
inkHost.dispatchEvent(new PointerEvent('pointermove', touch1));
inkHost.dispatchEvent(new PointerEvent('pointerup', mouse));
inkHost.dispatchEvent(new PointerEvent('pointermove', pen));
checkExpectations([
{type: 'pointerdown', init: mouse},
{type: 'pointermove', init: mouse},
{type: 'pointerup', init: mouse},
]);
// pointerleave should cause mouseup
inkHost.dispatchEvent(new PointerEvent('pointerdown', mouse));
inkHost.dispatchEvent(new PointerEvent('pointerleave', mouse));
checkExpectations([
{type: 'pointerdown', init: mouse},
{type: 'pointerup', init: mouse},
]);
// pointerleave does not apply to non-mouse pointers
inkHost.dispatchEvent(new PointerEvent('pointerdown', pen));
inkHost.dispatchEvent(new PointerEvent('pointerleave', pen));
inkHost.dispatchEvent(new PointerEvent('pointerup', pen));
checkExpectations([
{type: 'pointerdown', init: pen},
{type: 'pointerup', init: pen},
]);
// Browser will cancel touch on pen input
inkHost.resetPenMode();
inkHost.dispatchEvent(new PointerEvent('pointerdown', touch1));
inkHost.dispatchEvent(new PointerEvent('pointercancel', touch1));
inkHost.dispatchEvent(new PointerEvent('pointerdown', pen));
inkHost.dispatchEvent(new PointerEvent('pointerup', pen));
checkExpectations([
{type: 'pointerdown', init: touch1},
{type: 'pointercancel', init: touch1},
{type: 'pointerdown', init: pen},
{type: 'pointerup', init: pen},
]);
});
},
function testTouchPanGestures() {
testAsync(async () => {
// Ensure that we have an out-of-bounds area.
viewer.viewport_.setZoom(0.5);
chrome.test.assertTrue(isAnnotationMode());
const inkHost = contentElement();
function dispatchPointerEvent(type, init) {
const pointerEvent = new PointerEvent(type, init);
inkHost.dispatchEvent(pointerEvent);
return pointerEvent;
}
function dispatchPointerAndTouchEvents(type, init) {
const pointerEvent = dispatchPointerEvent(type, init);
let touchPrevented = false;
inkHost.onTouchStart_({
timeStamp: pointerEvent.timeStamp,
preventDefault() {
touchPrevented = true;
}
});
return touchPrevented;
}
const pen = {
pointerId: 2,
pointerType: 'pen',
pressure: 0.5,
clientX: innerWidth / 2,
clientY: innerHeight / 2,
};
const outOfBoundsPen = {
pointerId: 2,
pointerType: 'pen',
pressure: 0.5,
clientX: 2,
clientY: 3,
};
const touch = {
pointerId: 3,
pointerType: 'touch',
pressure: 0.5,
clientX: innerWidth / 2,
clientY: innerHeight / 2,
};
const outOfBoundsTouch = {
pointerId: 4,
pointerType: 'touch',
pressure: 0.5,
clientX: 4,
clientY: 5,
};
inkHost.resetPenMode();
let prevented = dispatchPointerAndTouchEvents('pointerdown', touch);
dispatchPointerEvent('pointerup', touch);
chrome.test.assertTrue(
prevented, 'in document touch should prevent default');
prevented =
dispatchPointerAndTouchEvents('pointerdown', outOfBoundsTouch);
dispatchPointerEvent('pointerup', outOfBoundsTouch);
chrome.test.assertFalse(
prevented, 'out of bounds touch should start gesture');
prevented = dispatchPointerAndTouchEvents('pointerdown', pen);
dispatchPointerEvent('pointerup', pen);
chrome.test.assertTrue(
prevented, 'in document pen should prevent default');
prevented = dispatchPointerAndTouchEvents('pointerdown', outOfBoundsPen);
dispatchPointerEvent('pointerup', outOfBoundsPen);
chrome.test.assertFalse(
prevented, 'out of bounds pen should start gesture');
chrome.test.assertTrue(
inkHost.penMode_, 'pen input should switch to pen mode');
prevented = dispatchPointerAndTouchEvents('pointerdown', touch);
dispatchPointerEvent('pointerup', touch);
chrome.test.assertFalse(
prevented, 'in document touch in pen mode should start gesture');
});
},
function testExitAnnotationMode() {
testAsync(async () => {
chrome.test.assertTrue(isAnnotationMode());
// Exit annotation mode.
$('toolbar').toggleAnnotation();
await viewer.loaded;
chrome.test.assertEq('EMBED', contentElement().tagName);
});
},
]);