Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dragDropElems (GAUD-6868) #468

Merged
merged 1 commit into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,33 @@ it('should click at coordinate', async() => {
});
```

Elements configured using the [Drag & Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) can be interacted with using `dragDropElems(sourceElem, destElem)`:

```javascript
import { dragDropElems } from '@brightspace-ui/testing';

it('should drag & drop', async() => {
const rootElem = await fixture(html`
<div>
<div id="source" draggable="true"></div>
<div id="dest"></div>
</div>
`);
// NOTE: destination must have "dragover" and "drop"
// event listeners added for the drag & drop API to function
await dragDropElems(
rootElem.querySelector('#source'),
rootElem.querySelector('#target')
);
// do assertions
});

it('should click at coordinate', async() => {
await clickAt(100, 200);
// do assertions
});
```

### Using the Keyboard

To place focus on an element using the keyboard, use `focusElem(elem)`. Doing so will trigger its `:focus-visible` CSS pseudo-class.
Expand Down
9 changes: 9 additions & 0 deletions src/browser/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ export async function clickElem(elem) {
return clickAt(position.x, position.y);
}

export async function dragDropElems(elem, toElem) {
const fromPosition = getElementPosition(elem);
const toPosition = getElementPosition(toElem);
await sendMouse({ type: 'move', position: [fromPosition.x, fromPosition.y] });
await sendMouse({ type: 'down' });
await sendMouse({ type: 'move', position: [toPosition.x, toPosition.y] });
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 });
Expand Down
2 changes: 1 addition & 1 deletion src/browser/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './vdiff.js';

export { assert, aTimeout, defineCE, expect, html, nextFrame, oneDefaultPreventedEvent, oneEvent, waitUntil } from '@open-wc/testing';
export { clickAt, clickElem, focusElem, hoverAt, hoverElem, sendKeys, sendKeysElem, setViewport } from './commands.js';
export { clickAt, clickElem, dragDropElems, focusElem, hoverAt, hoverElem, sendKeys, sendKeysElem, setViewport } from './commands.js';
export { fixture } from './fixture.js';
export { runConstructor } from './constructor.js';
172 changes: 110 additions & 62 deletions test/browser/commands.test.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,131 @@
import { clickAt, clickElem, expect, fixture, focusElem, hoverAt, hoverElem, sendKeys, sendKeysElem, setViewport } from '../../src/browser/index.js';
import { clickAt, clickElem, dragDropElems, expect, fixture, focusElem, hoverAt, hoverElem, sendKeys, sendKeysElem, setViewport } from '../../src/browser/index.js';
import { html } from 'lit';
import { spy } from 'sinon';

describe('commands', () => {

let elem;
const input = html`<input type="text">`;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed a more complex fixture template for drag & drop, so I refactored the tests here so that click/hover tests use a button, keyboard tests use this input and the drag & drop has its own. The tests themselves didn't change.

beforeEach(async() => {
elem = await fixture(input);
});
describe('click/hover', () => {

it('should click on element', async() => {
const clickSpy = spy();
elem.addEventListener('click', clickSpy);
await clickElem(elem);
expect(clickSpy).to.be.calledOnce;
});
let elem;
beforeEach(async() => {
elem = await fixture(html`<button>text</button>`);
});

it('should click at position', async() => {
const clickPos = { x: 0, y: 0 };
function onClick(e) {
clickPos.x = e.clientX;
clickPos.y = e.clientY;
}
window.addEventListener('click', onClick);
await clickAt(200, 300);
expect(clickPos.x).to.equal(200);
expect(clickPos.y).to.equal(300);
window.removeEventListener('click', onClick);
});
it('should click on element', async() => {
const clickSpy = spy();
elem.addEventListener('click', clickSpy);
await clickElem(elem);
expect(clickSpy).to.be.calledOnce;
});

it('should focus on element', async() => {
let focussed = false;
elem.addEventListener('focus', () => focussed = true);
await focusElem(elem);
expect(focussed).to.be.true;
});
it('should click at position', async() => {
const clickPos = { x: 0, y: 0 };
function onClick(e) {
clickPos.x = e.clientX;
clickPos.y = e.clientY;
}
window.addEventListener('click', onClick);
await clickAt(200, 300);
expect(clickPos.x).to.equal(200);
expect(clickPos.y).to.equal(300);
window.removeEventListener('click', onClick);
});

it('should hover over element', async() => {
let hovered = false;
elem.addEventListener('mouseover', () => hovered = true);
elem.addEventListener('mouseout', () => hovered = false);
await hoverElem(elem);
expect(hovered).to.be.true;
});
it('should hover over element', async() => {
let hovered = false;
elem.addEventListener('mouseover', () => hovered = true);
elem.addEventListener('mouseout', () => hovered = false);
await hoverElem(elem);
expect(hovered).to.be.true;
});

it('should hover at position', async() => {
const mousePos = { x: 0, y: 0 };
function onMouseMove(e) {
mousePos.x = e.clientX;
mousePos.y = e.clientY;
}
window.addEventListener('mousemove', onMouseMove);
await hoverAt(50, 100);
expect(mousePos.x).to.equal(50);
expect(mousePos.y).to.equal(100);
window.removeEventListener('mousemove', onMouseMove);
});

it('should hover at position', async() => {
const mousePos = { x: 0, y: 0 };
function onMouseMove(e) {
mousePos.x = e.clientX;
mousePos.y = e.clientY;
}
window.addEventListener('mousemove', onMouseMove);
await hoverAt(50, 100);
expect(mousePos.x).to.equal(50);
expect(mousePos.y).to.equal(100);
window.removeEventListener('mousemove', onMouseMove);
});

it('should send keys to element', async() => {
await sendKeysElem(elem, 'type', 'Hello');
expect(elem.value).to.equal('Hello');
describe('keyboard/focus', async() => {

let elem;
beforeEach(async() => {
elem = await fixture(html`<input type="text">`);
});

it('should focus on element', async() => {
let focussed = false;
elem.addEventListener('focus', () => focussed = true);
await focusElem(elem);
expect(focussed).to.be.true;
});

it('should send keys to element', async() => {
await sendKeysElem(elem, 'type', 'Hello');
expect(elem.value).to.equal('Hello');
});

it('should send keys to browser', async() => {
let key = undefined;
function onKeyDown(e) {
key = e.key;
}
window.addEventListener('keydown', onKeyDown);
await sendKeys('press', 'Escape');
expect(key).to.equal('Escape');
window.removeEventListener('keydown', onKeyDown);
});

});

it('should send keys to browser', async() => {
let key = undefined;
function onKeyDown(e) {
key = e.key;
}
window.addEventListener('keydown', onKeyDown);
await sendKeys('press', 'Escape');
expect(key).to.equal('Escape');
window.removeEventListener('keydown', onKeyDown);
describe('drag & drop', () => {

it('should drag & drop element', (done) => {
fixture(html`<div>
<div id="dest" style="height: 100px; width: 100px;"></div>
<div id="source" draggable="true" style="height: 50px; width: 50px;"></div>
</div>`).then(rootElem => {

let dragSource;
const sourceElem = rootElem.querySelector('#source');
sourceElem.addEventListener('dragstart', e => dragSource = e.target);

const destElem = rootElem.querySelector('#dest');
destElem.addEventListener('dragover', e => e.preventDefault());
destElem.addEventListener('drop', (e) => {
e.preventDefault();
expect(dragSource).to.equal(sourceElem);
done();
});

dragDropElems(sourceElem, destElem);

});
});

});

describe('mouseReset', () => {

const mousePos = { x: 0, y: 0 };
function onMouseMove(e) {
mousePos.x = e.clientX;
mousePos.y = e.clientY;
}

beforeEach(() => {
const buttonTemplate = html`<button>text</button>`;

let elem;
beforeEach(async() => {
elem = await fixture(buttonTemplate);
window.addEventListener('mousemove', onMouseMove);
});

Expand All @@ -99,7 +143,7 @@ describe('commands', () => {
await action(elem);
expect(mousePos.x).to.not.equal(0);
expect(mousePos.y).to.not.equal(0);
await fixture(input);
await fixture(buttonTemplate);
expect(mousePos.x).to.equal(0);
expect(mousePos.y).to.equal(0);
});
Expand All @@ -108,6 +152,10 @@ describe('commands', () => {

describe('viewport', () => {

beforeEach(async() => {
await fixture(html`<div></div>`);
});

it('should set width and height', async() => {
await setViewport({ height: 200, width: 300 });
expect(window.innerHeight).to.equal(200);
Expand Down