@wdio/visual-service 1.0.0 โ 3.0.0
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 +91 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/service.d.ts +6 -2
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +58 -20
- package/dist/utils.d.ts +21 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +135 -29
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,96 @@
|
|
|
1
1
|
# @wdio/visual-service
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- fd74a35: (feat): set default baseline folder next to test file
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- e93a878: fix default snapshot path to be overwritten through method/service options
|
|
12
|
+
- Updated dependencies [fd74a35]
|
|
13
|
+
- webdriver-image-comparison@4.0.1
|
|
14
|
+
|
|
15
|
+
## 2.0.0
|
|
16
|
+
|
|
17
|
+
### Major Changes
|
|
18
|
+
|
|
19
|
+
- ef386b6: # ๐ฅ Breaking changes:
|
|
20
|
+
|
|
21
|
+
- `resizeDimensions` on the element can now only be an object, it has been deprecated for a while
|
|
22
|
+
|
|
23
|
+
# ๐
New Features
|
|
24
|
+
|
|
25
|
+
- Next to supporting Web snapshot testing this module now also supports ๐ฅ **Native App** ๐ฅ snapshot testing. The methods `saveElement|checkElement | saveScreen | checkScreen` and the matchers `toMatchElementSnapshot | toMatchScreenSnapshot` are available for **Native Apps**
|
|
26
|
+
|
|
27
|
+
> [!NOTE]
|
|
28
|
+
> This module will automatically detect the context (web | webview | native_app) and will handle all complex logic for you
|
|
29
|
+
|
|
30
|
+
The methods `saveFullPageScreen | checkFullPageScreen | saveTabbablePage|checkTabbablePage` will throw an error when they are used in the native context for native mobile apps and will look like this
|
|
31
|
+
|
|
32
|
+
```logs
|
|
33
|
+
$ wdio tests/configs/wdio.local.android.emus.app.conf.ts
|
|
34
|
+
|
|
35
|
+
Execution of 1 workers started at 2024-01-30T06:18:24.865Z
|
|
36
|
+
|
|
37
|
+
[0-0] RUNNING in Android - file:///tests/specs/mobile.app.spec.ts
|
|
38
|
+
[0-0] Error in "@wdio/visual-service mobile app.should compare a screen successful for 'Pixel_7_Pro_Android_14_API_34' in PORTRAIT-mode"
|
|
39
|
+
Error: The method saveFullPageScreen is not supported in native context for native mobile apps!
|
|
40
|
+
at /wdio/visual-testing/packages/webdriver-image-comparison/src/commands/saveFullPageScreen.ts:26:15
|
|
41
|
+
at step (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:33:23)
|
|
42
|
+
at Object.next (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:14:53)
|
|
43
|
+
at /wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:8:71
|
|
44
|
+
at new Promise (<anonymous>)
|
|
45
|
+
at __awaiter (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:4:12)
|
|
46
|
+
at saveFullPageScreen (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:47:12)
|
|
47
|
+
at Browser.<anonymous> (file:///wdio/visual-testing/packages/service/dist/service.js:101:24)
|
|
48
|
+
[0-0] FAILED in Android - file:///tests/specs/mobile.app.spec.ts
|
|
49
|
+
|
|
50
|
+
"spec" Reporter:
|
|
51
|
+
------------------------------------------------------------------
|
|
52
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] Running: /wdio/visual-testing/apps/app.apk on Android
|
|
53
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] Session ID: c1101184-e3d5-42b5-a31f-8ebaa211f1a1
|
|
54
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0]
|
|
55
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] ยป /tests/specs/mobile.app.spec.ts
|
|
56
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] @wdio/visual-service mobile app
|
|
57
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] โ should compare a screen successful for 'Pixel_7_Pro_Android_14_API_34' in PORTRAIT-mode
|
|
58
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0]
|
|
59
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] 1 failing (1.5s)
|
|
60
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0]
|
|
61
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] 1) @wdio/visual-service mobile app should compare a screen successful for 'Pixel_7_Pro_Android_14_API_34' in PORTRAIT-mode
|
|
62
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] The method saveFullPageScreen is not supported in native context for native mobile apps!
|
|
63
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] Error: The method saveFullPageScreen is not supported in native context for native mobile apps!
|
|
64
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at /wdio/visual-testing/packages/webdriver-image-comparison/src/commands/saveFullPageScreen.ts:26:15
|
|
65
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at step (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:33:23)
|
|
66
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at Object.next (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:14:53)
|
|
67
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at /wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:8:71
|
|
68
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at new Promise (<anonymous>)
|
|
69
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at __awaiter (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:4:12)
|
|
70
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at saveFullPageScreen (/wdio/visual-testing/packages/webdriver-image-comparison/dist/commands/saveFullPageScreen.js:47:12)
|
|
71
|
+
[/wdio/visual-testing/apps/app.apk Android #0-0] at Browser.<anonymous> (file:///wdio/visual-testing/packages/service/dist/service.js:101:24)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
Spec Files: 0 passed, 1 failed, 1 total (100% completed) in 00:00:11
|
|
75
|
+
|
|
76
|
+
error Command failed with exit code 1.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `autoSaveBaseline` is true by default, so if no baseline images are present it will automatically create a new baseline
|
|
80
|
+
- Mobile screenshots of the complete screen now automatically exclude all native OS elements like the notification bar, home bar, address bar, and so on, the settings `blockOutSideBar | blockOutStatusBar |blockOutToolBar` are now all defaulted to `true`
|
|
81
|
+
-
|
|
82
|
+
|
|
83
|
+
# ๐ Fixed bugs:
|
|
84
|
+
|
|
85
|
+
- element screenshots could also get resized dimensions, which would cut out a bigger portion around the element. This was failing when the dimensions got out of the boundaries of the official screenshot. This has now been fixed with:
|
|
86
|
+
- not going outside of the screenshot
|
|
87
|
+
- log extra warnings
|
|
88
|
+
|
|
89
|
+
### Patch Changes
|
|
90
|
+
|
|
91
|
+
- Updated dependencies [ef386b6]
|
|
92
|
+
- webdriver-image-comparison@4.0.0
|
|
93
|
+
|
|
3
94
|
## 1.0.0
|
|
4
95
|
|
|
5
96
|
### Major Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./expect-webdriverio.js" />
|
|
2
|
+
import type { WicElement } from 'webdriver-image-comparison/dist/commands/element.interfaces.js';
|
|
2
3
|
import WdioImageComparisonService from './service.js';
|
|
3
4
|
import type { Output, Result, WdioCheckFullPageMethodOptions, WdioSaveFullPageMethodOptions, WdioSaveElementMethodOptions, WdioSaveScreenMethodOptions, WdioCheckElementMethodOptions, WdioCheckScreenMethodOptions } from './types.js';
|
|
4
5
|
declare global {
|
|
@@ -7,7 +8,7 @@ declare global {
|
|
|
7
8
|
/**
|
|
8
9
|
* Saves an image of an element
|
|
9
10
|
*/
|
|
10
|
-
saveElement(element:
|
|
11
|
+
saveElement(element: WicElement, tag: string, saveElementOptions?: WdioSaveElementMethodOptions): Promise<Output>;
|
|
11
12
|
/**
|
|
12
13
|
* Saves an image of a viewport
|
|
13
14
|
*/
|
|
@@ -23,7 +24,7 @@ declare global {
|
|
|
23
24
|
/**
|
|
24
25
|
* Compare an image of an element
|
|
25
26
|
*/
|
|
26
|
-
checkElement(element:
|
|
27
|
+
checkElement(element: WicElement, tag: string, checkElementOptions?: WdioCheckElementMethodOptions): Promise<Result>;
|
|
27
28
|
/**
|
|
28
29
|
* Compares an image of a viewport
|
|
29
30
|
*/
|
|
@@ -48,7 +49,7 @@ declare global {
|
|
|
48
49
|
namespace ExpectWebdriverIO {
|
|
49
50
|
interface Matchers<R, T> {
|
|
50
51
|
/**
|
|
51
|
-
*
|
|
52
|
+
* Checks that if current screen matches with snapshot of baseline.
|
|
52
53
|
* @param tag snapshot name
|
|
53
54
|
* @param expectedResult either a number representing a mismatch percentage (defaults to 0) or an asymmetric matcher
|
|
54
55
|
* @param options options to pass into the `checkScreen` method
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,OAAO,0BAA0B,MAAM,cAAc,CAAA;AACrD,OAAO,KAAK,EACR,MAAM,EACN,MAAM,EACN,8BAA8B,EAC9B,6BAA6B,EAC7B,4BAA4B,EAC5B,2BAA2B,EAC3B,6BAA6B,EAC7B,4BAA4B,EAC/B,MAAM,YAAY,CAAA;AAEnB,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,WAAW,CAAC;QAClB,UAAU,OAAO;YACb;;eAEG;YACH,WAAW,CACP,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/service.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import type { Frameworks } from '@wdio/types';
|
|
1
2
|
import type { ClassOptions } from 'webdriver-image-comparison';
|
|
2
3
|
import { BaseClass } from 'webdriver-image-comparison';
|
|
3
4
|
export default class WdioImageComparisonService extends BaseClass {
|
|
4
5
|
#private;
|
|
5
6
|
private _browser?;
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
private _isNativeContext;
|
|
8
|
+
constructor(options: ClassOptions, _: WebdriverIO.Capabilities, config: WebdriverIO.Config);
|
|
9
|
+
before(capabilities: WebdriverIO.Capabilities, _specs: string[], browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser): Promise<void>;
|
|
10
|
+
beforeTest(test: Frameworks.Test): void;
|
|
11
|
+
afterCommand(commandName: string, _args: string[], result: number | string, error: any): void;
|
|
8
12
|
}
|
|
9
13
|
//# sourceMappingURL=service.d.ts.map
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EACH,SAAS,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EACH,SAAS,EAUZ,MAAM,4BAA4B,CAAA;AAqBnC,MAAM,CAAC,OAAO,OAAO,0BAA2B,SAAQ,SAAS;;IAI7D,OAAO,CAAC,QAAQ,CAAC,CAAsD;IACvE,OAAO,CAAC,gBAAgB,CAAqB;gBAEjC,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM;IAMpF,MAAM,CACR,YAAY,EAAE,WAAW,CAAC,YAAY,EACtC,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB;IAuBjE,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI;IAKhC,YAAY,CAAE,WAAW,EAAC,MAAM,EAAE,KAAK,EAAC,MAAM,EAAE,EAAE,MAAM,EAAC,MAAM,GAAC,MAAM,EAAE,KAAK,EAAC,GAAG;CA6HpF"}
|
package/dist/service.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import logger from '@wdio/logger';
|
|
2
2
|
import { expect } from '@wdio/globals';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { dirname, normalize, resolve } from 'node:path';
|
|
4
|
+
import { BaseClass, checkElement, checkFullPageScreen, checkScreen, saveElement, saveFullPageScreen, saveScreen, saveTabbablePage, checkTabbablePage, FOLDERS, } from 'webdriver-image-comparison';
|
|
5
|
+
import { determineNativeContext, getFolders, getInstanceData } from './utils.js';
|
|
5
6
|
import { toMatchScreenSnapshot, toMatchFullPageSnapshot, toMatchElementSnapshot, toMatchTabbablePageSnapshot } from './matcher.js';
|
|
6
7
|
const log = logger('@wdio/visual-service');
|
|
7
8
|
const elementCommands = { saveElement, checkElement };
|
|
@@ -14,18 +15,25 @@ const pageCommands = {
|
|
|
14
15
|
checkTabbablePage,
|
|
15
16
|
};
|
|
16
17
|
export default class WdioImageComparisonService extends BaseClass {
|
|
18
|
+
#config;
|
|
19
|
+
#currentFile;
|
|
20
|
+
#currentFilePath;
|
|
17
21
|
_browser;
|
|
18
|
-
|
|
22
|
+
_isNativeContext;
|
|
23
|
+
constructor(options, _, config) {
|
|
19
24
|
super(options);
|
|
25
|
+
this.#config = config;
|
|
26
|
+
this._isNativeContext = undefined;
|
|
20
27
|
}
|
|
21
|
-
before(capabilities, _specs, browser) {
|
|
28
|
+
async before(capabilities, _specs, browser) {
|
|
22
29
|
this._browser = browser;
|
|
30
|
+
this._isNativeContext = determineNativeContext(this._browser);
|
|
23
31
|
if (!this._browser.isMultiremote) {
|
|
24
32
|
log.info('Adding commands to global browser');
|
|
25
|
-
this.#addCommandsToBrowser(
|
|
33
|
+
await this.#addCommandsToBrowser(this._browser);
|
|
26
34
|
}
|
|
27
35
|
else {
|
|
28
|
-
this.#extendMultiremoteBrowser(capabilities);
|
|
36
|
+
await this.#extendMultiremoteBrowser(capabilities);
|
|
29
37
|
}
|
|
30
38
|
/**
|
|
31
39
|
* add custom matcher for visual comparison
|
|
@@ -37,14 +45,39 @@ export default class WdioImageComparisonService extends BaseClass {
|
|
|
37
45
|
toMatchTabbablePageSnapshot,
|
|
38
46
|
});
|
|
39
47
|
}
|
|
40
|
-
|
|
48
|
+
beforeTest(test) {
|
|
49
|
+
this.#currentFile = test.file;
|
|
50
|
+
this.#currentFilePath = resolve(dirname(test.file), FOLDERS.DEFAULT.BASE);
|
|
51
|
+
}
|
|
52
|
+
afterCommand(commandName, _args, result, error) {
|
|
53
|
+
// This is for the cases where in the E2E tests we switch to a WEBVIEW or back to NATIVE_APP context
|
|
54
|
+
if (commandName === 'getContext' && error === undefined && typeof result === 'string') {
|
|
55
|
+
this._isNativeContext = result.includes('NATIVE');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
#getBaselineFolder() {
|
|
59
|
+
const isDefaultBaselineFolder = normalize(FOLDERS.DEFAULT.BASE) === this.folders.baselineFolder;
|
|
60
|
+
const baselineFolder = (isDefaultBaselineFolder ? this.#currentFilePath : this.folders.baselineFolder);
|
|
61
|
+
/**
|
|
62
|
+
* support `resolveSnapshotPath` WebdriverIO option
|
|
63
|
+
* @ref https://webdriver.io/docs/configuration#resolvesnapshotpath
|
|
64
|
+
*
|
|
65
|
+
* We only use this option if the baselineFolder is the default one, otherwise the
|
|
66
|
+
* service option for setting the baselineFolder should be used
|
|
67
|
+
*/
|
|
68
|
+
if (typeof this.#config.resolveSnapshotPath === 'function' && this.#currentFile && isDefaultBaselineFolder) {
|
|
69
|
+
return this.#config.resolveSnapshotPath(this.#currentFile, '.png');
|
|
70
|
+
}
|
|
71
|
+
return baselineFolder;
|
|
72
|
+
}
|
|
73
|
+
async #extendMultiremoteBrowser(capabilities) {
|
|
41
74
|
const browser = this._browser;
|
|
42
75
|
const browserNames = Object.keys(capabilities);
|
|
43
76
|
log.info(`Adding commands to Multi Browser: ${browserNames.join(', ')}`);
|
|
44
77
|
for (const browserName of browserNames) {
|
|
45
78
|
const multiremoteBrowser = browser;
|
|
46
79
|
const browserInstance = multiremoteBrowser.getInstance(browserName);
|
|
47
|
-
this.#addCommandsToBrowser(
|
|
80
|
+
await this.#addCommandsToBrowser(browserInstance);
|
|
48
81
|
}
|
|
49
82
|
/**
|
|
50
83
|
* Add all the commands to the global browser object that will execute
|
|
@@ -68,32 +101,37 @@ export default class WdioImageComparisonService extends BaseClass {
|
|
|
68
101
|
});
|
|
69
102
|
}
|
|
70
103
|
}
|
|
71
|
-
#addCommandsToBrowser(
|
|
72
|
-
const instanceData = getInstanceData(
|
|
73
|
-
const
|
|
74
|
-
const defaultOptions = this.defaultOptions;
|
|
104
|
+
async #addCommandsToBrowser(currentBrowser) {
|
|
105
|
+
const instanceData = await getInstanceData(currentBrowser);
|
|
106
|
+
const self = this;
|
|
75
107
|
for (const [commandName, command] of Object.entries(elementCommands)) {
|
|
76
108
|
log.info(`Adding element command "${commandName}" to browser object`);
|
|
77
109
|
currentBrowser.addCommand(commandName, function (element, tag, elementOptions = {}) {
|
|
78
110
|
return command({
|
|
79
|
-
executor:
|
|
111
|
+
executor: (script, ...varArgs) => {
|
|
112
|
+
return this.execute.bind(currentBrowser)(script, ...varArgs);
|
|
113
|
+
},
|
|
114
|
+
getElementRect: this.getElementRect.bind(currentBrowser),
|
|
80
115
|
screenShot: this.takeScreenshot.bind(currentBrowser),
|
|
81
|
-
}, instanceData, getFolders(elementOptions, folders), element, tag, {
|
|
82
|
-
wic: defaultOptions,
|
|
116
|
+
}, instanceData, getFolders(elementOptions, self.folders, self.#getBaselineFolder()), element, tag, {
|
|
117
|
+
wic: self.defaultOptions,
|
|
83
118
|
method: elementOptions,
|
|
84
|
-
});
|
|
119
|
+
}, self._isNativeContext);
|
|
85
120
|
});
|
|
86
121
|
}
|
|
87
122
|
for (const [commandName, command] of Object.entries(pageCommands)) {
|
|
88
123
|
log.info(`Adding element command "${commandName}" to browser object`);
|
|
89
124
|
currentBrowser.addCommand(commandName, function (tag, pageOptions = {}) {
|
|
90
125
|
return command({
|
|
91
|
-
executor:
|
|
126
|
+
executor: (script, ...varArgs) => {
|
|
127
|
+
return this.execute.bind(currentBrowser)(script, ...varArgs);
|
|
128
|
+
},
|
|
129
|
+
getElementRect: this.getElementRect.bind(currentBrowser),
|
|
92
130
|
screenShot: this.takeScreenshot.bind(currentBrowser),
|
|
93
|
-
}, instanceData, getFolders(pageOptions, folders), tag, {
|
|
94
|
-
wic: defaultOptions,
|
|
131
|
+
}, instanceData, getFolders(pageOptions, self.folders, self.#getBaselineFolder()), tag, {
|
|
132
|
+
wic: self.defaultOptions,
|
|
95
133
|
method: pageOptions,
|
|
96
|
-
});
|
|
134
|
+
}, self._isNativeContext);
|
|
97
135
|
});
|
|
98
136
|
}
|
|
99
137
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -6,14 +6,32 @@ import type { Folders, InstanceData, CheckScreenMethodOptions, SaveScreenMethodO
|
|
|
6
6
|
* Otherwise, use the values set during instantiation
|
|
7
7
|
*/
|
|
8
8
|
type getFolderMethodOptions = CheckElementMethodOptions | CheckFullPageMethodOptions | CheckScreenMethodOptions | SaveElementMethodOptions | SaveFullPageMethodOptions | SaveScreenMethodOptions;
|
|
9
|
-
export declare function getFolders(methodOptions: getFolderMethodOptions, folders: Folders): Folders;
|
|
9
|
+
export declare function getFolders(methodOptions: getFolderMethodOptions, folders: Folders, currentTestPath: string): Folders;
|
|
10
|
+
/**
|
|
11
|
+
* Get the size of a screenshot in pixels without the device pixel ratio
|
|
12
|
+
*/
|
|
13
|
+
export declare function getScreenshotSize(screenshot: string, devicePixelRation?: number): {
|
|
14
|
+
height: number;
|
|
15
|
+
width: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Get the device pixel ratio
|
|
19
|
+
*/
|
|
20
|
+
export declare function getDevicePixelRatio(screenshot: string, deviceScreenSize: {
|
|
21
|
+
height: number;
|
|
22
|
+
width: number;
|
|
23
|
+
}): number;
|
|
10
24
|
/**
|
|
11
25
|
* Get the instance data
|
|
12
26
|
*/
|
|
13
|
-
export declare function getInstanceData(
|
|
27
|
+
export declare function getInstanceData(currentBrowser: WebdriverIO.Browser): Promise<InstanceData>;
|
|
14
28
|
/**
|
|
15
|
-
*
|
|
29
|
+
* Traverse up the scope chain until browser element was reached
|
|
16
30
|
*/
|
|
17
31
|
export declare function getBrowserObject(elem: WebdriverIO.Element | WebdriverIO.Browser): WebdriverIO.Browser;
|
|
32
|
+
/**
|
|
33
|
+
* We can't say it's native context if the autoWebview is provided and set to true, for all other cases we can say it's native
|
|
34
|
+
*/
|
|
35
|
+
export declare function determineNativeContext(driver: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser): boolean;
|
|
18
36
|
export {};
|
|
19
37
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,OAAO,EACP,YAAY,EACZ,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EAC3B,MAAM,4BAA4B,CAAA;AAOnC;;;;;GAKG;AACH,KAAK,sBAAsB,GACrB,yBAAyB,GACzB,0BAA0B,GAC1B,wBAAwB,GACxB,wBAAwB,GACxB,yBAAyB,GACzB,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,OAAO,EACP,YAAY,EACZ,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EAC3B,MAAM,4BAA4B,CAAA;AAOnC;;;;;GAKG;AACH,KAAK,sBAAsB,GACrB,yBAAyB,GACzB,0BAA0B,GAC1B,wBAAwB,GACxB,wBAAwB,GACxB,yBAAyB,GACzB,uBAAuB,CAAC;AAE9B,wBAAgB,UAAU,CACtB,aAAa,EAAE,sBAAsB,EACrC,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,MAAM,GACxB,OAAO,CAMT;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,iBAAiB,SAAI,GAAG;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB,CAKA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE;IAAC,MAAM,EAAC,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GAAG,MAAM,CAOhH;AAkFD;;GAEG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CA+DhG;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAE,IAAI,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,CAGtG;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAClC,MAAM,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB,GAC7D,OAAO,CAQT"}
|
package/dist/utils.js
CHANGED
|
@@ -1,45 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
export function getFolders(methodOptions, folders) {
|
|
1
|
+
import { IOS_OFFSETS } from 'webdriver-image-comparison';
|
|
2
|
+
export function getFolders(methodOptions, folders, currentTestPath) {
|
|
3
3
|
return {
|
|
4
4
|
actualFolder: methodOptions.actualFolder ?? folders.actualFolder,
|
|
5
|
-
baselineFolder: methodOptions.baselineFolder ??
|
|
5
|
+
baselineFolder: methodOptions.baselineFolder ?? currentTestPath,
|
|
6
6
|
diffFolder: methodOptions.diffFolder ?? folders.diffFolder,
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Get the size of a screenshot in pixels without the device pixel ratio
|
|
11
|
+
*/
|
|
12
|
+
export function getScreenshotSize(screenshot, devicePixelRation = 1) {
|
|
13
|
+
return {
|
|
14
|
+
height: Buffer.from(screenshot, 'base64').readUInt32BE(20) / devicePixelRation,
|
|
15
|
+
width: Buffer.from(screenshot, 'base64').readUInt32BE(16) / devicePixelRation,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the device pixel ratio
|
|
20
|
+
*/
|
|
21
|
+
export function getDevicePixelRatio(screenshot, deviceScreenSize) {
|
|
22
|
+
const screenshotSize = getScreenshotSize(screenshot);
|
|
23
|
+
const devicePixelRatio = Math.round(screenshotSize.width / deviceScreenSize.width) === Math.round(screenshotSize.height / deviceScreenSize.height)
|
|
24
|
+
? Math.round(screenshotSize.width / deviceScreenSize.width)
|
|
25
|
+
: Math.round(screenshotSize.height / deviceScreenSize.width);
|
|
26
|
+
return Math.round(devicePixelRatio);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the mobile instance data
|
|
30
|
+
*/
|
|
31
|
+
async function getMobileInstanceData({ currentBrowser, isAndroid, isMobile }) {
|
|
32
|
+
const deviceScreenSize = {
|
|
33
|
+
height: 0,
|
|
34
|
+
width: 0,
|
|
35
|
+
};
|
|
36
|
+
const devicePlatformRect = {
|
|
37
|
+
statusBar: { height: 0, x: 0, width: 0, y: 0 },
|
|
38
|
+
homeBar: { height: 0, x: 0, width: 0, y: 0 },
|
|
39
|
+
};
|
|
40
|
+
let devicePixelRatio = 1;
|
|
41
|
+
if (isMobile) {
|
|
42
|
+
const currentDriverCapabilities = currentBrowser.capabilities;
|
|
43
|
+
const { height, width } = await currentBrowser.getWindowSize();
|
|
44
|
+
deviceScreenSize.height = height;
|
|
45
|
+
deviceScreenSize.width = width;
|
|
46
|
+
// @TODO: This is al based on PORTRAIT mode
|
|
47
|
+
if (isAndroid && currentDriverCapabilities) {
|
|
48
|
+
// We use a few `@ts-ignore` here because `pixelRatio` and `statBarHeight`
|
|
49
|
+
// are returned by the driver, and not recognized by the types because they are not requested
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
if (currentDriverCapabilities?.pixelRatio !== undefined) {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
devicePixelRatio = currentDriverCapabilities?.pixelRatio;
|
|
54
|
+
}
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
if (currentDriverCapabilities?.statBarHeight !== undefined) {
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
devicePlatformRect.statusBar.height = currentDriverCapabilities?.statBarHeight;
|
|
59
|
+
devicePlatformRect.statusBar.width = width;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// This is to already determine the device pixel ratio if it's not set in the capabilities
|
|
64
|
+
const base64Image = await currentBrowser.takeScreenshot();
|
|
65
|
+
devicePixelRatio = getDevicePixelRatio(base64Image, deviceScreenSize);
|
|
66
|
+
const isIphone = width < 1024 && height < 1024;
|
|
67
|
+
const deviceType = isIphone ? 'IPHONE' : 'IPAD';
|
|
68
|
+
const defaultPortraitHeight = isIphone ? 667 : 1024;
|
|
69
|
+
const portraitHeight = width > height ? width : height;
|
|
70
|
+
const offsetPortraitHeight = Object.keys(IOS_OFFSETS[deviceType]).indexOf(portraitHeight.toString()) > -1 ? portraitHeight : defaultPortraitHeight;
|
|
71
|
+
const currentOffsets = IOS_OFFSETS[deviceType][offsetPortraitHeight].PORTRAIT;
|
|
72
|
+
// NOTE: The values for iOS are based on CSS pixels, so we need to multiply them with the devicePixelRatio,
|
|
73
|
+
// This will NOT be done here but in a central place
|
|
74
|
+
devicePlatformRect.statusBar = {
|
|
75
|
+
y: 0,
|
|
76
|
+
x: 0,
|
|
77
|
+
width,
|
|
78
|
+
height: currentOffsets.STATUS_BAR,
|
|
79
|
+
};
|
|
80
|
+
devicePlatformRect.homeBar = currentOffsets.HOME_BAR;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
devicePixelRatio,
|
|
85
|
+
devicePlatformRect,
|
|
86
|
+
deviceScreenSize,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
9
89
|
/**
|
|
10
90
|
* Get the instance data
|
|
11
91
|
*/
|
|
12
|
-
export function getInstanceData(
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
? capabilities['wdio-ics:options']?.logName ??
|
|
24
|
-
''
|
|
92
|
+
export async function getInstanceData(currentBrowser) {
|
|
93
|
+
const NOT_KNOWN = 'not-known';
|
|
94
|
+
const { capabilities: currentCapabilities, requestedCapabilities } = currentBrowser;
|
|
95
|
+
const { browserName: rawBrowserName = NOT_KNOWN, browserVersion: rawBrowserVersion = NOT_KNOWN, platformName: rawPlatformName = NOT_KNOWN, } = currentCapabilities;
|
|
96
|
+
// Generic data
|
|
97
|
+
const browserName = rawBrowserName === '' ? NOT_KNOWN : rawBrowserName.toLowerCase();
|
|
98
|
+
const browserVersion = rawBrowserVersion === '' ? NOT_KNOWN : rawBrowserVersion.toLowerCase();
|
|
99
|
+
let devicePixelRatio = 1;
|
|
100
|
+
const platformName = rawPlatformName === '' ? NOT_KNOWN : rawPlatformName.toLowerCase();
|
|
101
|
+
const logName = 'wdio-ics:options' in requestedCapabilities
|
|
102
|
+
? requestedCapabilities['wdio-ics:options']?.logName ?? ''
|
|
25
103
|
: '';
|
|
26
|
-
const name = 'wdio-ics:options' in
|
|
27
|
-
?
|
|
104
|
+
const name = 'wdio-ics:options' in requestedCapabilities
|
|
105
|
+
? requestedCapabilities['wdio-ics:options']?.name ?? ''
|
|
28
106
|
: '';
|
|
29
|
-
//
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
107
|
+
// Mobile data
|
|
108
|
+
const { isAndroid, isIOS, isMobile } = currentBrowser;
|
|
109
|
+
const {
|
|
110
|
+
// We use a few `@ts-ignore` here because this is returned by the driver
|
|
111
|
+
// and not recognized by the types because they are not requested
|
|
112
|
+
// @ts-ignore
|
|
113
|
+
app: rawApp = NOT_KNOWN,
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
deviceName: rawDeviceName = NOT_KNOWN,
|
|
116
|
+
// @ts-ignore
|
|
117
|
+
platformVersion: rawPlatformVersion = NOT_KNOWN, } = currentCapabilities;
|
|
118
|
+
const { 'appium:deviceName': requestedDeviceName } = requestedCapabilities;
|
|
119
|
+
const appName = rawApp !== NOT_KNOWN
|
|
120
|
+
? rawApp.replace(/\\/g, '/').split('/').pop().replace(/[^a-zA-Z0-9]/g, '_')
|
|
121
|
+
: NOT_KNOWN;
|
|
122
|
+
const deviceName = (requestedDeviceName || rawDeviceName || '').toLowerCase();
|
|
123
|
+
const nativeWebScreenshot = !!(requestedCapabilities['appium:nativeWebScreenshot']);
|
|
124
|
+
const platformVersion = (rawPlatformVersion === undefined || rawPlatformVersion === '') ? NOT_KNOWN : rawPlatformVersion.toLowerCase();
|
|
125
|
+
const { devicePixelRatio: mobileDevicePixelRatio, devicePlatformRect, deviceScreenSize, } = await getMobileInstanceData({ currentBrowser, isAndroid, isMobile });
|
|
126
|
+
devicePixelRatio = isMobile ? mobileDevicePixelRatio : devicePixelRatio;
|
|
39
127
|
return {
|
|
128
|
+
appName,
|
|
40
129
|
browserName,
|
|
41
130
|
browserVersion,
|
|
42
131
|
deviceName,
|
|
132
|
+
devicePixelRatio,
|
|
133
|
+
devicePlatformRect,
|
|
134
|
+
deviceScreenSize,
|
|
135
|
+
isAndroid,
|
|
136
|
+
isIOS,
|
|
137
|
+
isMobile,
|
|
43
138
|
logName,
|
|
44
139
|
name,
|
|
45
140
|
nativeWebScreenshot,
|
|
@@ -48,9 +143,20 @@ export function getInstanceData(capabilities, currentBrowser) {
|
|
|
48
143
|
};
|
|
49
144
|
}
|
|
50
145
|
/**
|
|
51
|
-
*
|
|
146
|
+
* Traverse up the scope chain until browser element was reached
|
|
52
147
|
*/
|
|
53
148
|
export function getBrowserObject(elem) {
|
|
54
149
|
const elemObject = elem;
|
|
55
150
|
return elemObject.parent ? getBrowserObject(elemObject.parent) : elem;
|
|
56
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* We can't say it's native context if the autoWebview is provided and set to true, for all other cases we can say it's native
|
|
154
|
+
*/
|
|
155
|
+
export function determineNativeContext(driver) {
|
|
156
|
+
if (driver.isMobile) {
|
|
157
|
+
return !!driver.requestedCapabilities?.browserName === false
|
|
158
|
+
&& driver.requestedCapabilities?.['appium:app'] !== undefined
|
|
159
|
+
&& driver.requestedCapabilities?.['appium:autoWebview'] !== true;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@wdio/visual-service",
|
|
3
3
|
"author": "Wim Selles - wswebcreation",
|
|
4
4
|
"description": "Image comparison / visual regression testing for WebdriverIO",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "3.0.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://webdriver.io/docs/visual-testing",
|
|
8
8
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@wdio/logger": "^8.24.12",
|
|
24
24
|
"@wdio/types": "^8.26.2",
|
|
25
|
-
"webdriver-image-comparison": "^
|
|
25
|
+
"webdriver-image-comparison": "^4.0.1"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "run-s clean build:*",
|