Skip to content

Commit

Permalink
Merge branch 'master' into export-test-error-type
Browse files Browse the repository at this point in the history
  • Loading branch information
DudaGod authored Feb 21, 2024
2 parents 579bd17 + 467f589 commit 47917ba
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 29 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [8.1.0](https://github.com/gemini-testing/hermione/compare/v8.0.6...v8.1.0) (2024-02-20)


### Features

* add new command `moveCursorTo` which move cursro from left-top corener of the element


### Bug Fixes

* move cursor relative to the center of body when using "resetCursor" option ([36c4f36](https://github.com/gemini-testing/hermione/commit/36c4f367cf2e2a360dda5f7bb9bbceecdb188fcf))

### [8.0.6](https://github.com/gemini-testing/hermione/compare/v8.0.5...v8.0.6) (2024-02-12)


Expand Down
82 changes: 79 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ Hermione is a utility for integration testing of web pages using [WebdriverIO](h
- [Configuring .hermione.conf.js by yourself (a slow way)](#configuring-hermioneconfjs-by-yourself-a-slow-way)
- [Chrome Devtools Protocol](#chrome-devtools-protocol)
- [Webdriver protocol](#webdriver-protocol)
- [Commands API](#commands-api)
- [Browser commands](#browser-commands)
- [clearSession](#clearsession)
- [Element commands](#element-commands)
- [moveCursorTo](#movecursorto)
- [Tests API](#tests-api)
- [Arguments](#arguments)
- [Hooks](#hooks)
Expand All @@ -38,6 +43,7 @@ Hermione is a utility for integration testing of web pages using [WebdriverIO](h
- [Execution context](#execution-context)
- [AssertView](#assertview)
- [RunStep](#runstep)
- [OpenAndWait](#openandwait)
- [.hermione.conf.js](#hermioneconfjs)
- [sets](#sets)
- [browsers](#browsers)
Expand Down Expand Up @@ -74,6 +80,7 @@ Hermione is a utility for integration testing of web pages using [WebdriverIO](h
- [compareOpts](#compareopts)
- [buildDiffOpts](#builddiffopts)
- [assertViewOpts](#assertviewopts)
- [openAndWaitOpts](#openandwaitopts)
- [screenshotsDir](#screenshotsdir)
- [strictTestsOrder](#stricttestsorder)
- [compositeImage](#compositeimage)
Expand Down Expand Up @@ -108,10 +115,15 @@ Hermione is a utility for integration testing of web pages using [WebdriverIO](h
- [Reporters](#reporters)
- [Require modules](#require-modules)
- [Overriding settings](#overriding-settings)
- [Debug mode](#debug-mode)
- [REPL mode](#repl-mode)
- [switchToRepl](#switchtorepl)
- [Test development in runtime](#test-development-in-runtime)
- [How to set up using VSCode](#how-to-set-up-using-vscode)
- [How to set up using Webstorm](#how-to-set-up-using-webstorm)
- [Environment variables](#environment-variables)
- [HERMIONE_SKIP_BROWSERS](#hermione_skip_browsers)
- [HERMIONE_SETS](#hermione_sets)
- [Debug mode](#debug-mode)
- [Programmatic API](#programmatic-api)
- [config](#config)
- [events](#events)
Expand Down Expand Up @@ -389,12 +401,14 @@ node_modules/.bin/hermione

Since Hermione is based on [WebdriverIO v8](https://webdriver.io/docs/api/), all the commands provided by WebdriverIO are available in it. But Hermione also has her own commands.

### clearSession
### Browser commands

#### clearSession

Browser command that clears session state (deletes cookies, clears local and session storages). For example:

```js
it('', async ({ browser }) => {
it('test', async ({ browser }) => {
await browser.url('https://github.com/gemini-testing/hermione');

(await browser.getCookies()).length; // 5
Expand All @@ -409,6 +423,25 @@ it('', async ({ browser }) => {
});
```

### Element commands

#### moveCursorTo

> This command is temporary and will be removed in the next major (hermione@9). Differs from the standard [moveTo](https://webdriver.io/docs/api/element/moveTo/) in that it moves the cursor relative to the top-left corner of the element (like it was in hermione@7).
Move the mouse by an offset of the specified element. If offset is not specified then mouse will be moved to the top-left corder of the element.

Usage:

```typescript
await browser.$(selector).moveCursorTo({ xOffset, yOffset });
```

Available parameters:

* **xOffset** (optional) `Number` – X offset to move to, relative to the top-left corner of the element;
* **yOffset** (optional) `Number` – Y offset to move to, relative to the top-left corner of the element.

## Tests API

### Arguments
Expand Down Expand Up @@ -759,6 +792,39 @@ Parameters:

*Note: [html-reporter](https://github.com/gemini-testing/html-reporter) v9.7.7+ provides better history representation.*

### OpenAndWait

Command that allows to open page and wait until it loads (by combination of the specified factors). For example:

```js
it('some test', async ({browser}) => {
await browser.openAndWait('some/url', {
selector: ['.some', '.selector'],
predicate: () => document.isReady,
ignoreNetworkErrorsPatterns: ['https://mc.yandex.ru'],
waitNetworkIdle: true,
waitNetworkIdleTimeout: 500,
failOnNetworkError: true,
timeout: 20000,
});
});
```

In this example, page will be considered as loaded, if elements with selectors `.some` and `.selector` exists, `document.isReady` is `true`, it has been 500 ms since the last network request succeeded, and there were no errors, while trying to load images, fonts and stylesheets.

Parameters:

- url (required) `String` – page url
- waitOpts (optional) `Object`:
- selector (optional) `String|String[]` – Selector(s) to element(s), which should exist on the page.
- predicate (optional) `() => Promise<bool> | bool` – Predicate, which should return `true` if page is loaded. Predicate is being executed in the browser context: [waitUntil](https://webdriver.io/docs/api/element/waitUntil).
- waitNetworkIdle (optional) `Boolean` – Waits until all network requests are done. `true` by default. Only works in chrome browser or when using [Chrome Devtools Protocol](#chrome-devtools-protocol).
- waitNetworkIdleTimeout (optional) `Number` - Time (ms) after all network requests are resolved to consider network idle. 500 by default.
- failOnNetworkError (optional) `Boolean` – If `true`, throws an error when network requests are failed. `true` by default. Only works in chrome browser or when using [Chrome Devtools Protocol](#chrome-devtools-protocol).
- shouldThrowError (optional) `(match) => Boolean` - Predicate, which should return `true` on [Match](https://webdriver.io/docs/api/mock#match), if network error is considered critical for page load. By default, throws an error on image, stylesheet and font load error.
- ignoreNetworkErrorsPatterns (optional) `Array<String | RegExp>` - Array of url patterns to ignore network requests errors. Has a priority over `shouldThrowError`
- timeout (optional) `Number` - Page load timeout. [pageLoadTimeout](#pageloadtimeout) by default. Throws an error, if selectors are still not exist after timeout, or predicate is still resolving false.

## .hermione.conf.js
`hermione` is tuned using a configuration file. By default, it uses `.hermione.conf.js`, but you can use the `--config` option to specify a path to the configuration file.

Expand Down Expand Up @@ -864,6 +930,7 @@ Option name | Description
`compareOpts` | Options for comparing images.
`buildDiffOpts` | Options for building diff image.
`assertViewOpts` | Options for `assertView` command, used by default.
`openAndWaitOpts` | Options for `openAndWaitOpts` command, used by default
`screenshotsDir` | Directory to save reference images for command `assertView`. Default dir is `hermione/screens` which is relative to `process.cwd()`.
`strictTestsOrder` | `hermione` will guarantee tests order in [readTests](#readtests) results. `false` by default.
`compositeImage` | Allows testing of regions which bottom bounds are outside of a viewport height. In the resulting screenshot the area which fits the viewport bounds will be joined with the area which is outside of the viewport height. `true` by default.
Expand Down Expand Up @@ -1060,6 +1127,15 @@ Default options used when calling [assertView](https://github.com/gemini-testing
disableAnimation: true
```

#### openAndWaitOpts
Default options used when calling [openAndWait](https://github.com/gemini-testing/hermione/#openandwait), can be overriden by `openAndWait` options. Default values are:
```javascript
waitNetworkIdle: true,
waitNetworkIdleTimeout: 500,
failOnNetworkError: true,
ignoreNetworkErrorsPatterns: []
```

#### screenshotsDir

Directory to save reference images for command `assertView`. Default dir is `hermione/screens` which is relative to `process.cwd()`. The value of this option can also be a function which accepts one argument - an instance of a test within which comand `assertView` is called:
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hermione",
"version": "8.0.6",
"version": "8.1.0",
"description": "Tests framework based on mocha and wdio",
"main": "build/src/index.js",
"files": [
Expand Down
1 change: 1 addition & 0 deletions src/browser/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ module.exports = [
"scrollIntoView",
"openAndWait",
"switchToRepl",
"moveCursorTo",
];
34 changes: 34 additions & 0 deletions src/browser/commands/moveCursorTo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Browser } from "../types";

type MoveToOptions = {
xOffset?: number;
yOffset?: number;
};

// TODO: remove after next major version
export = async (browser: Browser): Promise<void> => {
const { publicAPI: session } = browser;

session.addCommand(
"moveCursorTo",
async function (this: WebdriverIO.Element, { xOffset = 0, yOffset = 0 }: MoveToOptions = {}): Promise<void> {
if (!this.isW3C) {
return this.moveToElement(this.elementId, xOffset, yOffset);
}

const { x, y, width, height } = await this.getElementRect(this.elementId);
const { scrollX, scrollY } = await session.execute(function (this: Window) {
return { scrollX: this.scrollX, scrollY: this.scrollY };
});

const newXOffset = Math.floor(x - scrollX + (typeof xOffset === "number" ? xOffset : width / 2));
const newYOffset = Math.floor(y - scrollY + (typeof yOffset === "number" ? yOffset : height / 2));

return session
.action("pointer", { parameters: { pointerType: "mouse" } })
.move({ x: newXOffset, y: newYOffset })
.perform();
},
true,
);
};
17 changes: 12 additions & 5 deletions src/worker/runner/test-runner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,18 @@ module.exports = class TestRunner {

await body.scrollIntoView();

const { x = 0, y = 0 } = await session.execute(function () {
return document.body.getBoundingClientRect();
});
// x and y must be integer, wdio will throw error otherwise
await body.moveTo({ xOffset: -Math.floor(x), yOffset: -Math.floor(y) });
if (!session.isW3C) {
const { x = 0, y = 0 } = await session.execute(function () {
return document.body.getBoundingClientRect();
});

return session.moveToElement(body.elementId, -Math.floor(x), -Math.floor(y));
}

await session
.action("pointer", { parameters: { pointerType: "mouse" } })
.move({ x: 0, y: 0 })
.perform();
}
};

Expand Down
80 changes: 62 additions & 18 deletions test/src/worker/runner/test-runner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,31 @@ describe("worker/runner/test-runner", () => {
const mkElement_ = proto => {
return _.defaults(proto, {
scrollIntoView: sandbox.stub().named("scrollIntoView").resolves(),
moveTo: sandbox.stub().named("moveTo").resolves(),
getSize: sandbox.stub().named("getSize").resolves({ width: 100, height: 500 }),
elementId: 100500,
});
};

const mkActionAPI_ = () => {
const actionStub = {};
actionStub.move = sandbox.stub().named("move").returns(actionStub);
actionStub.perform = sandbox.stub().named("perform").resolves();

return actionStub;
};

const mkBrowser_ = ({ prototype, config, id } = {}) => {
const actionStub = {};
actionStub.move = sandbox.stub().named("move").returns(actionStub);
actionStub.perform = sandbox.stub().named("perform").resolves();

const publicAPI = _.defaults(prototype, {
$: sandbox.stub().named("$").resolves(mkElement_()),
execute: sandbox.stub().named("execute").resolves({ x: 0, y: 0 }),
assertView: sandbox.stub().named("assertView").resolves(),
moveToElement: sandbox.stub().named("moveToElement").resolves(),
action: sandbox.stub().named("getSize").returns(mkActionAPI_()),
isW3C: true,
});
config = _.defaults(config, { resetCursor: true });

Expand Down Expand Up @@ -272,7 +288,7 @@ describe("worker/runner/test-runner", () => {
it('should not move cursor to position "0,0" on body element', async () => {
await run_();

assert.notCalled(body.moveTo);
assert.notCalled(browser.publicAPI.action);
});
});

Expand Down Expand Up @@ -309,32 +325,60 @@ describe("worker/runner/test-runner", () => {
assert.calledOnceWith(body.scrollIntoView);
});

it('should move cursor to position "0,0" on body element', async () => {
await run_();
describe("in jsonwp protocol", () => {
beforeEach(() => {
browser.publicAPI.isW3C = false;
});

assert.calledOnceWith(body.moveTo, { xOffset: 0, yOffset: 0 });
});
it("should scroll before moving cursor", async () => {
await run_();

it("should move cursor correctly if body element has negative coords", async () => {
browser.publicAPI.execute.resolves({ x: -100, y: -500 });
assert.callOrder(body.scrollIntoView, browser.publicAPI.moveToElement);
});

await run_();
it('should move cursor to position "0,0"', async () => {
browser.publicAPI.execute.resolves({ x: 5, y: 5 });
body.elementId = 12345;

assert.calledOnceWith(body.moveTo, { xOffset: 100, yOffset: 500 });
});
await run_();

it("should scroll before moving cursor", async () => {
await run_();
assert.calledOnceWith(browser.publicAPI.moveToElement, 12345, -5, -5);
});

assert.callOrder(body.scrollIntoView, body.moveTo);
it("should floor coords if body element has fractional coords", async () => {
browser.publicAPI.execute.resolves({ x: 10.123, y: 15.899 });
body.elementId = 12345;

await run_();

assert.calledOnceWith(browser.publicAPI.moveToElement, 12345, -10, -15);
});
});

it("should floor coords if body element has fractional coords", async () => {
browser.publicAPI.execute.resolves({ x: 10.123, y: 15.899 });
describe("in w3c protocol", () => {
beforeEach(() => {
browser.publicAPI.isW3C = true;
});

await run_();
it("should scroll before moving cursor", async () => {
await run_();

assert.calledOnceWith(body.moveTo, { xOffset: -10, yOffset: -15 });
assert.callOrder(body.scrollIntoView, browser.publicAPI.action);
});

it('should move cursor to position "0,0"', async () => {
const actionAPI = mkActionAPI_();
browser.publicAPI.action.returns(actionAPI);

await run_();

assert.calledOnceWith(browser.publicAPI.action, "pointer", {
parameters: { pointerType: "mouse" },
});
assert.calledOnceWith(actionAPI.move, { x: 0, y: 0 });
assert.calledOnce(actionAPI.perform);
assert.callOrder(browser.publicAPI.action, actionAPI.move, actionAPI.perform);
});
});
});
});
Expand Down

0 comments on commit 47917ba

Please sign in to comment.