@applitools/eyes-cypress 3.25.7 → 3.26.2
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 +29 -0
- package/README.md +159 -42
- package/dist/browser/spec-driver.js +10 -13
- package/index.d.ts +89 -0
- package/package.json +43 -45
- package/src/browser/commands.js +5 -4
- package/src/browser/eyesCheckMapping.js +104 -37
- package/src/browser/spec-driver.ts +9 -12
- package/src/plugin/config.js +4 -3
- package/src/plugin/server.js +31 -6
- package/src/setup/getFilePath.js +2 -2
- package/dist/plugin/handler.js +0 -55
- package/eyes-index.d.ts +0 -34
- package/src/plugin/handler.ts +0 -58
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,35 @@
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## 3.26.2 - 2022/6/8
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
- add support for secure websocket
|
|
16
|
+
- Allowed `` values in custom properties
|
|
17
|
+
### Bug fixes
|
|
18
|
+
- Fix incorrect test results when dealing with a page that contains CORS iframes
|
|
19
|
+
- Fixed broken links to enums implementation in the README.md
|
|
20
|
+
- Fix calling `waitBeforeCapture` when failed to set required viewport size
|
|
21
|
+
|
|
22
|
+
## 3.26.1 - 2022/6/2
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
- Improve type definitions
|
|
26
|
+
- Dorp support for Node.js versions <=12
|
|
27
|
+
### Bug fixes
|
|
28
|
+
- Support `cy.eyesGetAllTestResults` in TypeScript
|
|
29
|
+
- Set EyesExceptions (such as new test, diffs exception and failed test exception) to exception property in TestResultsSummary
|
|
30
|
+
- Improve error message when failed to set viewport size
|
|
31
|
+
|
|
32
|
+
## 3.26.0 - 2022/5/19
|
|
33
|
+
|
|
34
|
+
### Features
|
|
35
|
+
- `cy.eyesCheckWindow` now supports passing DOM elements and jQuery object for target and regions.
|
|
36
|
+
### Bug fixes
|
|
37
|
+
- Support `cy.eyesGetAllTestResults` in TypeScript
|
|
38
|
+
|
|
10
39
|
## 3.25.7 - 2022/5/6
|
|
11
40
|
|
|
12
41
|
### Features
|
package/README.md
CHANGED
|
@@ -173,43 +173,66 @@ Applitools will take screenshots and perform the visual comparisons in the backg
|
|
|
173
173
|
<br/>
|
|
174
174
|
|
|
175
175
|
### 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
|
-
- [
|
|
176
|
+
- [Eyes-Cypress](#eyes-cypress)
|
|
177
|
+
- [Installation](#installation)
|
|
178
|
+
- [Install npm package](#install-npm-package)
|
|
179
|
+
- [Configure plugin and commands](#configure-plugin-and-commands)
|
|
180
|
+
- [Automatic configuration](#automatic-configuration)
|
|
181
|
+
- [Manual configuration](#manual-configuration)
|
|
182
|
+
- [1. Configure Eyes-Cypress plugin](#1-configure-eyes-cypress-plugin)
|
|
183
|
+
- [2. Configure custom commands](#2-configure-custom-commands)
|
|
184
|
+
- [3. (Optional) TypeScript configuration](#3-optional-typescript-configuration)
|
|
185
|
+
- [Applitools API key](#applitools-api-key)
|
|
186
|
+
- [Eyes server URL (optional)](#eyes-server-url-optional)
|
|
187
|
+
- [Usage](#usage)
|
|
188
|
+
- [Example](#example)
|
|
189
|
+
- [Best practice for using the SDK](#best-practice-for-using-the-sdk)
|
|
190
|
+
- [Index](#index)
|
|
191
|
+
- [Commands](#commands)
|
|
192
|
+
- [Open](#open)
|
|
193
|
+
- [Check window](#check-window)
|
|
194
|
+
- [Arguments to `cy.eyesCheckWindow`](#arguments-to-cyeyescheckwindow)
|
|
195
|
+
- [`tag`](#tag)
|
|
196
|
+
- [`target`](#target)
|
|
197
|
+
- [`fully`](#fully)
|
|
198
|
+
- [`selector`](#selector)
|
|
199
|
+
- [`region`](#region)
|
|
200
|
+
- [`element`](#element)
|
|
201
|
+
- [`ignore`](#ignore)
|
|
202
|
+
- [`floating`](#floating)
|
|
203
|
+
- [`layout`](#layout)
|
|
204
|
+
- [`strict`](#strict)
|
|
205
|
+
- [`content`](#content)
|
|
206
|
+
- [`accessibility`](#accessibility)
|
|
207
|
+
- [`region in shadow DOM`](#region-in-shadow-dom)
|
|
208
|
+
- [`scriptHooks`](#scripthooks)
|
|
209
|
+
- [`layoutBreakpoints`](#layoutbreakpoints)
|
|
210
|
+
- [`sendDom`](#senddom)
|
|
211
|
+
- [`variationGroupId`](#variationgroupid)
|
|
212
|
+
- [`waitBeforeCapture`](#waitbeforecapture)
|
|
213
|
+
- [`useDom`](#usedom)
|
|
214
|
+
- [`enablePatterns`](#enablepatterns)
|
|
215
|
+
- [`matchLevel`](#matchlevel)
|
|
216
|
+
- [`visualGridOptions`](#visualgridoptions)
|
|
217
|
+
- [Close](#close)
|
|
218
|
+
- [GetAllTestResults](#getalltestresults)
|
|
219
|
+
- [deleteTestResults](#deletetestresults)
|
|
220
|
+
- [Concurrency](#concurrency)
|
|
221
|
+
- [Advanced configuration](#advanced-configuration)
|
|
222
|
+
- [Here are the available configuration properties:](#here-are-the-available-configuration-properties)
|
|
223
|
+
- [Global configuration properties:](#global-configuration-properties)
|
|
224
|
+
- [Method 1: Arguments for `cy.eyesOpen`](#method-1-arguments-for-cyeyesopen)
|
|
225
|
+
- [Method 2: Environment variables](#method-2-environment-variables)
|
|
226
|
+
- [Method 3: The `applitools.config.js` file](#method-3-the-applitoolsconfigjs-file)
|
|
227
|
+
- [Configuring the browser](#configuring-the-browser)
|
|
228
|
+
- [Previous browser versions](#previous-browser-versions)
|
|
229
|
+
- [Getting a screenshot of multiple browsers in parallel](#getting-a-screenshot-of-multiple-browsers-in-parallel)
|
|
230
|
+
- [Device emulation](#device-emulation)
|
|
231
|
+
- [iOS device](#ios-device)
|
|
232
|
+
- [Intelligent Code Completion](#intelligent-code-completion)
|
|
233
|
+
- [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)
|
|
234
|
+
- [1. Triple slash directives](#1-triple-slash-directives)
|
|
235
|
+
- [2. Reference type declarations via `tsconfig`](#2-reference-type-declarations-via-tsconfig)
|
|
213
236
|
|
|
214
237
|
<br/><hr/><br/>
|
|
215
238
|
|
|
@@ -255,7 +278,7 @@ cy.eyesCheckWindow({ tag: 'Login screen', target: 'your target' })
|
|
|
255
278
|
<br/> 1. `window`
|
|
256
279
|
This is the default value. If set then the captured image is of the entire page or the viewport, use [`fully`](#fully) for specifying what `window` mode to use.
|
|
257
280
|
<br/>2. `region`
|
|
258
|
-
If set then the captured image is of the parts of the page, use this parameter with [`region`](#region)
|
|
281
|
+
If set then the captured image is of the parts of the page, use this parameter with [`region`](#region), [`selector`](#selector), or [`element`](#element) for specifying the areas to captured.
|
|
259
282
|
|
|
260
283
|
##### `fully`
|
|
261
284
|
|
|
@@ -310,18 +333,72 @@ cy.eyesCheckWindow({ tag: 'Login screen', target: 'your target' })
|
|
|
310
333
|
});
|
|
311
334
|
```
|
|
312
335
|
|
|
336
|
+
##### `element`
|
|
337
|
+
|
|
338
|
+
(optional): In case [`target`](#target) is `region`, this should be an instance of either an HTML element or a jQuery object. For example:
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
// passing a jQuery object
|
|
342
|
+
cy.get('body > div > h1')
|
|
343
|
+
.then($el => {
|
|
344
|
+
cy.eyesCheckWindow({
|
|
345
|
+
target: 'region',
|
|
346
|
+
element: $el
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// passing an HTML element
|
|
351
|
+
cy.document()
|
|
352
|
+
.then(doc => {
|
|
353
|
+
const el = document.querySelector('div')
|
|
354
|
+
cy.eyesCheckWindow({
|
|
355
|
+
target: 'region',
|
|
356
|
+
element: el
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
```
|
|
360
|
+
|
|
313
361
|
##### `ignore`
|
|
314
362
|
|
|
315
363
|
(optional): A single or an array of regions to ignore when checking for visual differences. For example:
|
|
316
364
|
|
|
317
|
-
|
|
365
|
+
```js
|
|
366
|
+
// ignore region by coordinates
|
|
367
|
+
cy.eyesCheckWindow({
|
|
368
|
+
ignore: {top: 100, left: 0, width: 1000, height: 100},
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// ignore regions by selector
|
|
372
|
+
cy.eyesCheckWindow({
|
|
373
|
+
ignore: {selector: '.some-div-to-ignore'} // all elements matching this selector would become ignore regions
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// ignore regions by jQuery or DOM elements
|
|
377
|
+
cy.get('.some-div-to-ignore').then($el => {
|
|
378
|
+
cy.eyesCheckWindow({
|
|
379
|
+
ignore: $el
|
|
380
|
+
});
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
// mix multiple ignore regions with different methods
|
|
384
|
+
cy.eyesCheckWindow({
|
|
385
|
+
ignore: [
|
|
386
|
+
{top: 100, left: 0, width: 1000, height: 100},
|
|
387
|
+
{selector: '.some-div-to-ignore'}
|
|
388
|
+
]
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// mix multiple ignore regions with different methods including element
|
|
392
|
+
cy.get('.some-div-to-ignore').then($el => {
|
|
318
393
|
cy.eyesCheckWindow({
|
|
319
394
|
ignore: [
|
|
320
395
|
{top: 100, left: 0, width: 1000, height: 100},
|
|
321
396
|
{selector: '.some-div-to-ignore'}
|
|
397
|
+
$el
|
|
322
398
|
]
|
|
323
399
|
});
|
|
324
|
-
|
|
400
|
+
})
|
|
401
|
+
```
|
|
325
402
|
|
|
326
403
|
##### `floating`
|
|
327
404
|
|
|
@@ -334,6 +411,15 @@ cy.eyesCheckWindow({
|
|
|
334
411
|
{selector: '.some-div-to-float', maxUpOffset: 20, maxDownOffset: 20, maxLeftOffset: 20, maxRightOffset: 20}
|
|
335
412
|
]
|
|
336
413
|
});
|
|
414
|
+
|
|
415
|
+
// use jQuery or DOM elements
|
|
416
|
+
cy.get('.some-div-to-float').then($el => {
|
|
417
|
+
cy.eyesCheckWindow({
|
|
418
|
+
floating: [
|
|
419
|
+
{element: $el, maxUpOffset: 20, maxDownOffset: 20, maxLeftOffset: 20, maxRightOffset: 20},
|
|
420
|
+
]
|
|
421
|
+
})
|
|
422
|
+
})
|
|
337
423
|
```
|
|
338
424
|
|
|
339
425
|
##### `layout`
|
|
@@ -347,6 +433,13 @@ cy.eyesCheckWindow({
|
|
|
347
433
|
{selector: '.some-div-to-test-as-layout'}
|
|
348
434
|
]
|
|
349
435
|
});
|
|
436
|
+
|
|
437
|
+
// use jQuery or DOM elements
|
|
438
|
+
cy.get('.some-div-to-test-as-layout').then($el => {
|
|
439
|
+
cy.eyesCheckWindow({
|
|
440
|
+
layout: $el
|
|
441
|
+
});
|
|
442
|
+
})
|
|
350
443
|
```
|
|
351
444
|
|
|
352
445
|
##### `strict`
|
|
@@ -360,6 +453,13 @@ cy.eyesCheckWindow({
|
|
|
360
453
|
{selector: '.some-div-to-test-as-strict'}
|
|
361
454
|
]
|
|
362
455
|
});
|
|
456
|
+
|
|
457
|
+
// use jQuery or DOM elements
|
|
458
|
+
cy.get('.some-div-to-test-as-strict').then($el => {
|
|
459
|
+
cy.eyesCheckWindow({
|
|
460
|
+
strict: $el
|
|
461
|
+
});
|
|
462
|
+
})
|
|
363
463
|
```
|
|
364
464
|
|
|
365
465
|
##### `content`
|
|
@@ -373,6 +473,13 @@ cy.eyesCheckWindow({
|
|
|
373
473
|
{selector: '.some-div-to-test-as-content'}
|
|
374
474
|
]
|
|
375
475
|
});
|
|
476
|
+
|
|
477
|
+
// use jQuery or DOM elements
|
|
478
|
+
cy.get('.some-div-to-test-as-content').then($el => {
|
|
479
|
+
cy.eyesCheckWindow({
|
|
480
|
+
content: $el
|
|
481
|
+
});
|
|
482
|
+
})
|
|
376
483
|
```
|
|
377
484
|
|
|
378
485
|
##### `accessibility`
|
|
@@ -387,6 +494,16 @@ cy.eyesCheckWindow({
|
|
|
387
494
|
{accessibilityType: 'BoldText', top: 100, left: 0, width: 1000, height: 100},
|
|
388
495
|
]
|
|
389
496
|
});
|
|
497
|
+
|
|
498
|
+
// use jQuery or DOM elements
|
|
499
|
+
cy.get('.some-div').then($el => {
|
|
500
|
+
cy.eyesCheckWindow({
|
|
501
|
+
accessibility: [
|
|
502
|
+
{accessibilityType: 'RegularText', element: $el},
|
|
503
|
+
]
|
|
504
|
+
});
|
|
505
|
+
})
|
|
506
|
+
|
|
390
507
|
```
|
|
391
508
|
|
|
392
509
|
Possible accessibilityType values are: `IgnoreContrast`,`RegularText`,`LargeText`,`BoldText` and `GraphicalObject`.
|
|
@@ -733,7 +850,7 @@ cy.eyesOpen({
|
|
|
733
850
|
|
|
734
851
|
Possible values for screen orientation are `landscape` and `portrait`, and if no value is specified, the default is `portrait`.
|
|
735
852
|
|
|
736
|
-
The list of device names is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-
|
|
853
|
+
The list of device names is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/DeviceName.ts
|
|
737
854
|
|
|
738
855
|
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:
|
|
739
856
|
|
|
@@ -765,7 +882,7 @@ cy.eyesOpen({
|
|
|
765
882
|
})
|
|
766
883
|
```
|
|
767
884
|
|
|
768
|
-
The list of devices is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-
|
|
885
|
+
The list of devices is available at https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/IosDeviceName.ts
|
|
769
886
|
|
|
770
887
|
Possible values for `iosVersion` are:
|
|
771
888
|
|
|
@@ -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/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
import type * as types from '@applitools/types'
|
|
4
|
+
import type * as api from '@applitools/eyes-api'
|
|
5
|
+
|
|
6
|
+
type MaybeArray<T> = T | T[]
|
|
7
|
+
|
|
8
|
+
type LegacyRegion = {left: number; top: number; width: number; height: number}
|
|
9
|
+
type Selector = {selector: string; type?: 'css' | 'xpath', nodeType?: 'element' | 'shadow-root'} | 'string'
|
|
10
|
+
type Element = HTMLElement | JQuery<HTMLElement>
|
|
11
|
+
|
|
12
|
+
interface CypressCheckSettings extends types.CheckSettings<Element, Selector> {
|
|
13
|
+
tag?: CypressCheckSettings['name']
|
|
14
|
+
|
|
15
|
+
target?: 'window' | 'region'
|
|
16
|
+
selector?: Selector
|
|
17
|
+
element?: Element
|
|
18
|
+
|
|
19
|
+
ignore?: MaybeArray<CypressCheckSettings['ignoreRegions'][number] | LegacyRegion>
|
|
20
|
+
layout?: MaybeArray<CypressCheckSettings['layoutRegions'][number] | LegacyRegion>
|
|
21
|
+
content?: MaybeArray<CypressCheckSettings['contentRegions'][number] | LegacyRegion>
|
|
22
|
+
strict?: MaybeArray<CypressCheckSettings['strictRegions'][number] | LegacyRegion>
|
|
23
|
+
floating?: MaybeArray<CypressCheckSettings['floatingRegions'][number] | (({element: Element} | Selector | LegacyRegion) & {maxUpOffset?: number; maxDownOffset?: number; maxLeftOffset?: number; maxRightOffset?: number})>
|
|
24
|
+
accessibility?: MaybeArray<CypressCheckSettings['accessibilityRegions'][number] | (({element: Element} | Selector | LegacyRegion) & {accessibilityType?: types.AccessibilityRegionType})>
|
|
25
|
+
|
|
26
|
+
scriptHooks?: CypressCheckSettings['hooks']
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface CypressEyesConfig extends types.EyesConfig<Element, Selector> {
|
|
30
|
+
browser?: MaybeArray<CypressEyesConfig['browsersInfo'][number] | {deviceName: string; screenOrientation?: types.ScreenOrientation; name?: string}>
|
|
31
|
+
|
|
32
|
+
batchId?: CypressEyesConfig['batch']['id']
|
|
33
|
+
batchName?: CypressEyesConfig['batch']['name']
|
|
34
|
+
batchSequence?: CypressEyesConfig['batch']['sequenceName']
|
|
35
|
+
notifyOnCompletion?: CypressEyesConfig['batch']['notifyOnCompletion']
|
|
36
|
+
|
|
37
|
+
envName?: CypressEyesConfig['environmentName']
|
|
38
|
+
|
|
39
|
+
ignoreCaret?: CypressEyesConfig['defaultMatchSettings']['ignoreCaret']
|
|
40
|
+
matchLevel?: CypressEyesConfig['defaultMatchSettings']['matchLevel']
|
|
41
|
+
accessibilitySettings?: CypressEyesConfig['defaultMatchSettings']['accessibilitySettings']
|
|
42
|
+
ignoreDisplacements?: CypressEyesConfig['defaultMatchSettings']['ignoreDisplacements']
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare global {
|
|
46
|
+
namespace Cypress {
|
|
47
|
+
interface Chainable {
|
|
48
|
+
/**
|
|
49
|
+
* Create an Applitools test.
|
|
50
|
+
* This will start a session with the Applitools server.
|
|
51
|
+
* @example
|
|
52
|
+
* cy.eyesOpen({ appName: 'My App' })
|
|
53
|
+
*/
|
|
54
|
+
eyesOpen(config?: CypressEyesConfig): null
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Generate a screenshot of the current page and add it to the Applitools Test.
|
|
58
|
+
* @example
|
|
59
|
+
* cy.eyesCheckWindow()
|
|
60
|
+
*
|
|
61
|
+
* OR
|
|
62
|
+
*
|
|
63
|
+
* cy.eyesCheckWindow({
|
|
64
|
+
* target: 'region',
|
|
65
|
+
* selector: '.my-element'
|
|
66
|
+
* });
|
|
67
|
+
*/
|
|
68
|
+
eyesCheckWindow(tag?: string): null
|
|
69
|
+
eyesCheckWindow(settings?: CypressCheckSettings): null
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Close the applitools test and check that all screenshots are valid.
|
|
73
|
+
* @example cy.eyesClose()
|
|
74
|
+
*/
|
|
75
|
+
eyesClose(): null
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns an object with the applitools test results from a given test / test file. This should be called after close.
|
|
79
|
+
* @example
|
|
80
|
+
* after(() => {
|
|
81
|
+
* cy.eyesGetAllTestResults().then(summary => {
|
|
82
|
+
* console.log(summary)
|
|
83
|
+
* })
|
|
84
|
+
* })
|
|
85
|
+
*/
|
|
86
|
+
eyesGetAllTestResults(): Chainable<api.TestResultsSummary>
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/eyes-cypress",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"
|
|
3
|
+
"version": "3.26.2",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git://github.com/applitools/eyes.sdk.javascript1.git",
|
|
7
|
+
"directory": "packages/eyes-cypress"
|
|
8
|
+
},
|
|
5
9
|
"license": "SEE LICENSE IN LICENSE",
|
|
10
|
+
"main": "index.js",
|
|
11
|
+
"types": "./index.d.ts",
|
|
6
12
|
"bin": {
|
|
7
13
|
"eyes-setup": "./bin/eyes-setup.js"
|
|
8
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"dist",
|
|
18
|
+
"bin",
|
|
19
|
+
"index.js",
|
|
20
|
+
"commands.js",
|
|
21
|
+
"index.d.ts"
|
|
22
|
+
],
|
|
9
23
|
"scripts": {
|
|
24
|
+
"lint": "eslint \"**/*.js\"",
|
|
10
25
|
"build": "tsc",
|
|
11
|
-
"
|
|
26
|
+
"generate:tests": "coverage-tests generate",
|
|
27
|
+
"test": "yarn test:unit && yarn test:it && yarn test:e2e && yarn test:ts && yarn test:coverage",
|
|
12
28
|
"test:unit": "mocha --no-timeouts 'test/unit/**/*.test.js'",
|
|
13
29
|
"test:it": "mocha --no-timeouts 'test/it/**/*.test.js'",
|
|
14
30
|
"test:e2e": "mkdir -p test/fixtures/testAppCopies && mocha --no-timeouts 'test/e2e/**/*.test.js'",
|
|
15
|
-
"
|
|
16
|
-
"test": "
|
|
31
|
+
"test:ts:compile": "tsc --project test/e2e/ts/cypress",
|
|
32
|
+
"test:ts:run": "cypress run --config-file test/e2e/ts/cypress-ts.json",
|
|
33
|
+
"test:ts": "yarn test:ts:compile && yarn test:ts:run",
|
|
34
|
+
"test:coverage": "yarn generate:tests && cd test/coverage/generic && unset APPLITOOLS_API_KEY && APPLITOOLS_BATCH_NAME='JS Coverage Tests: eyes-selenium' APPLITOOLS_BATCH_ID=$(uuidgen) cypress run",
|
|
17
35
|
"cypress": "cypress open --config-file test/fixtures/cypress-play.json",
|
|
18
36
|
"cypress:new": "node_modules/cypress-new/bin/cypress open --config-file test/fixtures/cypress-play.json",
|
|
19
37
|
"cypress:run": "cypress run --config-file test/fixtures/cypress-play.json --spec=test/fixtures/testApp/cypress/integration-play/play.js",
|
|
20
38
|
"cypress:run:new": "node_modules/cypress-new/bin/cypress run --config-file test/fixtures/cypress-play.json --spec=test/fixtures/testApp/cypress/integration-play/play.js",
|
|
21
39
|
"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
|
+
"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
|
+
"prepublish:setup": "sudo apt-get install xvfb",
|
|
42
|
+
"deps": "bongo deps",
|
|
22
43
|
"preversion": "yarn build && bongo preversion --skip-deps --verifyPendingChanges",
|
|
23
44
|
"version": "bongo version --withPendingChanges",
|
|
24
|
-
"postversion": "bongo postversion --skip-release-notification"
|
|
25
|
-
"deps": "bongo deps",
|
|
26
|
-
"generate:tests": "coverage-tests generate",
|
|
27
|
-
"test:coverage": "yarn generate:tests && cd test/coverage/generic && unset APPLITOOLS_API_KEY && APPLITOOLS_BATCH_NAME='JS Coverage Tests: eyes-selenium' APPLITOOLS_BATCH_ID=$(uuidgen) cypress run",
|
|
28
|
-
"prepublish:setup": "sudo apt-get install xvfb"
|
|
29
|
-
},
|
|
30
|
-
"files": [
|
|
31
|
-
"src",
|
|
32
|
-
"dist",
|
|
33
|
-
"bin",
|
|
34
|
-
"index.js",
|
|
35
|
-
"commands.js",
|
|
36
|
-
"eyes-index.d.ts"
|
|
37
|
-
],
|
|
38
|
-
"types": "./eyes-index.d.ts",
|
|
39
|
-
"engines": {
|
|
40
|
-
"node": ">=8.0.0"
|
|
45
|
+
"postversion": "bongo postversion --skip-release-notification"
|
|
41
46
|
},
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
"husky": {
|
|
48
|
+
"hooks": {
|
|
49
|
+
"pre-push": "yarn bongo lint"
|
|
50
|
+
}
|
|
46
51
|
},
|
|
47
52
|
"dependencies": {
|
|
48
|
-
"@applitools/
|
|
49
|
-
"@applitools/eyes-
|
|
50
|
-
"@applitools/eyes-universal": "2.5.1",
|
|
53
|
+
"@applitools/eyes-api": "1.5.4",
|
|
54
|
+
"@applitools/eyes-universal": "2.7.1",
|
|
51
55
|
"@applitools/functional-commons": "1.6.0",
|
|
52
|
-
"@applitools/logger": "1.1.
|
|
53
|
-
"@applitools/visual-grid-client": "15.12.
|
|
54
|
-
"body-parser": "1.19.0",
|
|
56
|
+
"@applitools/logger": "1.1.10",
|
|
57
|
+
"@applitools/visual-grid-client": "15.12.41",
|
|
55
58
|
"chalk": "3.0.0",
|
|
56
|
-
"cors": "2.8.5",
|
|
57
|
-
"express": "4.17.1",
|
|
58
|
-
"lodash.flatten": "4.4.0",
|
|
59
59
|
"uuid": "8.3.2",
|
|
60
60
|
"ws": "8.5.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@applitools/bongo": "^2.
|
|
63
|
+
"@applitools/bongo": "^2.1.4",
|
|
64
64
|
"@applitools/scripts": "1.1.0",
|
|
65
65
|
"@applitools/sdk-coverage-tests": "^2.3.18",
|
|
66
66
|
"@applitools/snaptdout": "1.0.1",
|
|
67
|
-
"@applitools/test-server": "1.0.
|
|
67
|
+
"@applitools/test-server": "1.0.10",
|
|
68
68
|
"@applitools/test-utils": "1.3.2",
|
|
69
|
-
"@applitools/types": "^1.4.
|
|
70
|
-
"@applitools/utils": "1.
|
|
71
|
-
"@types/node": "
|
|
69
|
+
"@applitools/types": "^1.4.7",
|
|
70
|
+
"@applitools/utils": "1.3.6",
|
|
71
|
+
"@types/node": "12",
|
|
72
72
|
"@types/ws": "^8.2.2",
|
|
73
73
|
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
|
74
74
|
"@typescript-eslint/parser": "^5.10.2",
|
|
@@ -88,11 +88,9 @@
|
|
|
88
88
|
"ncp": "2.0.0",
|
|
89
89
|
"node-fetch": "2.6.0",
|
|
90
90
|
"prettier": "1.19.1",
|
|
91
|
-
"typescript": "
|
|
91
|
+
"typescript": "4.6.4"
|
|
92
92
|
},
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"pre-push": "yarn bongo lint"
|
|
96
|
-
}
|
|
93
|
+
"engines": {
|
|
94
|
+
"node": ">=12.13.0"
|
|
97
95
|
}
|
|
98
96
|
}
|
package/src/browser/commands.js
CHANGED
|
@@ -7,7 +7,6 @@ const {socketCommands} = require('./socketCommands');
|
|
|
7
7
|
const {eyesOpenMapValues} = require('./eyesOpenMapping');
|
|
8
8
|
const {eyesCheckMapValues} = require('./eyesCheckMapping');
|
|
9
9
|
const {TestResultsSummary} = require('@applitools/eyes-api');
|
|
10
|
-
|
|
11
10
|
const refer = new Refer(value => {
|
|
12
11
|
if (!value || !value.constructor || !value.constructor.name) return false;
|
|
13
12
|
const name = value.constructor.name;
|
|
@@ -111,7 +110,7 @@ Cypress.Commands.add('eyesOpen', function(args = {}) {
|
|
|
111
110
|
const driver = refer.ref(cy.state('window').document);
|
|
112
111
|
|
|
113
112
|
if (!connectedToUniversal) {
|
|
114
|
-
socket.connect(`
|
|
113
|
+
socket.connect(`wss://localhost:${Cypress.config('eyesPort')}/eyes`);
|
|
115
114
|
connectedToUniversal = true;
|
|
116
115
|
socket.emit('Core.makeSDK', {
|
|
117
116
|
name: 'eyes.cypress',
|
|
@@ -148,19 +147,21 @@ Cypress.Commands.add('eyesOpen', function(args = {}) {
|
|
|
148
147
|
});
|
|
149
148
|
});
|
|
150
149
|
|
|
151
|
-
Cypress.Commands.add('eyesCheckWindow', args =>
|
|
150
|
+
Cypress.Commands.add('eyesCheckWindow', (args = {}) =>
|
|
152
151
|
cy.then({timeout: 86400000}, () => {
|
|
153
152
|
if (isCurrentTestDisabled) return;
|
|
154
153
|
|
|
155
154
|
setRootContext();
|
|
155
|
+
const driver = refer.ref(cy.state('window').document);
|
|
156
156
|
|
|
157
157
|
Cypress.log({name: 'Eyes: check window'});
|
|
158
158
|
|
|
159
|
-
const checkSettings = eyesCheckMapValues({args});
|
|
159
|
+
const checkSettings = eyesCheckMapValues({args, refer});
|
|
160
160
|
|
|
161
161
|
return socket.request('Eyes.check', {
|
|
162
162
|
eyes,
|
|
163
163
|
settings: checkSettings,
|
|
164
|
+
driver,
|
|
164
165
|
});
|
|
165
166
|
}),
|
|
166
167
|
);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function toCheckWindowConfiguration(config = {}) {
|
|
1
|
+
/* global Node */
|
|
2
|
+
function eyesCheckMapValues({args, refer}) {
|
|
3
|
+
const config = args; // just did it for having less git changes at this moment
|
|
6
4
|
const mappedValues = [
|
|
7
5
|
'tag',
|
|
8
6
|
'scriptHooks',
|
|
@@ -21,17 +19,28 @@ function toCheckWindowConfiguration(config = {}) {
|
|
|
21
19
|
const checkSettings = {
|
|
22
20
|
name: config.tag,
|
|
23
21
|
hooks: config.scriptHooks,
|
|
24
|
-
ignoreRegions: config.ignore,
|
|
22
|
+
ignoreRegions: refElements(config.ignore),
|
|
25
23
|
floatingRegions: convertFloatingRegion(config.floating),
|
|
26
|
-
strictRegions: config.strict,
|
|
27
|
-
layoutRegions: config.layout,
|
|
28
|
-
contentRegions: config.content,
|
|
24
|
+
strictRegions: refElements(config.strict),
|
|
25
|
+
layoutRegions: refElements(config.layout),
|
|
26
|
+
contentRegions: refElements(config.content),
|
|
29
27
|
accessibilityRegions: convertAccessabilityRegions(config.accessibility),
|
|
30
28
|
};
|
|
31
29
|
|
|
32
30
|
if (config.target === 'region') {
|
|
33
31
|
if (!Array.isArray(config.selector)) {
|
|
34
|
-
if (
|
|
32
|
+
if (config.element) {
|
|
33
|
+
if (isHTMLElement(config.element)) {
|
|
34
|
+
regionSettings = {
|
|
35
|
+
region: refer.ref(config.element),
|
|
36
|
+
};
|
|
37
|
+
} else {
|
|
38
|
+
// JQuery element
|
|
39
|
+
regionSettings = {
|
|
40
|
+
region: refer.ref(config.element[0]),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
} else if (!config.hasOwnProperty('selector')) {
|
|
35
44
|
regionSettings = {
|
|
36
45
|
region: config.region,
|
|
37
46
|
};
|
|
@@ -66,39 +75,97 @@ function toCheckWindowConfiguration(config = {}) {
|
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
return Object.assign({}, checkSettings, regionSettings, config);
|
|
69
|
-
}
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
if (!accessibilityRegions) return accessibilityRegions;
|
|
79
|
+
// #region helper functions
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
function convertAccessabilityRegions(accessibilityRegions) {
|
|
82
|
+
if (!accessibilityRegions) return accessibilityRegions;
|
|
83
|
+
const accessibility = [];
|
|
84
|
+
|
|
85
|
+
accessibilityRegions.map(region => {
|
|
86
|
+
const accessabilityRegion = {
|
|
87
|
+
type: region.accessibilityType,
|
|
88
|
+
};
|
|
89
|
+
if (region.hasOwnProperty('selector')) {
|
|
90
|
+
accessabilityRegion.region = region.selector;
|
|
91
|
+
accessibility.push(accessabilityRegion);
|
|
92
|
+
} else if (region.hasOwnProperty('element')) {
|
|
93
|
+
const elements = refElements(region.element);
|
|
94
|
+
for (const element of elements) {
|
|
95
|
+
accessibility.push(Object.assign({}, accessabilityRegion, {region: element}));
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
accessabilityRegion.region = {
|
|
99
|
+
top: region.top,
|
|
100
|
+
left: region.left,
|
|
101
|
+
width: region.width,
|
|
102
|
+
height: region.height,
|
|
103
|
+
};
|
|
104
|
+
accessibility.push(accessabilityRegion);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
79
107
|
|
|
80
|
-
|
|
81
|
-
|
|
108
|
+
return accessibility;
|
|
109
|
+
}
|
|
82
110
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
floatingRegion.region = {
|
|
94
|
-
top: region.top,
|
|
95
|
-
left: region.left,
|
|
96
|
-
width: region.width,
|
|
97
|
-
height: region.height,
|
|
111
|
+
function convertFloatingRegion(floatingRegions) {
|
|
112
|
+
if (!floatingRegions) return floatingRegions;
|
|
113
|
+
const floating = [];
|
|
114
|
+
|
|
115
|
+
for (const region of floatingRegions) {
|
|
116
|
+
const floatingRegion = {
|
|
117
|
+
maxDownOffset: region.maxDownOffset || 0,
|
|
118
|
+
maxLeftOffset: region.maxLeftOffset || 0,
|
|
119
|
+
maxUpOffset: region.maxUpOffset || 0,
|
|
120
|
+
maxRightOffset: region.maxRightOffset || 0,
|
|
98
121
|
};
|
|
122
|
+
if (region.hasOwnProperty('selector')) {
|
|
123
|
+
floatingRegion.region = region.selector;
|
|
124
|
+
floating.push(floatingRegion);
|
|
125
|
+
} else if (region.hasOwnProperty('element')) {
|
|
126
|
+
const elements = refElements(region.element);
|
|
127
|
+
for (const element of elements) {
|
|
128
|
+
floating.push(Object.assign({}, floatingRegion, {region: element}));
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
floatingRegion.region = {
|
|
132
|
+
top: region.top,
|
|
133
|
+
left: region.left,
|
|
134
|
+
width: region.width,
|
|
135
|
+
height: region.height,
|
|
136
|
+
};
|
|
137
|
+
floating.push(floatingRegion);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return floating;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function refElements(regions) {
|
|
144
|
+
if (!regions) return regions;
|
|
145
|
+
if (!Array.isArray(regions)) regions = [regions];
|
|
146
|
+
const elements = [];
|
|
147
|
+
for (const region of regions) {
|
|
148
|
+
if (isHTMLElement(region)) {
|
|
149
|
+
elements.push(refer.ref(region));
|
|
150
|
+
} else if (region.jquery) {
|
|
151
|
+
region.each(function() {
|
|
152
|
+
// there's a small chance that `this` is not an HTML element. So we just verify it.
|
|
153
|
+
elements.push(isHTMLElement(this) ? refer.ref(this) : this);
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
elements.push(region);
|
|
157
|
+
}
|
|
99
158
|
}
|
|
100
|
-
return
|
|
101
|
-
}
|
|
159
|
+
return elements;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// #endregion
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function isHTMLElement(element) {
|
|
166
|
+
// Avoiding instanceof here since the element might come from an iframe, and `instanceof HTMLElement` would fail.
|
|
167
|
+
// This check looks naive, but if anyone passes something like {nodeType: 1} as a region, then I'm fine with them crashing :)
|
|
168
|
+
return element.nodeType && element.nodeType === Node.ELEMENT_NODE;
|
|
102
169
|
}
|
|
103
170
|
|
|
104
171
|
module.exports = {eyesCheckMapValues};
|
|
@@ -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
|
|
package/src/plugin/config.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const
|
|
2
|
+
const utils = require('@applitools/utils');
|
|
3
|
+
const {configParams, TypeUtils} = require('@applitools/visual-grid-client');
|
|
3
4
|
const DEFAULT_TEST_CONCURRENCY = 5;
|
|
4
5
|
const uuid = require('uuid');
|
|
5
6
|
|
|
6
7
|
function makeConfig() {
|
|
7
|
-
const config =
|
|
8
|
-
|
|
8
|
+
const config = utils.config.getConfig({
|
|
9
|
+
params: [
|
|
9
10
|
...configParams,
|
|
10
11
|
'failCypressOnDiff',
|
|
11
12
|
'tapDirPath',
|
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,
|
package/src/setup/getFilePath.js
CHANGED
|
@@ -6,12 +6,12 @@ function getFilePath(type, cypressConfig, cwd) {
|
|
|
6
6
|
let filePath = {
|
|
7
7
|
plugins: join('cypress', 'plugins', 'index.js'),
|
|
8
8
|
support: join('cypress', 'support', 'index.js'),
|
|
9
|
-
typeScript: join('cypress', 'support', '
|
|
9
|
+
typeScript: join('cypress', 'support', 'index.d.ts'),
|
|
10
10
|
}[type];
|
|
11
11
|
|
|
12
12
|
if (type === 'typeScript' && cypressConfig && cypressConfig[`supportFile`]) {
|
|
13
13
|
const supportDir = dirname(cypressConfig[`supportFile`]);
|
|
14
|
-
filePath = resolve(supportDir, '
|
|
14
|
+
filePath = resolve(supportDir, 'index.d.ts');
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
filePath = (cypressConfig && cypressConfig[`${type}File`]) || filePath;
|
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 = 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/eyes-index.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/// <reference types="Cypress" />
|
|
2
|
-
/// <reference types="@applitools/visual-grid-client" />
|
|
3
|
-
|
|
4
|
-
declare namespace Cypress {
|
|
5
|
-
interface Chainable {
|
|
6
|
-
/**
|
|
7
|
-
* Create an Applitools test.
|
|
8
|
-
* This will start a session with the Applitools server.
|
|
9
|
-
* @example
|
|
10
|
-
* cy.eyesOpen({ appName: 'My App' })
|
|
11
|
-
*/
|
|
12
|
-
eyesOpen(options?: Eyes.Open.Options): null // add isDisabled
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Generate a screenshot of the current page and add it to the Applitools Test.
|
|
16
|
-
* @example
|
|
17
|
-
* cy.eyesCheckWindow()
|
|
18
|
-
*
|
|
19
|
-
* OR
|
|
20
|
-
*
|
|
21
|
-
* cy.eyesCheckWindow({
|
|
22
|
-
* target: 'region',
|
|
23
|
-
* selector: '.my-element'
|
|
24
|
-
* });
|
|
25
|
-
*/
|
|
26
|
-
eyesCheckWindow(config?: Eyes.Check.Options|String): null
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Close the applitools test and check that all screenshots are valid.
|
|
30
|
-
* @example cy.eyesClose()
|
|
31
|
-
*/
|
|
32
|
-
eyesClose(): null
|
|
33
|
-
}
|
|
34
|
-
}
|
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
|
-
}
|