@applitools/eyes-cypress 3.26.1 → 3.26.4
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/CHANGELOG.md +30 -0
- package/README.md +77 -44
- package/bin/eyes-setup.js +22 -6
- package/dist/browser/spec-driver.js +10 -13
- package/package.json +24 -20
- package/src/browser/commands.js +3 -1
- package/src/browser/eyesCheckMapping.js +1 -0
- package/src/browser/spec-driver.ts +9 -12
- package/src/plugin/isGlobalHooksSupported.js +3 -2
- package/src/plugin/pluginExport.js +9 -2
- package/src/plugin/server.js +31 -6
- package/src/setup/addEyesCypressPlugin.js +1 -2
- package/src/setup/handleCommands.js +33 -1
- package/src/setup/handlePlugin.js +16 -8
- package/src/setup/handleTypeScript.js +13 -1
- package/dist/plugin/handler.js +0 -55
- package/src/plugin/handler.ts +0 -58
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,36 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
|
|
12
|
+
## 3.26.4 - 2022/7/7
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
- Add support for cypress 10
|
|
16
|
+
### Bug fixes
|
|
17
|
+
- Fix use cases where we fail to serialize elements
|
|
18
|
+
- Fixed bug where a failure in a single UFG environment fails all other environments in the same configuration
|
|
19
|
+
|
|
20
|
+
## 3.26.3 - 2022/6/17
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
- Add special attribute for pseudo elements
|
|
24
|
+
- Add the ability for the SDK to lazy load the page prior to performing a check window
|
|
25
|
+
- Support padding for regions in the following region types - ignoreRegions, layoutRegions, strictRegions, contentRegions
|
|
26
|
+
- Allow configuration file to be loaded from ancestor directories
|
|
27
|
+
### Bug fixes
|
|
28
|
+
- Fix rendering issues with Salesforce Lightning design system
|
|
29
|
+
- Fix issue that prevented self-signed certificates from working when connecting through a proxy server
|
|
30
|
+
|
|
31
|
+
## 3.26.2 - 2022/6/8
|
|
32
|
+
|
|
33
|
+
### Features
|
|
34
|
+
- add support for secure websocket
|
|
35
|
+
- Allowed `` values in custom properties
|
|
36
|
+
### Bug fixes
|
|
37
|
+
- Fix incorrect test results when dealing with a page that contains CORS iframes
|
|
38
|
+
- Fixed broken links to enums implementation in the README.md
|
|
39
|
+
- Fix calling `waitBeforeCapture` when failed to set required viewport size
|
|
40
|
+
|
|
11
41
|
## 3.26.1 - 2022/6/2
|
|
12
42
|
|
|
13
43
|
### Features
|
package/README.md
CHANGED
|
@@ -27,9 +27,12 @@ The above command will add the necessary imports to your cypress `pluginsFile` a
|
|
|
27
27
|
#### Manual configuration
|
|
28
28
|
|
|
29
29
|
##### 1. Configure Eyes-Cypress plugin
|
|
30
|
-
|
|
30
|
+
<br>
|
|
31
|
+
|
|
31
32
|
Eyes-Cypress acts as a [Cypress plugin](https://docs.cypress.io/guides/tooling/plugins-guide.html), so it should be configured as such.
|
|
32
|
-
Unfortunately there's no easy way to do this automatically, so you need to manually
|
|
33
|
+
Unfortunately there's no easy way to do this automatically, so you need to manually:
|
|
34
|
+
#### Cypress version < 10:
|
|
35
|
+
Add the following code to your `pluginsFile`:
|
|
33
36
|
|
|
34
37
|
**Important**: add this code **after** the definition of `module.exports`:
|
|
35
38
|
|
|
@@ -38,7 +41,16 @@ require('@applitools/eyes-cypress')(module)
|
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
Normally, this is `cypress/plugins/index.js`. You can read more about it in Cypress' docs [here](https://docs.cypress.io/guides/references/configuration.html#Folders-Files).
|
|
44
|
+
<br>
|
|
45
|
+
|
|
46
|
+
#### Cypress version >= 10:
|
|
41
47
|
|
|
48
|
+
Add the following code to your `cypress.config.js` file after `module.exports`:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
require('@applitools/eyes-cypress')(module)
|
|
52
|
+
```
|
|
53
|
+
This file is normally at the root of the project
|
|
42
54
|
##### 2. Configure custom commands
|
|
43
55
|
|
|
44
56
|
Eyes-Cypress exposes new commands to your tests. This means that more methods will be available on the `cy` object. To enable this, it's required to configure these custom commands.
|
|
@@ -48,7 +60,7 @@ As with the plugin, there's no automatic way to configure this in cypress, so yo
|
|
|
48
60
|
import '@applitools/eyes-cypress/commands'
|
|
49
61
|
```
|
|
50
62
|
|
|
51
|
-
Normally, this is `cypress/support/index.js
|
|
63
|
+
Normally, this is `cypress/support/index.js` for cypress version < 10 and `cypress/support/e2e.js` for cypress version >= 10. You can read more about it in Cypress' docs [here](https://docs.cypress.io/guides/references/configuration.html#Folders-Files).
|
|
52
64
|
|
|
53
65
|
##### 3. (Optional) TypeScript configuration
|
|
54
66
|
|
|
@@ -66,8 +78,6 @@ Add this file to your project with either:
|
|
|
66
78
|
```
|
|
67
79
|
cp node_modules/@applitools/eyes-cypress/eyes-index.d.ts ./cypress/support/
|
|
68
80
|
```
|
|
69
|
-
|
|
70
|
-
|
|
71
81
|
### Applitools API key
|
|
72
82
|
|
|
73
83
|
In order to authenticate via the Applitools server, you need to supply the Eyes-Cypress SDK with the API key you got from Applitools. Read more about how to obtain the API key [here](https://applitools.com/docs/topics/overview/obtain-api-key.html).
|
|
@@ -173,43 +183,66 @@ Applitools will take screenshots and perform the visual comparisons in the backg
|
|
|
173
183
|
<br/>
|
|
174
184
|
|
|
175
185
|
### Index
|
|
176
|
-
- [
|
|
177
|
-
- [
|
|
178
|
-
|
|
179
|
-
- [
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
- [
|
|
186
|
-
- [
|
|
187
|
-
|
|
188
|
-
- [
|
|
189
|
-
- [
|
|
190
|
-
- [
|
|
191
|
-
- [
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
- [
|
|
200
|
-
- [
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
- [
|
|
208
|
-
|
|
209
|
-
- [
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
- [
|
|
186
|
+
- [Eyes-Cypress](#eyes-cypress)
|
|
187
|
+
- [Installation](#installation)
|
|
188
|
+
- [Install npm package](#install-npm-package)
|
|
189
|
+
- [Configure plugin and commands](#configure-plugin-and-commands)
|
|
190
|
+
- [Automatic configuration](#automatic-configuration)
|
|
191
|
+
- [Manual configuration](#manual-configuration)
|
|
192
|
+
- [1. Configure Eyes-Cypress plugin](#1-configure-eyes-cypress-plugin)
|
|
193
|
+
- [2. Configure custom commands](#2-configure-custom-commands)
|
|
194
|
+
- [3. (Optional) TypeScript configuration](#3-optional-typescript-configuration)
|
|
195
|
+
- [Applitools API key](#applitools-api-key)
|
|
196
|
+
- [Eyes server URL (optional)](#eyes-server-url-optional)
|
|
197
|
+
- [Usage](#usage)
|
|
198
|
+
- [Example](#example)
|
|
199
|
+
- [Best practice for using the SDK](#best-practice-for-using-the-sdk)
|
|
200
|
+
- [Index](#index)
|
|
201
|
+
- [Commands](#commands)
|
|
202
|
+
- [Open](#open)
|
|
203
|
+
- [Check window](#check-window)
|
|
204
|
+
- [Arguments to `cy.eyesCheckWindow`](#arguments-to-cyeyescheckwindow)
|
|
205
|
+
- [`tag`](#tag)
|
|
206
|
+
- [`target`](#target)
|
|
207
|
+
- [`fully`](#fully)
|
|
208
|
+
- [`selector`](#selector)
|
|
209
|
+
- [`region`](#region)
|
|
210
|
+
- [`element`](#element)
|
|
211
|
+
- [`ignore`](#ignore)
|
|
212
|
+
- [`floating`](#floating)
|
|
213
|
+
- [`layout`](#layout)
|
|
214
|
+
- [`strict`](#strict)
|
|
215
|
+
- [`content`](#content)
|
|
216
|
+
- [`accessibility`](#accessibility)
|
|
217
|
+
- [`region in shadow DOM`](#region-in-shadow-dom)
|
|
218
|
+
- [`scriptHooks`](#scripthooks)
|
|
219
|
+
- [`layoutBreakpoints`](#layoutbreakpoints)
|
|
220
|
+
- [`sendDom`](#senddom)
|
|
221
|
+
- [`variationGroupId`](#variationgroupid)
|
|
222
|
+
- [`waitBeforeCapture`](#waitbeforecapture)
|
|
223
|
+
- [`useDom`](#usedom)
|
|
224
|
+
- [`enablePatterns`](#enablepatterns)
|
|
225
|
+
- [`matchLevel`](#matchlevel)
|
|
226
|
+
- [`visualGridOptions`](#visualgridoptions)
|
|
227
|
+
- [Close](#close)
|
|
228
|
+
- [GetAllTestResults](#getalltestresults)
|
|
229
|
+
- [deleteTestResults](#deletetestresults)
|
|
230
|
+
- [Concurrency](#concurrency)
|
|
231
|
+
- [Advanced configuration](#advanced-configuration)
|
|
232
|
+
- [Here are the available configuration properties:](#here-are-the-available-configuration-properties)
|
|
233
|
+
- [Global configuration properties:](#global-configuration-properties)
|
|
234
|
+
- [Method 1: Arguments for `cy.eyesOpen`](#method-1-arguments-for-cyeyesopen)
|
|
235
|
+
- [Method 2: Environment variables](#method-2-environment-variables)
|
|
236
|
+
- [Method 3: The `applitools.config.js` file](#method-3-the-applitoolsconfigjs-file)
|
|
237
|
+
- [Configuring the browser](#configuring-the-browser)
|
|
238
|
+
- [Previous browser versions](#previous-browser-versions)
|
|
239
|
+
- [Getting a screenshot of multiple browsers in parallel](#getting-a-screenshot-of-multiple-browsers-in-parallel)
|
|
240
|
+
- [Device emulation](#device-emulation)
|
|
241
|
+
- [iOS device](#ios-device)
|
|
242
|
+
- [Intelligent Code Completion](#intelligent-code-completion)
|
|
243
|
+
- [There are two ways you can add Eyes-Cypress intelliSense to your tests:](#there-are-two-ways-you-can-add-eyes-cypress-intellisense-to-your-tests)
|
|
244
|
+
- [1. Triple slash directives](#1-triple-slash-directives)
|
|
245
|
+
- [2. Reference type declarations via `tsconfig`](#2-reference-type-declarations-via-tsconfig)
|
|
213
246
|
|
|
214
247
|
<br/><hr/><br/>
|
|
215
248
|
|
|
@@ -827,7 +860,7 @@ cy.eyesOpen({
|
|
|
827
860
|
|
|
828
861
|
Possible values for screen orientation are `landscape` and `portrait`, and if no value is specified, the default is `portrait`.
|
|
829
862
|
|
|
830
|
-
The list of device names is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-
|
|
863
|
+
The list of device names is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/DeviceName.ts
|
|
831
864
|
|
|
832
865
|
In addition, it's possible to use chrome's device emulation with custom viewport sizes, pixel density and mobile mode, by passing `deviceScaleFactor` and `mobile` in addition to `width` and `height`. For example:
|
|
833
866
|
|
|
@@ -859,7 +892,7 @@ cy.eyesOpen({
|
|
|
859
892
|
})
|
|
860
893
|
```
|
|
861
894
|
|
|
862
|
-
The list of devices is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-
|
|
895
|
+
The list of devices is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/IosDeviceName.ts
|
|
863
896
|
|
|
864
897
|
Possible values for `iosVersion` are:
|
|
865
898
|
|
package/bin/eyes-setup.js
CHANGED
|
@@ -2,18 +2,34 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
-
const handlePlugin = require('../src/setup/handlePlugin');
|
|
6
|
-
const handleCommands = require('../src/setup/handleCommands');
|
|
7
|
-
const handleTypeScript = require('../src/setup/handleTypeScript');
|
|
5
|
+
const {handlePlugin} = require('../src/setup/handlePlugin');
|
|
6
|
+
const {handleCommands, handlerCommandsCypress10} = require('../src/setup/handleCommands');
|
|
7
|
+
const {handleTypeScript, handlerTypeScriptCypress10} = require('../src/setup/handleTypeScript');
|
|
8
8
|
const {version} = require('../package');
|
|
9
|
+
const fs = require('fs');
|
|
9
10
|
const cwd = process.cwd();
|
|
10
11
|
|
|
11
12
|
console.log(chalk.cyan('Setup eyes-cypress', version));
|
|
13
|
+
const packageJson = JSON.parse(fs.readFileSync('package.json'));
|
|
14
|
+
let cypressVersion;
|
|
12
15
|
|
|
16
|
+
if (packageJson.dependencies && packageJson.dependencies.cypress) {
|
|
17
|
+
cypressVersion = packageJson.dependencies.cypress;
|
|
18
|
+
} else if (packageJson.devDependencies && packageJson.devDependencies.cypress) {
|
|
19
|
+
cypressVersion = packageJson.devDependencies.cypress;
|
|
20
|
+
}
|
|
21
|
+
console.log(chalk.cyan('Cypress version that was found', cypressVersion));
|
|
22
|
+
const isCypress10 = parseFloat(cypressVersion, 10) >= 10 ? true : false;
|
|
13
23
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
if (!isCypress10) {
|
|
25
|
+
handlePlugin(cwd, isCypress10);
|
|
26
|
+
handleCommands(cwd);
|
|
27
|
+
handleTypeScript(cwd);
|
|
28
|
+
} else {
|
|
29
|
+
handlePlugin(cwd, isCypress10);
|
|
30
|
+
const supportFilePath = handlerCommandsCypress10(cwd);
|
|
31
|
+
handlerTypeScriptCypress10(supportFilePath);
|
|
32
|
+
}
|
|
17
33
|
} catch (e) {
|
|
18
34
|
console.log(chalk.red('Setup error:\n', e));
|
|
19
35
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getCookies = exports.getUrl = exports.getTitle = exports.findElements = exports.findElement = exports.transformSelector = exports.setViewportSize = exports.getViewportSize = exports.childContext = exports.parentContext = exports.mainContext = exports.executeScript = void 0;
|
|
4
4
|
function executeScript(context, script, arg) {
|
|
5
|
-
context = refreshContext(context);
|
|
6
5
|
let scriptToExecute;
|
|
7
6
|
if (script.includes('dom-snapshot') ||
|
|
8
7
|
script.includes('dom-capture') ||
|
|
@@ -23,13 +22,20 @@ function mainContext() {
|
|
|
23
22
|
}
|
|
24
23
|
exports.mainContext = mainContext;
|
|
25
24
|
function parentContext(context) {
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
// because Cypress doesn't support cross origin iframe, then childContext might return null, and then the input to parentContext might be null
|
|
26
|
+
if (!context) {
|
|
27
|
+
throw new Error('Context is not accessible');
|
|
28
|
+
}
|
|
29
|
+
;
|
|
28
30
|
return context === mainContext() ? context : context.defaultView.frameElement.ownerDocument;
|
|
29
31
|
}
|
|
30
32
|
exports.parentContext = parentContext;
|
|
31
33
|
function childContext(_context, element) {
|
|
32
|
-
|
|
34
|
+
if (element.contentDocument)
|
|
35
|
+
return element.contentDocument;
|
|
36
|
+
else {
|
|
37
|
+
throw new Error('Context is not accessible');
|
|
38
|
+
}
|
|
33
39
|
}
|
|
34
40
|
exports.childContext = childContext;
|
|
35
41
|
function getViewportSize() {
|
|
@@ -55,7 +61,6 @@ function transformSelector(selector) {
|
|
|
55
61
|
}
|
|
56
62
|
exports.transformSelector = transformSelector;
|
|
57
63
|
function findElement(context, selector, parent) {
|
|
58
|
-
context = refreshContext(context);
|
|
59
64
|
const eyesSelector = selector;
|
|
60
65
|
const root = parent !== null && parent !== void 0 ? parent : context;
|
|
61
66
|
const sel = typeof selector === 'string' ? selector : eyesSelector.selector;
|
|
@@ -68,7 +73,6 @@ function findElement(context, selector, parent) {
|
|
|
68
73
|
}
|
|
69
74
|
exports.findElement = findElement;
|
|
70
75
|
function findElements(context, selector, parent) {
|
|
71
|
-
context = refreshContext(context);
|
|
72
76
|
const eyesSelector = selector;
|
|
73
77
|
const root = parent !== null && parent !== void 0 ? parent : context;
|
|
74
78
|
const sel = typeof selector === 'string' ? selector : eyesSelector.selector;
|
|
@@ -86,12 +90,10 @@ function findElements(context, selector, parent) {
|
|
|
86
90
|
}
|
|
87
91
|
exports.findElements = findElements;
|
|
88
92
|
function getTitle(context) {
|
|
89
|
-
context = refreshContext(context);
|
|
90
93
|
return context.title;
|
|
91
94
|
}
|
|
92
95
|
exports.getTitle = getTitle;
|
|
93
96
|
function getUrl(context) {
|
|
94
|
-
context = refreshContext(context);
|
|
95
97
|
return context.location.href;
|
|
96
98
|
}
|
|
97
99
|
exports.getUrl = getUrl;
|
|
@@ -100,11 +102,6 @@ function getCookies() {
|
|
|
100
102
|
return Cypress.automation('get:cookies', {});
|
|
101
103
|
}
|
|
102
104
|
exports.getCookies = getCookies;
|
|
103
|
-
// we need to method to reset the context in case the user called open before visit
|
|
104
|
-
function refreshContext(context) {
|
|
105
|
-
//@ts-ignore
|
|
106
|
-
return (context && context.defaultView) ? context : cy.state('window').document;
|
|
107
|
-
}
|
|
108
105
|
// export function takeScreenshot(page: Driver): Promise<Buffer>;
|
|
109
106
|
// export function visit(page: Driver, url: string): Promise<void>; (??)
|
|
110
107
|
// export function isStaleElementError(err: any): boolean;
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/eyes-cypress",
|
|
3
|
-
"version": "3.26.
|
|
3
|
+
"version": "3.26.4",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git://github.com/applitools/eyes.sdk.javascript1.git",
|
|
7
|
-
"directory": "packages/eyes-cypress"
|
|
7
|
+
"directory": "js/packages/eyes-cypress"
|
|
8
8
|
},
|
|
9
9
|
"license": "SEE LICENSE IN LICENSE",
|
|
10
|
+
"aliases": [
|
|
11
|
+
"cypress",
|
|
12
|
+
"cy"
|
|
13
|
+
],
|
|
10
14
|
"main": "index.js",
|
|
11
15
|
"types": "./index.d.ts",
|
|
12
16
|
"bin": {
|
|
@@ -25,17 +29,18 @@
|
|
|
25
29
|
"build": "tsc",
|
|
26
30
|
"generate:tests": "coverage-tests generate",
|
|
27
31
|
"test": "yarn test:unit && yarn test:it && yarn test:e2e && yarn test:ts && yarn test:coverage",
|
|
32
|
+
"test:sanity": "yarn test:unit && yarn test:it && yarn test:ts",
|
|
28
33
|
"test:unit": "mocha --no-timeouts 'test/unit/**/*.test.js'",
|
|
29
|
-
"test:it": "mocha --no-timeouts 'test/it/**/*.test.js'",
|
|
30
|
-
"test:
|
|
34
|
+
"test:it": "yarn build && mocha --no-timeouts 'test/it/**/*.test.js'",
|
|
35
|
+
"test:ts": "yarn test:ts:compile && yarn test:ts:run",
|
|
31
36
|
"test:ts:compile": "tsc --project test/e2e/ts/cypress",
|
|
32
37
|
"test:ts:run": "cypress run --config-file test/e2e/ts/cypress-ts.json",
|
|
33
|
-
"test:
|
|
34
|
-
"test:
|
|
38
|
+
"test:coverage": "yarn generate:tests && cd test/coverage/generic && yarn && unset APPLITOOLS_API_KEY && APPLITOOLS_BATCH_NAME='JS Coverage Tests: eyes-selenium' APPLITOOLS_BATCH_ID=$(uuidgen) npx cypress run",
|
|
39
|
+
"test:e2e": "mkdir -p test/fixtures/testAppCopies && mocha --no-timeouts 'test/e2e/**/*.test.js'",
|
|
35
40
|
"cypress": "cypress open --config-file test/fixtures/cypress-play.json",
|
|
36
|
-
"cypress:new": "
|
|
41
|
+
"cypress:new": "cd test/play && yarn && npx cypress open",
|
|
37
42
|
"cypress:run": "cypress run --config-file test/fixtures/cypress-play.json --spec=test/fixtures/testApp/cypress/integration-play/play.js",
|
|
38
|
-
"cypress:run:new": "
|
|
43
|
+
"cypress:run:new": "cd test/play && yarn && npx cypress run --spec=../fixtures/testApp/cypress/integration-play/play.js",
|
|
39
44
|
"cypress:play": "cd test/fixtures/testApp && cypress run --config integrationFolder=cypress/integration-play,pluginsFile=cypress/plugins/index-play.js,supportFile=cypress/support/index-run.js --spec=cypress/integration-play/play.js",
|
|
40
45
|
"render": "run(){ npx cypress run --config integrationFolder=test/fixtures/testApp/cypress/render,pluginsFile=test/fixtures/testApp/cypress/plugins/index-render.js,supportFile=test/fixtures/testApp/cypress/support/index-run.js --env url=$1; }; run",
|
|
41
46
|
"prepublish:setup": "sudo apt-get install xvfb",
|
|
@@ -50,24 +55,24 @@
|
|
|
50
55
|
}
|
|
51
56
|
},
|
|
52
57
|
"dependencies": {
|
|
53
|
-
"@applitools/eyes-api": "1.
|
|
54
|
-
"@applitools/eyes-universal": "2.6
|
|
58
|
+
"@applitools/eyes-api": "1.7.4",
|
|
59
|
+
"@applitools/eyes-universal": "2.9.6",
|
|
55
60
|
"@applitools/functional-commons": "1.6.0",
|
|
56
|
-
"@applitools/logger": "1.1.
|
|
57
|
-
"@applitools/visual-grid-client": "15.
|
|
61
|
+
"@applitools/logger": "1.1.15",
|
|
62
|
+
"@applitools/visual-grid-client": "15.13.6",
|
|
58
63
|
"chalk": "3.0.0",
|
|
59
64
|
"uuid": "8.3.2",
|
|
60
65
|
"ws": "8.5.0"
|
|
61
66
|
},
|
|
62
67
|
"devDependencies": {
|
|
63
|
-
"@applitools/bongo": "^2.1.
|
|
68
|
+
"@applitools/bongo": "^2.1.6",
|
|
64
69
|
"@applitools/scripts": "1.1.0",
|
|
65
|
-
"@applitools/sdk-coverage-tests": "^2.3.
|
|
70
|
+
"@applitools/sdk-coverage-tests": "^2.3.19",
|
|
66
71
|
"@applitools/snaptdout": "1.0.1",
|
|
67
|
-
"@applitools/test-server": "1.
|
|
68
|
-
"@applitools/test-utils": "1.
|
|
69
|
-
"@applitools/types": "^1.
|
|
70
|
-
"@applitools/utils": "1.3.
|
|
72
|
+
"@applitools/test-server": "1.1.4",
|
|
73
|
+
"@applitools/test-utils": "1.4.2",
|
|
74
|
+
"@applitools/types": "^1.5.5",
|
|
75
|
+
"@applitools/utils": "1.3.10",
|
|
71
76
|
"@types/node": "12",
|
|
72
77
|
"@types/ws": "^8.2.2",
|
|
73
78
|
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
|
@@ -75,8 +80,7 @@
|
|
|
75
80
|
"chai": "4.2.0",
|
|
76
81
|
"chai-spies": "1.0.0",
|
|
77
82
|
"cookie-parser": "1.4.4",
|
|
78
|
-
"cypress": "
|
|
79
|
-
"cypress-new": "npm:cypress@9",
|
|
83
|
+
"cypress": "8.0.0",
|
|
80
84
|
"eslint": "8.10.0",
|
|
81
85
|
"eslint-plugin-mocha-no-only": "1.1.0",
|
|
82
86
|
"eslint-plugin-node": "7.0.1",
|
package/src/browser/commands.js
CHANGED
|
@@ -110,7 +110,7 @@ Cypress.Commands.add('eyesOpen', function(args = {}) {
|
|
|
110
110
|
const driver = refer.ref(cy.state('window').document);
|
|
111
111
|
|
|
112
112
|
if (!connectedToUniversal) {
|
|
113
|
-
socket.connect(`
|
|
113
|
+
socket.connect(`wss://localhost:${Cypress.config('eyesPort')}/eyes`);
|
|
114
114
|
connectedToUniversal = true;
|
|
115
115
|
socket.emit('Core.makeSDK', {
|
|
116
116
|
name: 'eyes.cypress',
|
|
@@ -152,6 +152,7 @@ Cypress.Commands.add('eyesCheckWindow', (args = {}) =>
|
|
|
152
152
|
if (isCurrentTestDisabled) return;
|
|
153
153
|
|
|
154
154
|
setRootContext();
|
|
155
|
+
const driver = refer.ref(cy.state('window').document);
|
|
155
156
|
|
|
156
157
|
Cypress.log({name: 'Eyes: check window'});
|
|
157
158
|
|
|
@@ -160,6 +161,7 @@ Cypress.Commands.add('eyesCheckWindow', (args = {}) =>
|
|
|
160
161
|
return socket.request('Eyes.check', {
|
|
161
162
|
eyes,
|
|
162
163
|
settings: checkSettings,
|
|
164
|
+
driver,
|
|
163
165
|
});
|
|
164
166
|
}),
|
|
165
167
|
);
|
|
@@ -4,7 +4,6 @@ export type Context = Document & {__applitoolsBrand?: never};
|
|
|
4
4
|
export type Element = HTMLElement & {__applitoolsBrand?: never};
|
|
5
5
|
|
|
6
6
|
export function executeScript(context: Context, script: string, arg: any): any {
|
|
7
|
-
context = refreshContext(context)
|
|
8
7
|
|
|
9
8
|
let scriptToExecute;
|
|
10
9
|
if (
|
|
@@ -28,13 +27,20 @@ export function mainContext(): Context {
|
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export function parentContext(context: Context): Context {
|
|
31
|
-
|
|
30
|
+
// because Cypress doesn't support cross origin iframe, then childContext might return null, and then the input to parentContext might be null
|
|
31
|
+
if (!context) {
|
|
32
|
+
throw new Error('Context is not accessible')
|
|
33
|
+
};
|
|
32
34
|
|
|
33
35
|
return context === mainContext() ? context : context.defaultView.frameElement.ownerDocument
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export function childContext(_context: Context, element: HTMLIFrameElement): Context {
|
|
37
|
-
|
|
39
|
+
if(element.contentDocument)
|
|
40
|
+
return element.contentDocument
|
|
41
|
+
else {
|
|
42
|
+
throw new Error('Context is not accessible')
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
export function getViewportSize(): Object {
|
|
@@ -60,7 +66,6 @@ export function transformSelector(selector: Selector): Selector {
|
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
export function findElement(context: Context, selector: Selector, parent?: Element) {
|
|
63
|
-
context = refreshContext(context)
|
|
64
69
|
const eyesSelector = (selector as EyesSelector)
|
|
65
70
|
const root = parent ?? context
|
|
66
71
|
const sel = typeof selector === 'string' ? selector : eyesSelector.selector
|
|
@@ -72,7 +77,6 @@ export function findElement(context: Context, selector: Selector, parent?: Eleme
|
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
export function findElements(context: Context, selector: Selector, parent: Element){
|
|
75
|
-
context = refreshContext(context)
|
|
76
80
|
const eyesSelector = (selector as EyesSelector)
|
|
77
81
|
const root = parent ?? context
|
|
78
82
|
const sel = typeof selector === 'string' ? selector : eyesSelector.selector
|
|
@@ -91,12 +95,10 @@ export function findElements(context: Context, selector: Selector, parent: Eleme
|
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
export function getTitle(context: Context): string {
|
|
94
|
-
context = refreshContext(context)
|
|
95
98
|
return context.title
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
export function getUrl(context: Context): string {
|
|
99
|
-
context = refreshContext(context)
|
|
100
102
|
return context.location.href
|
|
101
103
|
}
|
|
102
104
|
|
|
@@ -105,11 +107,6 @@ export function getCookies(): Array<any> {
|
|
|
105
107
|
return Cypress.automation('get:cookies', {})
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
// we need to method to reset the context in case the user called open before visit
|
|
109
|
-
function refreshContext(context: Context) {
|
|
110
|
-
//@ts-ignore
|
|
111
|
-
return (context && context.defaultView) ? context : cy.state('window').document
|
|
112
|
-
}
|
|
113
110
|
|
|
114
111
|
// export function takeScreenshot(page: Driver): Promise<Buffer>;
|
|
115
112
|
|
|
@@ -5,8 +5,9 @@ function isGlobalHooksSupported(config) {
|
|
|
5
5
|
const {version, experimentalRunEvents} = config;
|
|
6
6
|
|
|
7
7
|
return (
|
|
8
|
-
version >= CYPRESS_NO_FLAG_VERSION ||
|
|
9
|
-
(version >= CYPRESS_SUPPORTED_VERSION &&
|
|
8
|
+
parseFloat(version, 10) >= parseFloat(CYPRESS_NO_FLAG_VERSION, 10) ||
|
|
9
|
+
(parseFloat(version, 10) >= parseFloat(CYPRESS_SUPPORTED_VERSION, 10) &&
|
|
10
|
+
!!experimentalRunEvents)
|
|
10
11
|
);
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -6,8 +6,10 @@ const makeGlobalRunHooks = require('./hooks');
|
|
|
6
6
|
function makePluginExport({startServer, eyesConfig}) {
|
|
7
7
|
return function pluginExport(pluginModule) {
|
|
8
8
|
let eyesServer;
|
|
9
|
-
const pluginModuleExports = pluginModule.exports
|
|
10
|
-
|
|
9
|
+
const pluginModuleExports = pluginModule.exports.e2e
|
|
10
|
+
? pluginModule.exports.e2e.setupNodeEvents
|
|
11
|
+
: pluginModule.exports;
|
|
12
|
+
const setupNodeEvents = async function(...args) {
|
|
11
13
|
const {server, port, closeManager, closeBatches, closeUniversalServer} = await startServer();
|
|
12
14
|
eyesServer = server;
|
|
13
15
|
|
|
@@ -51,6 +53,11 @@ function makePluginExport({startServer, eyesConfig}) {
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
};
|
|
56
|
+
if (pluginModule.exports.e2e) {
|
|
57
|
+
pluginModule.exports.e2e.setupNodeEvents = setupNodeEvents;
|
|
58
|
+
} else {
|
|
59
|
+
pluginModule.exports = setupNodeEvents;
|
|
60
|
+
}
|
|
54
61
|
return function getCloseServer() {
|
|
55
62
|
return eyesServer.close();
|
|
56
63
|
};
|
package/src/plugin/server.js
CHANGED
|
@@ -1,21 +1,46 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const {makeHandler} = require('../../dist/plugin/handler');
|
|
3
2
|
const connectSocket = require('./webSocket');
|
|
4
3
|
const {makeServerProcess} = require('@applitools/eyes-universal');
|
|
5
4
|
const {TestResults} = require('@applitools/visual-grid-client');
|
|
6
5
|
const handleTestResults = require('./handleTestResults');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const {Server: HttpsServer} = require('https');
|
|
9
|
+
const {Server: WSServer} = require('ws');
|
|
7
10
|
|
|
8
11
|
function makeStartServer({logger}) {
|
|
9
12
|
return async function startServer() {
|
|
10
|
-
const
|
|
13
|
+
const key = fs.readFileSync(path.resolve(__dirname, '../pem/server.key'));
|
|
14
|
+
const cert = fs.readFileSync(path.resolve(__dirname, '../pem/server.cert'));
|
|
15
|
+
let port;
|
|
11
16
|
|
|
12
|
-
const
|
|
17
|
+
const https = new HttpsServer({
|
|
18
|
+
key,
|
|
19
|
+
cert,
|
|
20
|
+
});
|
|
21
|
+
await https.listen(0, err => {
|
|
22
|
+
if (err) {
|
|
23
|
+
logger.log('error starting plugin server', err);
|
|
24
|
+
} else {
|
|
25
|
+
logger.log(`plugin server running at port: ${https.address().port}`);
|
|
26
|
+
port = https.address().port;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const wss = new WSServer({server: https, path: '/eyes', maxPayload: 254 * 1024 * 1024});
|
|
31
|
+
|
|
32
|
+
wss.on('close', () => https.close());
|
|
33
|
+
|
|
34
|
+
const {port: universalPort, close: closeUniversalServer} = await makeServerProcess({
|
|
35
|
+
key: path.resolve(__dirname, '../pem/server.key'),
|
|
36
|
+
cert: path.resolve(__dirname, '../pem/server.cert'),
|
|
37
|
+
});
|
|
13
38
|
|
|
14
39
|
const managers = [];
|
|
15
40
|
let socketWithUniversal;
|
|
16
41
|
|
|
17
|
-
|
|
18
|
-
socketWithUniversal = connectSocket(`
|
|
42
|
+
wss.on('connection', socketWithClient => {
|
|
43
|
+
socketWithUniversal = connectSocket(`wss://localhost:${universalPort}/eyes`);
|
|
19
44
|
|
|
20
45
|
socketWithUniversal.setPassthroughListener(message => {
|
|
21
46
|
logger.log('<== ', message.toString().slice(0, 1000));
|
|
@@ -69,7 +94,7 @@ function makeStartServer({logger}) {
|
|
|
69
94
|
});
|
|
70
95
|
|
|
71
96
|
return {
|
|
72
|
-
server,
|
|
97
|
+
server: wss,
|
|
73
98
|
port,
|
|
74
99
|
closeManager,
|
|
75
100
|
closeBatches,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
3
6
|
const chalk = require('chalk');
|
|
4
7
|
const {readFileSync, writeFileSync} = require('fs');
|
|
5
8
|
const addEyesCommands = require('./addEyesCommands');
|
|
@@ -20,4 +23,33 @@ function handleCommands(cwd) {
|
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
function handlerCommandsCypress10(cwd) {
|
|
27
|
+
const configContent = fs.readFileSync(path.resolve(cwd, 'cypress.config.js'), 'utf-8');
|
|
28
|
+
let supportFilePath;
|
|
29
|
+
if (configContent.includes('supportFile')) {
|
|
30
|
+
const regex = new RegExp(/(?:supportFile:)(?:\s*)(.*)/g);
|
|
31
|
+
const filePath = regex.exec(configContent)[1].replace(/['|"|,]*/g, '');
|
|
32
|
+
supportFilePath = path.resolve(cwd, filePath);
|
|
33
|
+
} else {
|
|
34
|
+
if (fs.existsSync(path.resolve(cwd, 'cypress/support/e2e.js'))) {
|
|
35
|
+
supportFilePath = path.resolve(cwd, 'cypress/support/e2e.js');
|
|
36
|
+
} else if (fs.existsSync(path.resolve(cwd, 'cypress/support/component.js'))) {
|
|
37
|
+
supportFilePath = path.resolve(cwd, 'cypress/support/component.js');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (supportFilePath) {
|
|
42
|
+
const commandsFileContent = fs.readFileSync(supportFilePath, 'utf-8');
|
|
43
|
+
if (!isCommandsDefined(commandsFileContent)) {
|
|
44
|
+
writeFileSync(supportFilePath, addEyesCommands(commandsFileContent));
|
|
45
|
+
console.log(chalk.cyan('Commands defined.'));
|
|
46
|
+
} else {
|
|
47
|
+
console.log(chalk.cyan('Commands already defined.'));
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
throw new Error('Commands file not found!');
|
|
51
|
+
}
|
|
52
|
+
return supportFilePath;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {handleCommands, handlerCommandsCypress10};
|
|
@@ -2,22 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
const {readFileSync, writeFileSync} = require('fs');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
-
const addEyesCypressPlugin = require('./addEyesCypressPlugin');
|
|
5
|
+
const {addEyesCypressPlugin} = require('./addEyesCypressPlugin');
|
|
6
6
|
const isPluginDefined = require('./isPluginDefined');
|
|
7
7
|
const getFilePath = require('./getFilePath');
|
|
8
8
|
const getCypressConfig = require('./getCypressConfig');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
9
11
|
|
|
10
|
-
function handlePlugin(cwd) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
function handlePlugin(cwd, isCypress10) {
|
|
13
|
+
let fileContent, filePath;
|
|
14
|
+
if (!isCypress10) {
|
|
15
|
+
const cypressConfig = getCypressConfig(cwd);
|
|
16
|
+
filePath = getFilePath('plugins', cypressConfig, cwd);
|
|
17
|
+
fileContent = readFileSync(filePath).toString();
|
|
18
|
+
} else {
|
|
19
|
+
filePath = path.resolve(cwd, 'cypress.config.js');
|
|
20
|
+
fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
21
|
+
}
|
|
14
22
|
|
|
15
|
-
if (!isPluginDefined(
|
|
16
|
-
writeFileSync(
|
|
23
|
+
if (!isPluginDefined(fileContent)) {
|
|
24
|
+
writeFileSync(filePath, addEyesCypressPlugin(fileContent));
|
|
17
25
|
console.log(chalk.cyan('Plugins defined.'));
|
|
18
26
|
} else {
|
|
19
27
|
console.log(chalk.cyan('Plugins already defined'));
|
|
20
28
|
}
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
module.exports = handlePlugin;
|
|
31
|
+
module.exports = {handlePlugin};
|
|
@@ -4,6 +4,7 @@ const {writeFileSync, existsSync} = require('fs');
|
|
|
4
4
|
const getFilePath = require('./getFilePath');
|
|
5
5
|
const getCypressConfig = require('./getCypressConfig');
|
|
6
6
|
const eyesIndexContent = `import "@applitools/eyes-cypress"`;
|
|
7
|
+
const {resolve, dirname} = require('path');
|
|
7
8
|
|
|
8
9
|
function handleTypeScript(cwd) {
|
|
9
10
|
const cypressConfig = getCypressConfig(cwd);
|
|
@@ -17,5 +18,16 @@ function handleTypeScript(cwd) {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
function handlerTypeScriptCypress10(suportFilePath) {
|
|
22
|
+
const supportDir = dirname(suportFilePath);
|
|
23
|
+
const typeScriptFilePath = resolve(supportDir, 'index.d.ts');
|
|
24
|
+
if (!existsSync(typeScriptFilePath)) {
|
|
25
|
+
writeFileSync(typeScriptFilePath, eyesIndexContent);
|
|
26
|
+
console.log(chalk.cyan('Typescript defined.'));
|
|
27
|
+
} else {
|
|
28
|
+
console.log(chalk.cyan('Typescript already defined.'));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {handleTypeScript, handlerTypeScriptCypress10};
|
|
21
33
|
module.exports.eyesIndexContent = eyesIndexContent;
|
package/dist/plugin/handler.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.makeHandler = void 0;
|
|
4
|
-
const http_1 = require("http");
|
|
5
|
-
const ws_1 = require("ws");
|
|
6
|
-
const { name, version } = require('../../package.json'); // TODO is the handshake needed at all?
|
|
7
|
-
const TOKEN_HEADER = 'x-eyes-universal-token';
|
|
8
|
-
const TOKEN = `${name}@${version}`;
|
|
9
|
-
// TODO make default port 0 and return actual port
|
|
10
|
-
async function makeHandler({ port = 31077, singleton = true, lazy = false } = {}) {
|
|
11
|
-
const http = new http_1.Server();
|
|
12
|
-
http.on('request', (request, response) => {
|
|
13
|
-
if (request.url === '/handshake') {
|
|
14
|
-
if (request.headers[TOKEN_HEADER] === TOKEN) {
|
|
15
|
-
response.writeHead(200, { [TOKEN_HEADER]: TOKEN });
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
response.writeHead(400);
|
|
19
|
-
}
|
|
20
|
-
response.end();
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
http.listen(port, 'localhost');
|
|
24
|
-
return new Promise((resolve, reject) => {
|
|
25
|
-
http.on('listening', () => {
|
|
26
|
-
const ws = new ws_1.Server({ server: http, path: '/eyes' });
|
|
27
|
-
ws.on('close', () => http.close());
|
|
28
|
-
resolve({ server: ws, port });
|
|
29
|
-
});
|
|
30
|
-
http.on('error', async (err) => {
|
|
31
|
-
if (!lazy && err.code === 'EADDRINUSE') {
|
|
32
|
-
if (singleton && (await isHandshakable(port))) {
|
|
33
|
-
return resolve({ port });
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
return resolve(await makeHandler({ port: port + 1, singleton }));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
reject(err);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
exports.makeHandler = makeHandler;
|
|
44
|
-
async function isHandshakable(port) {
|
|
45
|
-
return new Promise(resolve => {
|
|
46
|
-
const handshake = (0, http_1.request)(`http://localhost:${port}/handshake`, {
|
|
47
|
-
headers: { [TOKEN_HEADER]: TOKEN },
|
|
48
|
-
});
|
|
49
|
-
handshake.on('response', ({ statusCode, headers }) => {
|
|
50
|
-
resolve(statusCode === 200 && headers[TOKEN_HEADER] === TOKEN);
|
|
51
|
-
});
|
|
52
|
-
handshake.on('error', () => resolve(false));
|
|
53
|
-
handshake.end();
|
|
54
|
-
});
|
|
55
|
-
}
|
package/src/plugin/handler.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import {Server as HTTPServer, request} from 'http'
|
|
2
|
-
import {Server as WSServer} from 'ws'
|
|
3
|
-
|
|
4
|
-
const {name, version} = require('../../package.json') // TODO is the handshake needed at all?
|
|
5
|
-
const TOKEN_HEADER = 'x-eyes-universal-token'
|
|
6
|
-
const TOKEN = `${name}@${version}`
|
|
7
|
-
|
|
8
|
-
// TODO make default port 0 and return actual port
|
|
9
|
-
export async function makeHandler({port = 31077, singleton = true, lazy = false} = {}): Promise<{
|
|
10
|
-
server?: WSServer
|
|
11
|
-
port: number
|
|
12
|
-
}> {
|
|
13
|
-
const http = new HTTPServer()
|
|
14
|
-
http.on('request', (request, response) => {
|
|
15
|
-
if (request.url === '/handshake') {
|
|
16
|
-
if (request.headers[TOKEN_HEADER] === TOKEN) {
|
|
17
|
-
response.writeHead(200, {[TOKEN_HEADER]: TOKEN})
|
|
18
|
-
} else {
|
|
19
|
-
response.writeHead(400)
|
|
20
|
-
}
|
|
21
|
-
response.end()
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
http.listen(port, 'localhost')
|
|
26
|
-
|
|
27
|
-
return new Promise((resolve, reject) => {
|
|
28
|
-
http.on('listening', () => {
|
|
29
|
-
const ws = new WSServer({server: http, path: '/eyes'})
|
|
30
|
-
ws.on('close', () => http.close())
|
|
31
|
-
resolve({server: ws, port})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
http.on('error', async (err: Error & {code: string}) => {
|
|
35
|
-
if (!lazy && err.code === 'EADDRINUSE') {
|
|
36
|
-
if (singleton && (await isHandshakable(port))) {
|
|
37
|
-
return resolve({port})
|
|
38
|
-
} else {
|
|
39
|
-
return resolve(await makeHandler({port: port + 1, singleton}))
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
reject(err)
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function isHandshakable(port: number) {
|
|
48
|
-
return new Promise(resolve => {
|
|
49
|
-
const handshake = request(`http://localhost:${port}/handshake`, {
|
|
50
|
-
headers: {[TOKEN_HEADER]: TOKEN},
|
|
51
|
-
})
|
|
52
|
-
handshake.on('response', ({statusCode, headers}) => {
|
|
53
|
-
resolve(statusCode === 200 && headers[TOKEN_HEADER] === TOKEN)
|
|
54
|
-
})
|
|
55
|
-
handshake.on('error', () => resolve(false))
|
|
56
|
-
handshake.end()
|
|
57
|
-
})
|
|
58
|
-
}
|