Skip to content

Commit 7810ff4

Browse files
author
Gabriel Grinberg
authored
Adds isDisplayed, scrollIntoView and hover functionality
2 parents 1a749f7 + 9bc352d commit 7810ff4

File tree

12 files changed

+163
-45
lines changed

12 files changed

+163
-45
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ package-lock.json
55
/*.js
66
/*.d.ts
77
!wallaby.js
8-
!protractor.conf.js
8+
!protractor.conf.js
9+
.idea

package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
22
"name": "unidriver",
3-
"version": "1.1.3",
3+
"version": "2.0.0",
44
"description": "",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",
77
"repository": {
8-
"type": "git",
9-
"url": "https://github.com/wix-incubator/unidriver"
8+
"type": "git",
9+
"url": "https://github.com/wix-incubator/unidriver"
1010
},
1111
"scripts": {
1212
"build": "rm -rf dist && (tsc -p . || true) && npm run build-testsuite && npm run expose-adapters",
1313
"build-testsuite": "cp src/test-suite/index.ejs dist/test-suite/index.ejs && browserify dist/test-suite/app.js > dist/test-suite/bundle.js",
14-
"testsuite-server": "node dist/test-suite/server.js",
14+
"testsuite-server": "node -e 'require(\"./dist/test-suite/server.js\").startServer(3000)'",
1515
"test": "bash support/test.sh",
1616
"expose-adapters": "import-path --path lib --dts"
1717
},
@@ -28,11 +28,9 @@
2828
"@types/ejs": "^2.5.0",
2929
"@types/express": "^4.11.0",
3030
"@types/mocha": "^2.2.46",
31-
"@types/protractor": "^4.0.0",
32-
"@types/puppeteer": "^0.13.9",
31+
"@types/puppeteer": "^1.11.1",
3332
"@types/react": "^16.0.34",
3433
"@types/react-dom": "^16.0.3",
35-
"@types/selenium-webdriver": "^3.0.8",
3634
"browserify": "^15.2.0",
3735
"chai": "^4.1.2",
3836
"chromedriver": "^2.38.3",
@@ -44,21 +42,23 @@
4442
"jsdom-global": "^3.0.2",
4543
"mocha": "^4.1.0",
4644
"mocha-teamcity-reporter": "^2.4.0",
47-
"puppeteer": "^1.3.0",
45+
"puppeteer": "^1.11.0",
4846
"react": "^15.2.0",
4947
"react-dom": "^15.2.0",
50-
"selenium-webdriver": "^4.0.0-alpha.1",
48+
"selenium-webdriver": "^3.6.0",
5149
"typescript": "^2.6.2"
5250
},
5351
"peerDependencies": {
54-
"@types/puppeteer": "^0.13.9",
55-
"@types/selenium-webdriver": "^3.0.8",
52+
"@types/puppeteer": "^1.11.1",
5653
"selenium-webdriver": "^3.6.0",
57-
"puppeteer": "^0.13.0",
54+
"puppeteer": "^1.11.0",
5855
"protractor": "^5.4.1",
5956
"react": "^15.2.0",
6057
"react-test-utils": "0.0.1",
6158
"react-dom": "^15.2.0"
6259
},
63-
"dependencies": {}
60+
"dependencies": {
61+
"protractor": "^5.4.2",
62+
"react-test-utils": "0.0.1"
63+
}
6464
}

src/lib/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ export type UniDriver<T = any> = {
2020
$$: (selector: Locator) => UniDriverList;
2121
text: () => Promise<string>;
2222
click: () => Promise<void>;
23+
hover: () => Promise<void>;
2324
value: () => Promise<string>;
2425
enterValue: (value: string) => Promise<void>;
2526
attr: (name: string) => Promise<string>;
2627
hasClass: (name: string) => Promise<boolean>;
2728
exists: () => Promise<boolean>;
29+
isDisplayed: () => Promise<boolean>;
2830
wait: (timeout?: number) => Promise<void>;
2931
type: string;
32+
scrollIntoView: () => Promise<{}>;
3033
getNative: () => Promise<T>;
3134
};

src/lib/protractor/index.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Locator, UniDriverList, UniDriver, MapFn} from '../';
2-
import {ElementFinder} from 'protractor';
3-
import {waitFor} from '../../utils';
2+
import {browser, ElementFinder} from 'protractor';
3+
import {waitFor} from '../../utils';
44

55
type ElementGetter = () => Promise<ElementFinder | null>;
66
type ElementsGetter = () => Promise<ElementFinder[]>;
@@ -82,11 +82,11 @@ export const protractorUniDriver = (
8282
// done
8383
$$: (selector: Locator) =>
8484
protractorUniDriverList(async () => {
85-
const elemement = await el();
86-
if (!elemement) {
85+
const element = await el();
86+
if (!element) {
8787
throw new Error(`Cannot find element`);
8888
}
89-
return elemement.$$(selector);
89+
return element.$$(selector);
9090
}),
9191
text: async () => {
9292
const text = await (await elem()).getAttribute('textContent');
@@ -95,6 +95,11 @@ export const protractorUniDriver = (
9595
click: async () => {
9696
return (await elem()).click();
9797
},
98+
hover: async () => {
99+
const e = await elem();
100+
101+
return (await e.browser_.actions().mouseMove(e).perform());
102+
},
98103
hasClass: async (className: string) => {
99104
const cm: any = await (await elem()).getAttribute('classList');
100105
return cm.indexOf(className) !== -1;
@@ -105,6 +110,10 @@ export const protractorUniDriver = (
105110
await e.type(value);
106111
},
107112
exists,
113+
isDisplayed: async () => {
114+
const e = await elem();
115+
return e.isDisplayed();
116+
},
108117
value: async () => {
109118
const value = await (await elem()).getAttribute('value');
110119
return value || '';
@@ -117,6 +126,11 @@ export const protractorUniDriver = (
117126
return waitFor(exists);
118127
},
119128
type: 'protractor',
129+
scrollIntoView: async () => {
130+
const el = await elem();
131+
132+
return browser.controlFlow().execute(() => browser.executeScript('arguments[0].scrollIntoView(true)', el.getWebElement()));
133+
},
120134
getNative: elem
121135
};
122136
};

src/lib/puppeteer/index.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,24 @@ export const pupUniDriver = (el: ElementGetter): UniDriver<ElementHandle> => {
9191
click: async () => {
9292
return (await elem()).click();
9393
},
94+
hover: async () => {
95+
return (await elem()).hover();
96+
},
9497
hasClass: async (className: string) => {
9598
const el = await elem();
9699
const cm: any = await el.getProperty('classList');
97100
return cm.indexOf(className) !== -1;
98101
},
99102
enterValue: async (value: string) => {
100103
const e = await elem();
101-
await e.focus()
104+
await e.focus();
102105
await e.type(value);
103106
},
104107
exists,
108+
isDisplayed: async () => {
109+
const e = await elem();
110+
return e.isIntersectingViewport();
111+
},
105112
value: async () => {
106113
const el = await elem();
107114
const valueHandle = await el.getProperty('value');
@@ -118,6 +125,12 @@ export const pupUniDriver = (el: ElementGetter): UniDriver<ElementHandle> => {
118125
return waitFor(exists);
119126
},
120127
type: 'puppeteer',
128+
scrollIntoView: async () => {
129+
const el = await elem();
130+
await el.hover();
131+
132+
return {};
133+
},
121134
getNative: elem
122135
};
123136
};

src/lib/puppeteer/spec.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
1-
import { runTestSuite } from '../../test-suite/spec';
1+
import { runTestSuite } from '../../test-suite/spec';
22
import { startServer, getUrl } from '../../test-suite/server';
3-
import * as puppeteer from 'puppeteer';
4-
import { goAndWait } from '../../examples/utils';
5-
import { pupUniDriver } from './index';
6-
import { TodoAppSetupFn } from '../../test-suite';
7-
import { Server } from 'http';
8-
import { Browser } from 'puppeteer';
3+
import * as puppeteer from 'puppeteer';
4+
import { pupUniDriver } from './index';
5+
import { TodoAppSetupFn } from '../../test-suite';
6+
import { Server } from 'http';
7+
import {Browser, Page} from 'puppeteer';
98

109
const port = 8082;
1110

1211
let server: Server;
1312
let browser: Browser;
13+
let page: Page;
1414

1515
const before = async () => {
1616
const args = process.env.CI ? ['--no-sandbox'] : [];
17-
const headless = process.env.CI ? true : false;
17+
const headless = !!process.env.CI;
1818
server = await startServer(port);
1919
browser = await puppeteer.launch({headless,args});
20+
page = await browser.newPage();
2021
};
2122

2223
const after = async () => {
2324
server.close();
25+
await page.close();
2426
await browser.close();
2527
};
2628

2729
const setup: TodoAppSetupFn = async (data) => {
2830

29-
const page = await goAndWait(browser, `http://localhost:${port}${getUrl(data)}`);
31+
await page.goto(`http://localhost:${port}${getUrl(data)}`);
3032
const driver = pupUniDriver(() => page.$('body'));
3133

32-
const tearDown = async () => {
33-
await page.close();
34-
};
34+
const tearDown = async () => { }
3535

3636
return {driver, tearDown};
3737
}
@@ -42,7 +42,7 @@ describe('puppeteer', () => {
4242
});
4343

4444

45-
describe('pupeeter specific tests', () => {
45+
describe('puppeteer specific tests', () => {
4646

4747

4848

src/lib/react/index.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UniDriverList, Locator, UniDriver } from '..';
2-
import { Simulate } from 'react-dom/test-utils';
3-
import { waitFor } from '../../utils';
2+
import { Simulate } from 'react-dom/test-utils';
3+
import { waitFor } from '../../utils';
44

55
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
66

@@ -93,7 +93,7 @@ export const reactUniDriver = (containerOrFn: ElementOrElementFinder): UniDriver
9393
// setting button 0 is now needed in React 16+ as it's not set by react anymore
9494
// 15 - https://github.com/facebook/react/blob/v15.6.1/src/renderers/dom/client/syntheticEvents/SyntheticMouseEvent.js#L45
9595
// 16 - https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticMouseEvent.js#L33
96-
96+
9797
// native events are preferred, but they will only work in React if the element is part of the DOM
9898
if (document.body.contains(el)) {
9999
const event: any = document.createEvent('HTMLEvents');
@@ -104,6 +104,23 @@ export const reactUniDriver = (containerOrFn: ElementOrElementFinder): UniDriver
104104
Simulate.click(el, {button: 0});
105105
}
106106
},
107+
hover: async () => {
108+
const el = await elem();
109+
110+
if (document.body.contains(el)) {
111+
const mouseenter: any = document.createEvent('HTMLEvents');
112+
const mouseover: any = document.createEvent('HTMLEvents');
113+
114+
mouseover.initEvent('mouseover', true, false);
115+
mouseenter.initEvent('mouseenter', true, false);
116+
117+
el.dispatchEvent(mouseenter);
118+
el.dispatchEvent(mouseover)
119+
} else {
120+
Simulate.mouseOver(el);
121+
Simulate.mouseEnter(el);
122+
}
123+
},
107124
hasClass: async (className: string) => (await elem()).classList.contains(className),
108125
enterValue: async (value: string) => {
109126
await wait(0);
@@ -115,10 +132,14 @@ export const reactUniDriver = (containerOrFn: ElementOrElementFinder): UniDriver
115132
return (await elem()).getAttribute(name) || '';
116133
},
117134
exists,
135+
isDisplayed: async () => {
136+
return true;
137+
},
118138
wait: async () => {
119139
return waitFor(exists);
120140
},
121141
type: 'react',
142+
scrollIntoView: async () => { return {} },
122143
getNative: () => elem()
123144
};
124145
};

src/lib/react/spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import * as ReactDOM from 'react-dom';
33
import { reactUniDriver } from './index';
44
import { TodoApp } from '../../test-suite/react-todoapp';
5-
import { TodoAppSetupFn } from '../../test-suite/index';
5+
import { TodoAppSetupFn } from '../../test-suite/';
66
import { runTestSuite } from '../../test-suite/spec';
77

88
const setup: TodoAppSetupFn = (data) => {

src/lib/selenium/index.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Locator, UniDriverList, UniDriver, MapFn } from '..';
2-
import { By, WebElement} from 'selenium-webdriver';
3-
import { waitFor } from '../../utils';
4-
2+
import { By, WebElement} from 'selenium-webdriver';
3+
import { waitFor } from '../../utils';
54

65
export type WebElementGetter = () => Promise<WebElement>;
76
export type WebElementsGetter = () => Promise<WebElement[]>;
@@ -85,6 +84,13 @@ export const seleniumUniDriver = (wep: WebElementGetter): UniDriver<WebElement>
8584
return el.getAttribute('value');
8685
},
8786
click: async () => (await elem()).click(),
87+
hover: async() => {
88+
const el = await elem();
89+
const driver = await el.getDriver();
90+
const actions = await (driver as any).actions({bridge: true});
91+
92+
return await actions.mouseMove({x:1, y:1, origin: el}).perform();
93+
},
8894
hasClass: async (className: string) => {
8995
const el = await elem();
9096
const cl = await el.getAttribute('class');
@@ -94,10 +100,33 @@ export const seleniumUniDriver = (wep: WebElementGetter): UniDriver<WebElement>
94100
(await elem()).sendKeys(value);
95101
},
96102
exists,
103+
isDisplayed: async () => {
104+
const el = await elem();
105+
106+
const retValue: boolean =
107+
await el.getDriver().executeScript(
108+
'const elem = arguments[0], ' +
109+
' box = elem.getBoundingClientRect(), ' +
110+
' cx = box.left + box.width / 2, ' +
111+
' cy = box.top + box.height / 2, ' +
112+
' e = document.elementFromPoint(cx, cy); ' +
113+
' for (; e; e = e.parentElement) { ' +
114+
' if ( e === elem) return true; ' +
115+
' } ' +
116+
'' +
117+
' return false;', el);
118+
return retValue;
119+
},
97120
wait: async () => {
98121
return waitFor(exists);
99122
},
100123
type: 'selenium',
124+
scrollIntoView: async () => {
125+
const el = await elem();
126+
127+
//return el.getDriver().executeScript(`window.scrollTo(${location.x}, ${location.y});`);
128+
return el.getDriver().executeScript('arguments[0].scrollIntoView();', el);
129+
},
101130
getNative: elem
102131
};
103132
};

0 commit comments

Comments
 (0)