Skip to content

Commit

Permalink
feat: add realActive() command (#50)
Browse files Browse the repository at this point in the history
* add realActive() command

* rename realActive to mouseDown

* mouseUp event

* Readme fixes

* Remove params jsdoc

* Rename to realMouseDown

Co-authored-by: Dmitriy Kovalenko <[email protected]>
  • Loading branch information
arjunpatel17 and dmtrKovalenko authored Apr 29, 2021
1 parent e095b94 commit c87d392
Show file tree
Hide file tree
Showing 7 changed files with 469 additions and 4 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ it("tests real events", () => {
cy.get("input").realClick(); // perform a native real click on the field
cy.realType("cypress real event"); // fires native system keypress events and fills the field
cy.realPress("Tab"); // native tab click switches the focus
cy.get("input").realMouseDown(); // perform a native mouse press on the field
cy.get("input").mouseUp(); // perform a native mouse release on the field
cy.focused().realHover(); // hovers over the new focused element
cy.contains("some text in the hovered popover");
});
Expand Down Expand Up @@ -80,6 +82,8 @@ Here is an overview of the available **real** event commands:
- [cy.realTouch](#cyrealtouch)
- [cy.realType](#cyrealtype)
- [cy.realSwipe](#cyrealswipe)
- [cy.realMouseDown](#cyrealMouseDown)
- [cy.mouseUp](#cymouseUp)

## cy.realClick

Expand Down Expand Up @@ -238,6 +242,48 @@ Options:
- `Optional` y coordinate to touch **y**: number
- `Optional` **touchPosition**: "topLeft" | "top" | "topRight" | "left" | "center" | "right" | "bottomLeft" | "bottom" | "bottomRight"

## cy.realMouseDown

Fires native system mouse down event.

```jsx
cy.get("button").realMouseDown();
cy.get("button").realMouseDown(options);
```

Example:

```js
cy.get("button").realMouseDown({ position: "topLeft" }) // click on the top left corner of button
```

Options:

- `Optional` **pointer**: \"mouse\" \| \"pen\"
- `Optional` **position**: "topLeft" | "top" | "topRight" | "left" | "center" | "right" | "bottomLeft" | "bottom" | "bottomRight"
- `Optional` **scrollBehavior**: "center" | "top" | "bottom" | "nearest" | false

## cy.mouseUp

Fires native system mouse down event.

```jsx
cy.get("button").mouseUp();
cy.get("button").mouseUp(options);
```

Example:

```js
cy.get("button").mouseUp({ position: "topLeft" }) // click on the top left corner of button
```

Options:

- `Optional` **pointer**: \"mouse\" \| \"pen\"
- `Optional` **position**: "topLeft" | "top" | "topRight" | "left" | "center" | "right" | "bottomLeft" | "bottom" | "bottomRight"
- `Optional` **scrollBehavior**: "center" | "top" | "bottom" | "nearest" | false

## UX

One problem of the real native system events I need to mention – you will not get an error message if the event wasn't produced. Similar to selenium or playwright – if a javascript event was not fired you will not get a comprehensive error message.
Expand Down
1 change: 1 addition & 0 deletions cypress/fixtures/frame-two.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<style>
#target { width: 100px; height: 100px; background: green; }
#target:hover { background: pink; }
#target:active { background: blue; }

* { box-sizing: border-box; }
html, body { height: 100%; }
Expand Down
290 changes: 290 additions & 0 deletions cypress/integration/mouse.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
describe("cy.realMouseDown and cy.mouseUp", () => {
beforeEach(() => {
cy.visit("https://example.cypress.io/commands/actions");
});

it("active state on the button", () => {
cy.get(".action-btn").should("have.css", "background-color", "rgb(217, 83, 79)");
cy.get(".action-btn").realMouseDown();
cy.get(".action-btn").should("have.css", "background-color", "rgb(172, 41, 37)");
cy.get(".action-btn").mouseUp(); // will go in hover state
cy.get(".action-btn").should("have.css", "background-color", "rgb(201, 48, 44)");
});

it("active/focused state on the text field", () => {
cy.get("#email1").realMouseDown().should("be.focused");
});

it("active state on different positions", () => {
cy.get(".action-btn")
.realMouseDown({ position: "topLeft" })
.mouseUp({ position: "topLeft" })
.realMouseDown({ position: "top" })
.mouseUp({ position: "top" })
.realMouseDown({ position: "topRight" })
.mouseUp({ position: "topRight" })
.realMouseDown({ position: "left" })
.mouseUp({ position: "left" })
.realMouseDown({ position: "center" })
.mouseUp({ position: "center" })
.realMouseDown({ position: "right" })
.mouseUp({ position: "right" })
.realMouseDown({ position: "bottomLeft" })
.mouseUp({ position: "bottomLeft" })
.realMouseDown({ position: "bottom" })
.mouseUp({ position: "bottom" })
.realMouseDown({ position: "bottomRight" })
.mouseUp({ position: "bottomRight" });
});

describe("realMouseDown scroll behavior", () => {
function getScreenEdges() {
const cypressAppWindow = window.parent.document.querySelector("iframe")
.contentWindow;
const windowTopEdge = cypressAppWindow.document.documentElement.scrollTop;
const windowBottomEdge = windowTopEdge + cypressAppWindow.innerHeight;
const windowCenter = windowTopEdge + cypressAppWindow.innerHeight / 2;

return {
top: windowTopEdge,
bottom: windowBottomEdge,
center: windowCenter,
};
}

function getElementEdges($el: JQuery) {
const $elTop = $el.offset().top;

return {
top: $elTop,
bottom: $elTop + $el.outerHeight(),
};
}

beforeEach(() => {
cy.window().scrollTo("top");
});

it("defaults to scrolling the element to the top of the viewport", () => {
cy.get("#action-canvas")
.realMouseDown()
.then(($canvas: JQuery) => {
const { top: $elTop } = getElementEdges($canvas);
const { top: screenTop } = getScreenEdges();

expect($elTop).to.equal(screenTop);
});
});

it("scrolls the element to center of viewport", () => {
cy.get("#action-canvas")
.realMouseDown({ scrollBehavior: "center" })
.then(($canvas: JQuery) => {
const { top: $elTop, bottom: $elBottom } = getElementEdges($canvas);
const { top: screenTop, bottom: screenBottom } = getScreenEdges();

const screenCenter = screenTop + (screenBottom - screenTop) / 2;

expect($elTop).to.equal(screenCenter - $canvas.outerHeight() / 2);
expect($elBottom).to.equal(screenCenter + $canvas.outerHeight() / 2);
});
});

it("scrolls the element to the top of the viewport", () => {
cy.get("#action-canvas")
.realMouseDown({ scrollBehavior: "top" })
.then(($canvas: JQuery) => {
const { top: $elTop } = getElementEdges($canvas);
const { top: screenTop } = getScreenEdges();

expect($elTop).to.equal(screenTop);
});
});

it("scrolls the element to the bottom of the viewport", () => {
cy.get("#action-canvas")
.realMouseDown({ scrollBehavior: "bottom" })
.then(($canvas: JQuery) => {
const { bottom: $elBottom } = getElementEdges($canvas);
const { bottom: screenBottom } = getScreenEdges();

expect($elBottom).to.equal(screenBottom);
});
});

it("scrolls the element to the nearest edge of the viewport", () => {
cy.window().scrollTo("bottom");

cy.get("#action-canvas")
.realMouseDown({ scrollBehavior: "nearest" })
.then(($canvas: JQuery) => {
const { top: $elTop } = getElementEdges($canvas);
const { top: screenTop } = getScreenEdges();

expect($elTop).to.equal(screenTop);
});

cy.window().scrollTo("top");

cy.get("#action-canvas")
.realMouseDown({ scrollBehavior: "nearest" })
.then(($canvas: JQuery) => {
const { bottom: $elBottom } = getElementEdges($canvas);
const { bottom: screenBottom } = getScreenEdges();

expect($elBottom).to.equal(screenBottom);
});
});
});

describe("mouseUp scroll behavior", () => {
function getScreenEdges() {
const cypressAppWindow = window.parent.document.querySelector("iframe")
.contentWindow;
const windowTopEdge = cypressAppWindow.document.documentElement.scrollTop;
const windowBottomEdge = windowTopEdge + cypressAppWindow.innerHeight;
const windowCenter = windowTopEdge + cypressAppWindow.innerHeight / 2;

return {
top: windowTopEdge,
bottom: windowBottomEdge,
center: windowCenter,
};
}

function getElementEdges($el: JQuery) {
const $elTop = $el.offset().top;

return {
top: $elTop,
bottom: $elTop + $el.outerHeight(),
};
}

beforeEach(() => {
cy.window().scrollTo("top");
});

it("defaults to scrolling the element to the top of the viewport", () => {
cy.get("#action-canvas")
.mouseUp()
.then(($canvas: JQuery) => {
const { top: $elTop } = getElementEdges($canvas);
const { top: screenTop } = getScreenEdges();

expect($elTop).to.equal(screenTop);
});
});

it("scrolls the element to center of viewport", () => {
cy.get("#action-canvas")
.mouseUp({ scrollBehavior: "center" })
.then(($canvas: JQuery) => {
const { top: $elTop, bottom: $elBottom } = getElementEdges($canvas);
const { top: screenTop, bottom: screenBottom } = getScreenEdges();

const screenCenter = screenTop + (screenBottom - screenTop) / 2;

expect($elTop).to.equal(screenCenter - $canvas.outerHeight() / 2);
expect($elBottom).to.equal(screenCenter + $canvas.outerHeight() / 2);
});
});

it("scrolls the element to the top of the viewport", () => {
cy.get("#action-canvas")
.mouseUp({ scrollBehavior: "top" })
.then(($canvas: JQuery) => {
const { top: $elTop } = getElementEdges($canvas);
const { top: screenTop } = getScreenEdges();

expect($elTop).to.equal(screenTop);
});
});

it("scrolls the element to the bottom of the viewport", () => {
cy.get("#action-canvas")
.mouseUp({ scrollBehavior: "bottom" })
.then(($canvas: JQuery) => {
const { bottom: $elBottom } = getElementEdges($canvas);
const { bottom: screenBottom } = getScreenEdges();

expect($elBottom).to.equal(screenBottom);
});
});

it("scrolls the element to the nearest edge of the viewport", () => {
cy.window().scrollTo("bottom");

cy.get("#action-canvas")
.mouseUp({ scrollBehavior: "nearest" })
.then(($canvas: JQuery) => {
const { top: $elTop } = getElementEdges($canvas);
const { top: screenTop } = getScreenEdges();

expect($elTop).to.equal(screenTop);
});

cy.window().scrollTo("top");

cy.get("#action-canvas")
.mouseUp({ scrollBehavior: "nearest" })
.then(($canvas: JQuery) => {
const { bottom: $elBottom } = getElementEdges($canvas);
const { bottom: screenBottom } = getScreenEdges();

expect($elBottom).to.equal(screenBottom);
});
});
});
});

describe('realMouseDown and mouseUp iframe behavior', () => {
beforeEach(() => {
cy.visit('./cypress/fixtures/iframe-page.html');
});

it('sets elements inside iframes to active state', () => {
cy.get('iframe').then(($firstIframe) => {
return cy.wrap($firstIframe.contents().find('iframe'));
}).then(($secondIframe) => {
return cy.wrap($secondIframe.contents().find('body'));
}).within(() => {
cy.get('#target').then(($target) => {
expect($target.css('background-color')).to.equal('rgb(0, 128, 0)');
});

cy.get('#target').realMouseDown().then(($target) => {
expect($target.css('background-color')).to.equal('rgb(0, 0, 255)');
});

// will go in hover state
cy.get('#target').mouseUp().then(($target) => {
expect($target.css('background-color')).to.equal('rgb(255, 192, 203)');
});
});
});

it('sets elements inside transformed iframes to active states', () => {
cy.get('iframe').then(($firstIframe) => {
$firstIframe.css('transform', 'scale(.5)');
return cy.wrap($firstIframe.contents().find('iframe'));
}).then(($secondIframe) => {
$secondIframe.css('transform', 'scale(.75)');
return cy.wrap($secondIframe.contents().find('body'));
}).within(() => {
cy.get('#target').then(($target) => {
expect($target.css('background-color')).to.equal('rgb(0, 128, 0)');
});

cy.get('#target').realMouseDown().then(($target) => {
expect($target.css('background-color')).to.equal('rgb(0, 0, 255)');
});

// will go in hover state
cy.get('#target').mouseUp().then(($target) => {
expect($target.css('background-color')).to.equal('rgb(255, 192, 203)');
});
});
});
});

Loading

0 comments on commit c87d392

Please sign in to comment.