From 98649322be61deac5481fc9ced829eb64ca8bac4 Mon Sep 17 00:00:00 2001
From: Stacey Van Herk <13419300+svanherk@users.noreply.github.com>
Date: Wed, 26 Aug 2026 18:27:37 -0400
Subject: [PATCH 1/4] GAUD-10597 - Add drag test command
---
src/browser/commands.js | 16 ++++++++
src/browser/index.js | 2 +-
test/browser/commands.test.js | 69 ++++++++++++++++++++++++++++++++++-
3 files changed, 85 insertions(+), 2 deletions(-)
diff --git a/src/browser/commands.js b/src/browser/commands.js
index 3d9883be..dd7af392 100644
--- a/src/browser/commands.js
+++ b/src/browser/commands.js
@@ -40,6 +40,22 @@ export async function dragDropElems(elem, toElem) {
await sendMouse({ type: 'up' });
}
+export async function dragElemBy(elem, offsetX = 0, offsetY = 0) {
+ const pixels = 10; // Mimic dragging by moving in 10px increments to the target position
+ const numSteps = Math.ceil(Math.max(Math.abs(offsetX), Math.abs(offsetY)) / pixels);
+
+ const position = getElementPosition(elem);
+ await sendMouse({ type: 'move', position: [position.x, position.y] });
+
+ await sendMouse({ type: 'down' });
+ for (let i = 1; i <= numSteps; i++) {
+ const dx = Math.sign(offsetX) * Math.min(Math.abs(offsetX), pixels * i);
+ const dy = Math.sign(offsetY) * Math.min(Math.abs(offsetY), pixels * i);
+ await sendMouse({ type: 'move', position: [position.x + dx, position.y + dy] });
+ }
+ await sendMouse({ type: 'up' });
+}
+
export async function focusElem(elem) {
await cmdSendKeys({ press: 'Shift' }); // Tab moves focus, Escape causes dismissible things to close
elem.focus({ focusVisible: true });
diff --git a/src/browser/index.js b/src/browser/index.js
index cc27b0ac..2a709401 100644
--- a/src/browser/index.js
+++ b/src/browser/index.js
@@ -2,6 +2,6 @@ import './vdiff.js';
import './axe.js';
export { assert, aTimeout, defineCE, expect, html, nextFrame, oneDefaultPreventedEvent, oneEvent, waitUntil } from '@open-wc/testing';
-export { clickAt, clickElem, clickElemAt, dragDropElems, focusElem, hoverAt, hoverElem, hoverElemAt, sendKeys, sendKeysElem, setViewport } from './commands.js';
+export { clickAt, clickElem, clickElemAt, dragDropElems, dragElemBy, focusElem, hoverAt, hoverElem, hoverElemAt, sendKeys, sendKeysElem, setViewport } from './commands.js';
export { fixture, waitForElem } from './fixture.js';
export { runConstructor } from './constructor.js';
diff --git a/test/browser/commands.test.js b/test/browser/commands.test.js
index 7162ce3a..02f4e457 100644
--- a/test/browser/commands.test.js
+++ b/test/browser/commands.test.js
@@ -1,9 +1,10 @@
-import { clickAt, clickElem, clickElemAt, dragDropElems, expect, fixture, focusElem, hoverAt, hoverElem, hoverElemAt, sendKeys, sendKeysElem, setViewport } from '../../src/browser/index.js';
+import { clickAt, clickElem, clickElemAt, dragDropElems, dragElemBy, expect, fixture, focusElem, hoverAt, hoverElem, hoverElemAt, sendKeys, sendKeysElem, setViewport } from '../../src/browser/index.js';
import { html } from 'lit';
import { spy } from 'sinon';
describe('commands', () => {
const buttonTemplate = html``;
+ const dragTemplate = html`
@@ -19,6 +20,7 @@ describe('commands', () => {
let elem, focusSource, hovered, key, keys;
const clickPos = { x: 0, y: 0 };
const mousePos = { x: 0, y: 0 };
+ const pointerEvents = [];
function onClick(e) {
clickPos.x = e.clientX;
@@ -41,6 +43,14 @@ describe('commands', () => {
mousePos.y = e.clientY;
}
+ function onPointer(e) {
+ pointerEvents.push({
+ type: e.type,
+ x: e.clientX,
+ y: e.clientY
+ });
+ }
+
function onMouseOver() {
hovered = true;
}
@@ -53,12 +63,22 @@ describe('commands', () => {
window.addEventListener('click', onClick);
window.addEventListener('keydown', onKeyDown);
window.addEventListener('mousemove', onMouseMove);
+ window.addEventListener('pointerdown', onPointer);
+ window.addEventListener('pointermove', onPointer);
+ window.addEventListener('pointerup', onPointer);
+ });
+
+ beforeEach(() => {
+ pointerEvents.length = 0;
});
after(() => {
window.removeEventListener('click', onClick);
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('mousemove', onMouseMove);
+ window.removeEventListener('pointerdown', onPointer);
+ window.removeEventListener('pointermove', onPointer);
+ window.removeEventListener('pointerup', onPointer);
});
describe('click/hover', () => {
@@ -194,6 +214,53 @@ describe('commands', () => {
});
+ describe('drag', () => {
+ const getDragMoves = (events) => events.slice(1).filter(e => e.type === 'pointermove').map(e => ({ x: e.x, y: e.y }));
+
+ beforeEach(async() => {
+ elem = await fixture(dragTemplate);
+ pointerEvents.length = 0;
+ });
+
+ it('should start dragging from the center of element and fire pointer events throughout the full drag flow', async() => {
+ await dragElemBy(elem, 20, 20);
+ expect(pointerEvents).to.deep.equal([
+ { type: 'pointermove', x: 100, y: 100 }, // Move to element center
+ { type: 'pointerdown', x: 100, y: 100 }, // Start drag
+ { type: 'pointermove', x: 110, y: 110 }, // Drag 10px
+ { type: 'pointermove', x: 120, y: 120 }, // Drag 10px
+ { type: 'pointerup', x: 120, y: 120 }, // End drag
+ ]);
+ });
+
+ it('should move to the target offset in 10px increments to the max', async() => {
+ await dragElemBy(elem, 25, 0);
+ expect(getDragMoves(pointerEvents)).to.deep.equal([
+ { x: 110, y: 100 },
+ { x: 120, y: 100 },
+ { x: 125, y: 100 },
+ ]);
+ });
+
+ it('should clamp the shorter axis while the longer axis keeps stepping', async() => {
+ await dragElemBy(elem, 30, 15);
+ expect(getDragMoves(pointerEvents)).to.deep.equal([
+ { x: 110, y: 110 },
+ { x: 120, y: 115 },
+ { x: 130, y: 115 },
+ ]);
+ });
+
+ it('should drag in negative directions', async() => {
+ await dragElemBy(elem, -25, -25);
+ expect(getDragMoves(pointerEvents)).to.deep.equal([
+ { x: 90, y: 90 },
+ { x: 80, y: 80 },
+ { x: 75, y: 75 },
+ ]);
+ });
+ });
+
describe('drag & drop', () => {
it('should drag & drop element', (done) => {
From ce66d5b862fdadb6de805706153fff434be490cd Mon Sep 17 00:00:00 2001
From: Stacey Van Herk <13419300+svanherk@users.noreply.github.com>
Date: Thu, 27 Aug 2026 10:43:39 -0400
Subject: [PATCH 2/4] Alternative way to do the tests (#1070)
---
test/browser/commands.test.js | 50 +++++++++++++++++------------------
1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/test/browser/commands.test.js b/test/browser/commands.test.js
index 02f4e457..abbdc6db 100644
--- a/test/browser/commands.test.js
+++ b/test/browser/commands.test.js
@@ -20,7 +20,6 @@ describe('commands', () => {
let elem, focusSource, hovered, key, keys;
const clickPos = { x: 0, y: 0 };
const mousePos = { x: 0, y: 0 };
- const pointerEvents = [];
function onClick(e) {
clickPos.x = e.clientX;
@@ -43,14 +42,6 @@ describe('commands', () => {
mousePos.y = e.clientY;
}
- function onPointer(e) {
- pointerEvents.push({
- type: e.type,
- x: e.clientX,
- y: e.clientY
- });
- }
-
function onMouseOver() {
hovered = true;
}
@@ -63,22 +54,12 @@ describe('commands', () => {
window.addEventListener('click', onClick);
window.addEventListener('keydown', onKeyDown);
window.addEventListener('mousemove', onMouseMove);
- window.addEventListener('pointerdown', onPointer);
- window.addEventListener('pointermove', onPointer);
- window.addEventListener('pointerup', onPointer);
- });
-
- beforeEach(() => {
- pointerEvents.length = 0;
});
after(() => {
window.removeEventListener('click', onClick);
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('mousemove', onMouseMove);
- window.removeEventListener('pointerdown', onPointer);
- window.removeEventListener('pointermove', onPointer);
- window.removeEventListener('pointerup', onPointer);
});
describe('click/hover', () => {
@@ -215,16 +196,35 @@ describe('commands', () => {
});
describe('drag', () => {
- const getDragMoves = (events) => events.slice(1).filter(e => e.type === 'pointermove').map(e => ({ x: e.x, y: e.y }));
+ const allPointerEvents = [], dragMoveEvents = [];
+ let dragStarted = false;
+ function onPointer(e) {
+ allPointerEvents.push({ type: e.type, x: e.clientX, y: e.clientY });
+ if (e.type === 'pointerdown') dragStarted = true;
+ else if (e.type === 'pointerup') dragStarted = false;
+ else if (e.type === 'pointermove' && dragStarted) dragMoveEvents.push({ x: e.clientX, y: e.clientY });
+ }
beforeEach(async() => {
elem = await fixture(dragTemplate);
- pointerEvents.length = 0;
+
+ window.addEventListener('pointerdown', onPointer);
+ window.addEventListener('pointermove', onPointer);
+ window.addEventListener('pointerup', onPointer);
+ });
+
+ afterEach(() => {
+ allPointerEvents.length = 0;
+ dragMoveEvents.length = 0;
+
+ window.removeEventListener('pointerdown', onPointer);
+ window.removeEventListener('pointermove', onPointer);
+ window.removeEventListener('pointerup', onPointer);
});
it('should start dragging from the center of element and fire pointer events throughout the full drag flow', async() => {
await dragElemBy(elem, 20, 20);
- expect(pointerEvents).to.deep.equal([
+ expect(allPointerEvents).to.deep.equal([
{ type: 'pointermove', x: 100, y: 100 }, // Move to element center
{ type: 'pointerdown', x: 100, y: 100 }, // Start drag
{ type: 'pointermove', x: 110, y: 110 }, // Drag 10px
@@ -235,7 +235,7 @@ describe('commands', () => {
it('should move to the target offset in 10px increments to the max', async() => {
await dragElemBy(elem, 25, 0);
- expect(getDragMoves(pointerEvents)).to.deep.equal([
+ expect(dragMoveEvents).to.deep.equal([
{ x: 110, y: 100 },
{ x: 120, y: 100 },
{ x: 125, y: 100 },
@@ -244,7 +244,7 @@ describe('commands', () => {
it('should clamp the shorter axis while the longer axis keeps stepping', async() => {
await dragElemBy(elem, 30, 15);
- expect(getDragMoves(pointerEvents)).to.deep.equal([
+ expect(dragMoveEvents).to.deep.equal([
{ x: 110, y: 110 },
{ x: 120, y: 115 },
{ x: 130, y: 115 },
@@ -253,7 +253,7 @@ describe('commands', () => {
it('should drag in negative directions', async() => {
await dragElemBy(elem, -25, -25);
- expect(getDragMoves(pointerEvents)).to.deep.equal([
+ expect(dragMoveEvents).to.deep.equal([
{ x: 90, y: 90 },
{ x: 80, y: 80 },
{ x: 75, y: 75 },
From 7b0470c2e4c8171922bc0dde724993d24f2170c3 Mon Sep 17 00:00:00 2001
From: Stacey Van Herk <13419300+svanherk@users.noreply.github.com>
Date: Thu, 27 Aug 2026 12:06:20 -0400
Subject: [PATCH 3/4] Rename variable, adjust comment
---
src/browser/commands.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/browser/commands.js b/src/browser/commands.js
index dd7af392..37bb3d89 100644
--- a/src/browser/commands.js
+++ b/src/browser/commands.js
@@ -41,16 +41,16 @@ export async function dragDropElems(elem, toElem) {
}
export async function dragElemBy(elem, offsetX = 0, offsetY = 0) {
- const pixels = 10; // Mimic dragging by moving in 10px increments to the target position
- const numSteps = Math.ceil(Math.max(Math.abs(offsetX), Math.abs(offsetY)) / pixels);
+ const dragIncrementSize = 10; // To mimic real user dragging
+ const numSteps = Math.ceil(Math.max(Math.abs(offsetX), Math.abs(offsetY)) / dragIncrementSize);
const position = getElementPosition(elem);
await sendMouse({ type: 'move', position: [position.x, position.y] });
await sendMouse({ type: 'down' });
for (let i = 1; i <= numSteps; i++) {
- const dx = Math.sign(offsetX) * Math.min(Math.abs(offsetX), pixels * i);
- const dy = Math.sign(offsetY) * Math.min(Math.abs(offsetY), pixels * i);
+ const dx = Math.sign(offsetX) * Math.min(Math.abs(offsetX), dragIncrementSize * i);
+ const dy = Math.sign(offsetY) * Math.min(Math.abs(offsetY), dragIncrementSize * i);
await sendMouse({ type: 'move', position: [position.x + dx, position.y + dy] });
}
await sendMouse({ type: 'up' });
From 17539b633eaf2819d24bf8e4559f1d0f31491bea Mon Sep 17 00:00:00 2001
From: Stacey Van Herk <13419300+svanherk@users.noreply.github.com>
Date: Thu, 27 Aug 2026 12:26:16 -0400
Subject: [PATCH 4/4] Better test cleanup, add a few missing tests
---
test/browser/commands.test.js | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/test/browser/commands.test.js b/test/browser/commands.test.js
index abbdc6db..3cdbcd4f 100644
--- a/test/browser/commands.test.js
+++ b/test/browser/commands.test.js
@@ -214,12 +214,13 @@ describe('commands', () => {
});
afterEach(() => {
- allPointerEvents.length = 0;
- dragMoveEvents.length = 0;
-
window.removeEventListener('pointerdown', onPointer);
window.removeEventListener('pointermove', onPointer);
window.removeEventListener('pointerup', onPointer);
+
+ allPointerEvents.length = 0;
+ dragMoveEvents.length = 0;
+ dragStarted = false;
});
it('should start dragging from the center of element and fire pointer events throughout the full drag flow', async() => {
@@ -293,6 +294,8 @@ describe('commands', () => {
{ command: 'clickElem', action: (elem) => clickElem(elem) },
{ command: 'clickAt', action: () => clickAt(5, 10) },
{ command: 'clickElemAt', action: (elem) => clickElemAt(elem, 10, 10) },
+ { command: 'dragDropElems', action: (elem) => dragDropElems(elem, elem) },
+ { command: 'dragElemBy', action: (elem) => dragElemBy(elem, 10, 10) },
{ command: 'hoverElem', action: (elem) => hoverElem(elem) },
{ command: 'hoverAt', action: () => hoverAt(5, 10) },
{ command: 'hoverElemAt', action: (elem) => hoverElemAt(elem, 10, 10) },