sefiutils 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/fiberUtils.d.ts +13 -0
- package/lib/fiberUtils.d.ts.map +1 -0
- package/lib/fiberUtils.js +60 -0
- package/lib/fiberUtils.js.map +1 -0
- package/lib/globalSetup.d.ts +22 -0
- package/lib/globalSetup.d.ts.map +1 -0
- package/lib/globalSetup.js +88 -0
- package/lib/globalSetup.js.map +1 -0
- package/{src/globalTeardown.ts → lib/globalTeardown.d.ts} +5 -12
- package/lib/globalTeardown.d.ts.map +1 -0
- package/lib/globalTeardown.js +34 -0
- package/lib/globalTeardown.js.map +1 -0
- package/lib/localSetup.d.ts +4 -0
- package/lib/localSetup.d.ts.map +1 -0
- package/lib/localSetup.js +139 -0
- package/lib/localSetup.js.map +1 -0
- package/lib/localTeardown.d.ts +6 -0
- package/lib/localTeardown.d.ts.map +1 -0
- package/lib/localTeardown.js +28 -0
- package/lib/localTeardown.js.map +1 -0
- package/lib/seUtils.d.ts +127 -0
- package/lib/seUtils.d.ts.map +1 -0
- package/lib/seUtils.js +510 -0
- package/lib/seUtils.js.map +1 -0
- package/package.json +4 -1
- package/.gitlab-ci.yml +0 -20
- package/jest.config.js +0 -11
- package/logo.graffle +0 -0
- package/logo.png +0 -0
- package/src/fiberUtils.ts +0 -60
- package/src/globalSetup.ts +0 -62
- package/src/localSetup.ts +0 -100
- package/src/localTeardown.ts +0 -14
- package/src/seUtils.ts +0 -448
- package/tests/tsconfig.json +0 -7
- package/tsconfig.json +0 -102
- /package/{src → lib}/execScripts/dom.js +0 -0
- /package/{src → lib}/execScripts/fiber.js +0 -0
package/src/globalSetup.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
declare global {
|
|
2
|
-
var server: any; // only loaded in remote case; this global variable is only visible in globalTeardown.ts
|
|
3
|
-
// and not in the tests, cf. https://jestjs.io/docs/configuration#globalsetup-string
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* This function is called by Jest once before all tests are executed.
|
|
7
|
-
* In order to get called, it must be referenced in the jest configuration file:
|
|
8
|
-
* ```
|
|
9
|
-
* "globalSetup": "<rootDir>/tests/globalSetup.ts"
|
|
10
|
-
* ```
|
|
11
|
-
*
|
|
12
|
-
* **Important Note**: "Any global variables that are defined through globalSetup can only be
|
|
13
|
-
* read in globalTeardown. You cannot retrieve globals defined here in your test suites."
|
|
14
|
-
* [Jest Documentation](https://jestjs.io/docs/configuration#globalsetup-string)
|
|
15
|
-
*
|
|
16
|
-
* We only start the selenium server here if we are running in remote mode, which is the default.
|
|
17
|
-
* The mode can configured by setting the environment variable BROWSER to "chrome", "firefox" or "remote".
|
|
18
|
-
*
|
|
19
|
-
* Do not forget a global teardown function, which is called after all tests are executed,
|
|
20
|
-
* cf. globalTeardown.ts
|
|
21
|
-
*/
|
|
22
|
-
export default async function setGlobalObjects() {
|
|
23
|
-
const browser = (process.env.BROWSER as "chrome" | "firefox" | "remote") || "remote";
|
|
24
|
-
|
|
25
|
-
// console.log(`Using browser: ${browser}`);
|
|
26
|
-
try {
|
|
27
|
-
if (browser === "remote") {
|
|
28
|
-
await setUpSeleniumServer();
|
|
29
|
-
}
|
|
30
|
-
} catch (err) {
|
|
31
|
-
console.error(`Error setting up server: ${err}`);
|
|
32
|
-
throw err;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
async function setUpSeleniumServer() {
|
|
38
|
-
|
|
39
|
-
const remote = await import("selenium-webdriver/remote");
|
|
40
|
-
//@ts-ignore
|
|
41
|
-
const jar = await import('selenium-server-standalone-jar');
|
|
42
|
-
|
|
43
|
-
if (globalThis.server) {
|
|
44
|
-
await globalThis.server.kill();
|
|
45
|
-
console.log(`Server killed before starting again`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// console.log(`Load selenium server from ${jar.path}`);
|
|
49
|
-
const s = new remote.SeleniumServer(jar.path, {
|
|
50
|
-
port: 4444,
|
|
51
|
-
env: {
|
|
52
|
-
...process.env,
|
|
53
|
-
"SE_NODE_MAX_SESSIONS": "4",
|
|
54
|
-
"SE_NODE_OVERRIDE_MAX_SESSIONS": "true",
|
|
55
|
-
// "JAVA_OPTS": "-XX:ActiveProcessorCount=4"
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
const address = await s.start(3000);
|
|
59
|
-
globalThis.server = s;
|
|
60
|
-
// const address = await globalThis.server.address()
|
|
61
|
-
// console.log(`Selenium server started at address: ${address}`);
|
|
62
|
-
}
|
package/src/localSetup.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { Builder, Capabilities, WebDriver } from "selenium-webdriver";
|
|
2
|
-
import { setBrowserWindowSize, setViewPortSize } from "./seUtils";
|
|
3
|
-
|
|
4
|
-
export const LOCAL_SETUP_TIMEOUT = 20000;
|
|
5
|
-
|
|
6
|
-
export async function localSetup(page: string, width = 500, height = 500): Promise<WebDriver> {
|
|
7
|
-
const driver = await getWebDriver();
|
|
8
|
-
console.log(`Load page: ${page}`);
|
|
9
|
-
await driver.get(page);
|
|
10
|
-
await setBrowserWindowSize(driver, width, height);
|
|
11
|
-
return driver;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* To be configured in jest setup file via globalSetup as it needs to be run before any tests in all files.
|
|
16
|
-
* Additionally,
|
|
17
|
-
* ```
|
|
18
|
-
* await driver.get(page);
|
|
19
|
-
* await setViewPortSize(500, 500);
|
|
20
|
-
* ```
|
|
21
|
-
* are to be called.
|
|
22
|
-
*/
|
|
23
|
-
async function getWebDriver() {
|
|
24
|
-
const browser = (process.env.BROWSER as "chrome" | "firefox" | "remote") || "remote";
|
|
25
|
-
|
|
26
|
-
// console.log(`Using browser: ${browser}`);
|
|
27
|
-
try {
|
|
28
|
-
if (browser === "chrome") {
|
|
29
|
-
return await setUpWebDriverChrome();
|
|
30
|
-
} else if (browser === "firefox") {
|
|
31
|
-
return await setUpWebDriverFirefox();
|
|
32
|
-
} else if (browser === "remote") {
|
|
33
|
-
return await setUpWebDriverInDocker();
|
|
34
|
-
} else {
|
|
35
|
-
throw new Error(`Unknown browser: ${browser}`);
|
|
36
|
-
}
|
|
37
|
-
} catch (err) {
|
|
38
|
-
console.error(`Error setting up driver: ${err}`);
|
|
39
|
-
throw err;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
async function setUpWebDriverInDocker() {
|
|
45
|
-
|
|
46
|
-
const firefox = await import('selenium-webdriver/firefox');
|
|
47
|
-
|
|
48
|
-
let address = "http://localhost:4444/wd/hub";
|
|
49
|
-
let start = Date.now();
|
|
50
|
-
const driver = await new Builder()
|
|
51
|
-
.usingServer(address)
|
|
52
|
-
.withCapabilities(Capabilities.firefox())
|
|
53
|
-
.setFirefoxOptions(new firefox.Options().headless())
|
|
54
|
-
.build();
|
|
55
|
-
let duration = (Date.now() - start) / 1000;
|
|
56
|
-
// console.log(`Driver resolved and connected to ${address}, required ${duration}s.`);
|
|
57
|
-
return driver;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* To be called in the beforeAll() function of a test suite.
|
|
63
|
-
* Additionally,
|
|
64
|
-
* ```
|
|
65
|
-
* await driver.get(page);
|
|
66
|
-
* await setViewPortSize(500, 500);
|
|
67
|
-
* ```
|
|
68
|
-
* are to be called.
|
|
69
|
-
*/
|
|
70
|
-
async function setUpWebDriverChrome() {
|
|
71
|
-
//@ts-ignore
|
|
72
|
-
await import('chromedriver');
|
|
73
|
-
const chrome = await import('selenium-webdriver/chrome');
|
|
74
|
-
const driver = await new Builder()
|
|
75
|
-
.forBrowser('chrome')
|
|
76
|
-
.setChromeOptions(new chrome.Options().headless())
|
|
77
|
-
.build();
|
|
78
|
-
return driver;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* To be called in the beforeAll() function of a test suite.
|
|
83
|
-
* Additionally,
|
|
84
|
-
* ```
|
|
85
|
-
* await driver.get(page);
|
|
86
|
-
* await setViewPortSize(500, 500);
|
|
87
|
-
* ```
|
|
88
|
-
* are to be called.
|
|
89
|
-
*/
|
|
90
|
-
async function setUpWebDriverFirefox() {
|
|
91
|
-
//@ts-ignore
|
|
92
|
-
await import('geckodriver'); // installed geckodriver will cause remote configuration to fail in docker...
|
|
93
|
-
const firefox = await import('selenium-webdriver/firefox');
|
|
94
|
-
const driver = await new Builder()
|
|
95
|
-
.forBrowser('firefox')
|
|
96
|
-
.setFirefoxOptions(new firefox.Options().headless())
|
|
97
|
-
.build();
|
|
98
|
-
|
|
99
|
-
return driver;
|
|
100
|
-
}
|
package/src/localTeardown.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { WebDriver } from "selenium-webdriver";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* To be called in the afterAll() function of a test suite.
|
|
5
|
-
*/
|
|
6
|
-
export async function localTeardown(driver: WebDriver) {
|
|
7
|
-
if (driver) {
|
|
8
|
-
await driver.quit();
|
|
9
|
-
// console.log(`Driver quit`);
|
|
10
|
-
} else {
|
|
11
|
-
throw new Error("Driver not started yet");
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
}
|
package/src/seUtils.ts
DELETED
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* (C) Jens von Pilgrim, 2022
|
|
3
|
-
*/
|
|
4
|
-
import * as fs from "fs";
|
|
5
|
-
import * as fsPromises from "fs/promises";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import { By, Locator, WebDriver, WebElement } from 'selenium-webdriver';
|
|
8
|
-
|
|
9
|
-
export async function execScript<T>(driver: WebDriver, scriptName: string, fct: string): Promise<T> {
|
|
10
|
-
const scriptPath = path.join(path.dirname(__filename), scriptName);
|
|
11
|
-
const script = fs.readFileSync(scriptPath, "utf-8");
|
|
12
|
-
const res = await driver.executeScript(script + `
|
|
13
|
-
return ${fct}();
|
|
14
|
-
`);
|
|
15
|
-
return res as T;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface Bounds {
|
|
19
|
-
top: number;
|
|
20
|
-
left: number;
|
|
21
|
-
bottom: number;
|
|
22
|
-
right: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export function boundsToString(bounds: Bounds) {
|
|
27
|
-
return `(${bounds.left};${bounds.top})x(${bounds.right};${bounds.bottom})`
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function toFloor(bounds: Bounds[]) {
|
|
31
|
-
const floors: Bounds[] = [];
|
|
32
|
-
for (const b of bounds) {
|
|
33
|
-
floors.push({
|
|
34
|
-
top: Math.floor(b.top),
|
|
35
|
-
bottom: Math.floor(b.bottom),
|
|
36
|
-
left: Math.floor(b.left),
|
|
37
|
-
right: Math.floor(b.right)
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
return floors;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Coordinates are sometimes floats. We compare two numbers with a delta of 1 to ignore rounding problems.
|
|
45
|
-
*/
|
|
46
|
-
export function circa(n1: number, n2: number) {
|
|
47
|
-
const delta = n1 - n2;
|
|
48
|
-
return Math.abs(delta) < 1;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export async function waitFor<T>(callback: () => Promise<T>, timeout=1000, intervalTime=100): Promise<T> {
|
|
54
|
-
// try first time
|
|
55
|
-
const start = new Date().getTime();
|
|
56
|
-
try {
|
|
57
|
-
const result = await callback();
|
|
58
|
-
return result;
|
|
59
|
-
} catch (error) {
|
|
60
|
-
// ignore error
|
|
61
|
-
}
|
|
62
|
-
// if error, try until timeout ms have passed
|
|
63
|
-
return new Promise<T>((resolve, reject) => {
|
|
64
|
-
const interval = setInterval(async () => {
|
|
65
|
-
try {
|
|
66
|
-
const result = await callback();
|
|
67
|
-
clearInterval(interval);
|
|
68
|
-
resolve(result);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
const duration = new Date().getTime() - start;
|
|
71
|
-
if (duration > timeout) {
|
|
72
|
-
clearInterval(interval);
|
|
73
|
-
reject(error);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}, intervalTime);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Takes screenshot of current window and saves it to the given file.
|
|
83
|
-
* The folder is created if it does not yet exists.
|
|
84
|
-
*/
|
|
85
|
-
export async function saveScreenshot(driver: WebDriver, fileName: string): Promise<void> {
|
|
86
|
-
const pngBase64 = await driver.takeScreenshot();
|
|
87
|
-
const dirName = path.dirname(fileName);
|
|
88
|
-
if (!fs.existsSync(dirName)) {
|
|
89
|
-
await fsPromises.mkdir(dirName, { recursive: true });
|
|
90
|
-
}
|
|
91
|
-
await fsPromises.writeFile(fileName, pngBase64, 'base64');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Helper function for printing out an element (tag, id if present)
|
|
98
|
-
*/
|
|
99
|
-
export async function describe(we: WebElement) {
|
|
100
|
-
let s = await we.getTagName();
|
|
101
|
-
const id = await we.getAttribute("id");
|
|
102
|
-
if (id) {
|
|
103
|
-
s += "#" + id;
|
|
104
|
-
} else {
|
|
105
|
-
const clazz = await we.getAttribute("class");
|
|
106
|
-
if (clazz) {
|
|
107
|
-
s += "." + clazz;
|
|
108
|
-
}
|
|
109
|
-
let text = await we.getText();
|
|
110
|
-
s += ` ("${text.substring(0, 7)}${text.length > 7 ? '…' : ''})"`;
|
|
111
|
-
}
|
|
112
|
-
return s;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Gets the element by means of `WebElement` by its id attribute.
|
|
117
|
-
*/
|
|
118
|
-
export async function getElementById(driver: WebDriver, id: string): Promise<WebElement> {
|
|
119
|
-
const el = await driver.findElement(By.id(id));
|
|
120
|
-
return el;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Gets the first element by means of `WebElement` by its class attribute.
|
|
125
|
-
*/
|
|
126
|
-
export async function getElementByClass(driver: WebDriver, clazz: string): Promise<WebElement> {
|
|
127
|
-
const el = await driver.findElement(By.className(clazz));
|
|
128
|
-
return el;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Gets the first element by means of `WebElement` by its tag name.
|
|
133
|
-
*/
|
|
134
|
-
export async function getElementByTag(driver: WebDriver, tagName: string): Promise<WebElement> {
|
|
135
|
-
const el = await driver.findElement(By.css(tagName));
|
|
136
|
-
return el;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Returns first element containing given text or undefined.
|
|
141
|
-
*
|
|
142
|
-
* Test in browser console: `$x("//*[contains(text(), 'Hello')]")`
|
|
143
|
-
*/
|
|
144
|
-
export async function getElementContainingText(driver: WebDriver, text: string): Promise<WebElement|undefined> {
|
|
145
|
-
try {
|
|
146
|
-
// cf. https://stackoverflow.com/questions/3655549/xpath-containstext-some-string-doesnt-work-when-used-with-node-with-more
|
|
147
|
-
//const el = await driver.findElement(By.xpath(`//*[contains(text(), "${text}")]`));
|
|
148
|
-
const el = await driver.findElement(By.xpath(`//*[text()[contains(., '${text}')]]`));
|
|
149
|
-
return el;
|
|
150
|
-
} catch (error) {
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Returns the bounding client rect (the bounding box relative to the viewport)
|
|
158
|
-
* of an element (found by its is attribute) by means of a JavaScript call.
|
|
159
|
-
*/
|
|
160
|
-
export async function getBoundingClientRect(driver: WebDriver, id: string): Promise<DOMRect> {
|
|
161
|
-
const bcr = await driver.executeScript('return document.getElementById("' + id + '").getBoundingClientRect()');
|
|
162
|
-
return bcr as DOMRect;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Returns all {@link getBoundingClientRect} values of the elements
|
|
167
|
-
* found in the given element list.
|
|
168
|
-
* The element list is computed by means of JavaScript code,
|
|
169
|
-
* e.g. `document.getElementsByClassName('grid').item(0).children`.
|
|
170
|
-
*
|
|
171
|
-
* @param elementList JavaScript code to determine the element list
|
|
172
|
-
*/
|
|
173
|
-
export async function getBoundingClientRects(driver: WebDriver, jsElementList: string): Promise<DOMRect[]> {
|
|
174
|
-
const bcr = await driver.executeScript(
|
|
175
|
-
`return (()=>{
|
|
176
|
-
const elements = ${jsElementList};
|
|
177
|
-
const bounds=[];
|
|
178
|
-
for (let el of elements) { bounds.push(el.getBoundingClientRect()) }
|
|
179
|
-
return bounds;
|
|
180
|
-
})()`) as DOMRect[];
|
|
181
|
-
return bcr;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Returns the rects using the driver API.
|
|
186
|
-
*/
|
|
187
|
-
export async function getRects(driver: WebDriver, locator: Locator): Promise<Bounds[]> {
|
|
188
|
-
const elements = await driver.findElements(locator);
|
|
189
|
-
const bounds: Bounds[] = [];
|
|
190
|
-
for (const element of elements) {
|
|
191
|
-
const rect = await element.getRect();
|
|
192
|
-
bounds.push({
|
|
193
|
-
top: rect.x, left: rect.y,
|
|
194
|
-
bottom: rect.x + rect.width, right: rect.y + rect.height
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
return bounds;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Returns the rects of the first child (of each found element) using the driver API.
|
|
202
|
-
*/
|
|
203
|
-
export async function getRectsOfContent(driver: WebDriver, locator: Locator): Promise<Bounds[]> {
|
|
204
|
-
const elements = await driver.findElements(locator);
|
|
205
|
-
const bounds: Bounds[] = [];
|
|
206
|
-
for (const element of elements) {
|
|
207
|
-
const rect = await element.getRect();
|
|
208
|
-
bounds.push({
|
|
209
|
-
top: rect.x, left: rect.y,
|
|
210
|
-
bottom: rect.x + rect.width, right: rect.y + rect.height
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
return bounds;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Returns all text content of the elements
|
|
221
|
-
* found in the given element list.
|
|
222
|
-
* The elements list is computed by means of JavaScript code,
|
|
223
|
-
* e.g. `document.getElementsByClassName('grid').item(0).children`.
|
|
224
|
-
*
|
|
225
|
-
* This is useful in combination with {@link getBoundingClientRects} in
|
|
226
|
-
* order to describe the items.
|
|
227
|
-
*
|
|
228
|
-
* @param elementList JavaScript code to determine the element list
|
|
229
|
-
*/
|
|
230
|
-
export async function getTextContents(driver: WebDriver, jsElementList: string): Promise<string[]> {
|
|
231
|
-
const tcs = await driver.executeScript(
|
|
232
|
-
`const elements = ${jsElementList};
|
|
233
|
-
const tcs=[];
|
|
234
|
-
for (let el of elements) { tcs.push(el.textContent) }
|
|
235
|
-
return tcs;
|
|
236
|
-
`) as string[];
|
|
237
|
-
return tcs;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Returns all computed styles of the elements
|
|
242
|
-
* found in the given element list.
|
|
243
|
-
* The elements list is computed by means of JavaScript code,
|
|
244
|
-
* e.g. `document.getElementsByClassName('grid').item(0).children`.
|
|
245
|
-
*
|
|
246
|
-
* @param elementList JavaScript code to determine the element list
|
|
247
|
-
*/
|
|
248
|
-
export async function getComputedStyles(driver: WebDriver, jsElementList: string, style: string): Promise<string[]> {
|
|
249
|
-
const tcs = await driver.executeScript(
|
|
250
|
-
`const elements = ${jsElementList};
|
|
251
|
-
const tcs=[];
|
|
252
|
-
for (let el of elements) { tcs.push(getComputedStyle(el).${style}) }
|
|
253
|
-
return tcs;
|
|
254
|
-
`) as string[];
|
|
255
|
-
return tcs;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Returns the inner size of the windows bounding client rect (the bounding box relative to the viewport)
|
|
261
|
-
* of an element (found by its is attribute) by means of a JavaScript call.
|
|
262
|
-
* The window size contains the scrollbars. I.e. it is a bit larger than {@link getViewPortSize}.
|
|
263
|
-
*/
|
|
264
|
-
export async function getInnerSizeOfWindow(driver: WebDriver): Promise<{ width: number, height: number }> {
|
|
265
|
-
const w = await driver.executeScript('return window.innerWidth') as number;
|
|
266
|
-
const h = await driver.executeScript('return window.innerHeight') as number;
|
|
267
|
-
return { width: w, height: h };
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Returns the viewport size, which is the inner window size but not with the scrollbars.
|
|
272
|
-
* I.e. the viewport size is a bit smaller than {@link getInnerSizeOfWindow} (if scrollbars are visible).
|
|
273
|
-
* The value is retrieved by means of JavaScript.
|
|
274
|
-
*/
|
|
275
|
-
export async function getViewPortSize(driver: WebDriver): Promise<{ width: number, height: number }> {
|
|
276
|
-
const [clientWidthOfHTML, clientHeightOfHTML] = await Promise.all([
|
|
277
|
-
driver.executeScript("return document.documentElement.clientWidth"),
|
|
278
|
-
driver.executeScript("return document.documentElement.clientHeight")]) as number[];
|
|
279
|
-
return { width: clientWidthOfHTML, height: clientHeightOfHTML };
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Returns true if the element identified by its id attribute is with the view port.
|
|
284
|
-
* That does not necessarily means that it is visible, since its display or visible attribute may
|
|
285
|
-
* hide the element.
|
|
286
|
-
*/
|
|
287
|
-
export async function isInViewPort(driver: WebDriver, id: string) {
|
|
288
|
-
const innerSize = await getInnerSizeOfWindow(driver);
|
|
289
|
-
const [x, y] = await Promise.all([
|
|
290
|
-
driver.executeScript('return window.scrollX'),
|
|
291
|
-
driver.executeScript('return window.scrollY')]) as number[];
|
|
292
|
-
const bounds = await getBoundingClientRect(driver, id);
|
|
293
|
-
return bounds.x + bounds.width >= x && bounds.y + bounds.height >= y
|
|
294
|
-
&& bounds.x < x + innerSize.width && bounds.y < y + innerSize.height
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Sets the window so that the {@link getViewPortSize} matches the given width and height.
|
|
299
|
-
*/
|
|
300
|
-
export async function setBrowserWindowSize(driver: WebDriver, width: number, height: number): Promise<void> {
|
|
301
|
-
// const rect = await driver.manage().window().getRect();
|
|
302
|
-
await waitForSetRect(driver, width, height);
|
|
303
|
-
// await driver.manage().window().setRect({ width: width, height: height });
|
|
304
|
-
// return waitFor(async () => {
|
|
305
|
-
// const rectAfter = await driver.manage().window().getRect();
|
|
306
|
-
|
|
307
|
-
// if (check && (rectAfter.width != width || rectAfter.height != height)) {
|
|
308
|
-
// throw new Error(`Cannot set window to size (${width}, ${height}). Size is probably too small or too large, got ${rectAfter.width}/${rectAfter.height}, was before ${rect.width}/${rect.height}.`);
|
|
309
|
-
// }
|
|
310
|
-
// });
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async function waitForSetRect(driver: WebDriver, width: number, height: number): Promise<void> {
|
|
314
|
-
const rectBefore = await driver.manage().window().getRect();
|
|
315
|
-
|
|
316
|
-
await driver.manage().window().setRect({ width: width, height: height });
|
|
317
|
-
return waitFor(async () => {
|
|
318
|
-
const rectAfter = await driver.manage().window().getRect();
|
|
319
|
-
if (rectAfter.width != width || rectAfter.height != height) {
|
|
320
|
-
throw new Error(`Cannot set window to size (${width}, ${height}). Size is probably too small or too large, got ${rectAfter.width}/${rectAfter.height}, was before ${rectBefore.width}/${rectBefore.height}.`);
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Sets the window so that the {@link getViewPortSize} matches the given width and height. Warning: This does not work reliably.
|
|
327
|
-
*/
|
|
328
|
-
export async function setViewPortSize(driver: WebDriver, width: number, height: number): Promise<void> {
|
|
329
|
-
const rect = await driver.manage().window().getRect();
|
|
330
|
-
const innerBefore = await getInnerSizeOfWindow(driver);
|
|
331
|
-
const deltaX = rect.width - innerBefore.width;
|
|
332
|
-
const deltaY = rect.height - innerBefore.height;
|
|
333
|
-
await waitForSetRect(driver, width + deltaX, height + deltaY);
|
|
334
|
-
|
|
335
|
-
const [innerAfter, viewport] = await Promise.all([getInnerSizeOfWindow(driver), getViewPortSize(driver)]);
|
|
336
|
-
|
|
337
|
-
// adjust scrollbars if necessary
|
|
338
|
-
const sbHeight = innerAfter.height - viewport.height;
|
|
339
|
-
const sbWidth = innerAfter.width - viewport.width;
|
|
340
|
-
await waitForSetRect(driver, width + deltaX + sbWidth, height + deltaY + sbHeight);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
export async function getInheritedBackgroundColorsByJS(driver: WebDriver, nodelist: string) {
|
|
344
|
-
const colors = await driver.executeScript(`
|
|
345
|
-
const list = ${nodelist};
|
|
346
|
-
function getDefaultBackground() {
|
|
347
|
-
var div = document.createElement("div")
|
|
348
|
-
document.head.appendChild(div)
|
|
349
|
-
var bg = getComputedStyle(div).backgroundColor
|
|
350
|
-
document.head.removeChild(div)
|
|
351
|
-
return bg
|
|
352
|
-
}
|
|
353
|
-
const defaultStyle = getDefaultBackground()
|
|
354
|
-
function getInheritedBackgroundColor(el) {
|
|
355
|
-
var backgroundColor = getComputedStyle(el).backgroundColor
|
|
356
|
-
if (backgroundColor != defaultStyle) return backgroundColor
|
|
357
|
-
if (!el.parentElement) return defaultStyle
|
|
358
|
-
return getInheritedBackgroundColor(el.parentElement)
|
|
359
|
-
}
|
|
360
|
-
const colors = []
|
|
361
|
-
for (let e of list) {
|
|
362
|
-
colors.push(getInheritedBackgroundColor(e));
|
|
363
|
-
}
|
|
364
|
-
return colors;
|
|
365
|
-
`) as string[];
|
|
366
|
-
return colors;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
export async function getInheritedColorsByJS(driver: WebDriver, nodelist: string) {
|
|
370
|
-
const colors = await driver.executeScript(`
|
|
371
|
-
const list = ${nodelist};
|
|
372
|
-
function getDefaultColor() {
|
|
373
|
-
var div = document.createElement("div")
|
|
374
|
-
document.head.appendChild(div)
|
|
375
|
-
var bg = getComputedStyle(div).color
|
|
376
|
-
document.head.removeChild(div)
|
|
377
|
-
return bg
|
|
378
|
-
}
|
|
379
|
-
const defaultStyle = getDefaultColor()
|
|
380
|
-
function getInheritedColor(el) {
|
|
381
|
-
var color = getComputedStyle(el).color
|
|
382
|
-
if (color != defaultStyle) return color
|
|
383
|
-
if (!el.parentElement) return defaultStyle
|
|
384
|
-
return getInheritedColor(el.parentElement)
|
|
385
|
-
}
|
|
386
|
-
const colors = []
|
|
387
|
-
for (let e of list) {
|
|
388
|
-
colors.push(getInheritedColor(e));
|
|
389
|
-
}
|
|
390
|
-
return colors;
|
|
391
|
-
`) as string[];
|
|
392
|
-
return colors;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function rgb2hex(rgba: string) {
|
|
396
|
-
return "#" +
|
|
397
|
-
rgba.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)$/)!
|
|
398
|
-
.slice(1)
|
|
399
|
-
.map((n, i) => (i === 3
|
|
400
|
-
? Math.round(parseFloat(n) * 255)
|
|
401
|
-
: parseFloat(n)).toString(16).padStart(2, '0').replace('NaN', ''))
|
|
402
|
-
.join('');
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
export async function getAllTextNodes(driver: WebDriver): Promise<{ text: string; color: string; background: string; line: string; }[]> {
|
|
406
|
-
const res = await driver.executeScript(`
|
|
407
|
-
|
|
408
|
-
const TRANSPARENT = "rgba(0, 0, 0, 0)";
|
|
409
|
-
const all = [];
|
|
410
|
-
const view = document.defaultView;
|
|
411
|
-
function collect(e) {
|
|
412
|
-
const styles = view.getComputedStyle(e);
|
|
413
|
-
const pStyles = view.getComputedStyle(e.parentElement);
|
|
414
|
-
const childNodes = e.childNodes;
|
|
415
|
-
for (let i=0; i<childNodes.length; i++) {
|
|
416
|
-
const node = childNodes.item(i);
|
|
417
|
-
if (node instanceof Element) {
|
|
418
|
-
collect(node);
|
|
419
|
-
} else if (node instanceof Text && node.textContent.trim().length>0) {
|
|
420
|
-
all.push({
|
|
421
|
-
text: node.textContent.trim(),
|
|
422
|
-
color: styles?.color!==TRANSPARENT?styles?.color:pStyles?.color,
|
|
423
|
-
background: styles?.backgroundColor!==TRANSPARENT?styles?.backgroundColor:pStyles?.backgroundColor,
|
|
424
|
-
line: styles?.textDecorationLine==='none'?pStyles?.textDecorationLine:styles?.textDecorationLine
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
collect(document.body);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return all;
|
|
433
|
-
`) as { text: string, color: string, background: string, line: string }[];
|
|
434
|
-
|
|
435
|
-
res.forEach(e => {
|
|
436
|
-
e.color = rgb2hex(e.color);
|
|
437
|
-
e.background = rgb2hex(e.background);
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return res;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
export async function getButtonsAndHRefs(driver: WebDriver): Promise<WebElement[]> {
|
|
445
|
-
const elements: WebElement[] = await execScript(driver,
|
|
446
|
-
"execScripts/dom.js", "getButtonsAndHRefs");
|
|
447
|
-
return elements;
|
|
448
|
-
}
|