@wdio/visual-service 5.1.0 → 5.2.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 CHANGED
@@ -1,5 +1,182 @@
1
1
  # @wdio/visual-service
2
2
 
3
+ ## 5.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0b01b64: ### @wdio/visual-service
8
+
9
+ #### 🚀 New Features
10
+
11
+ **Added Reporting output**
12
+ You now have the option to export the compare results into a JSON report file. By enabling the module option `createJsonReportFiles: true`, each image that is compared will create a report stored in the `actual` folder, next to each `actual` image result.
13
+
14
+ The output will look like this:
15
+
16
+ ```json
17
+ {
18
+ "parent": "check methods",
19
+ "test": "should fail comparing with a baseline",
20
+ "tag": "examplePageFail",
21
+ "instanceData": {
22
+ "browser": {
23
+ "name": "chrome-headless-shell",
24
+ "version": "126.0.6478.183"
25
+ },
26
+ "platform": {
27
+ "name": "mac",
28
+ "version": "not-known"
29
+ }
30
+ },
31
+ "commandName": "checkScreen",
32
+ "boundingBoxes": {
33
+ "diffBoundingBoxes": [
34
+ {
35
+ "left": 1088,
36
+ "top": 717,
37
+ "right": 1186,
38
+ "bottom": 730
39
+ }
40
+ //....
41
+ ],
42
+ "ignoredBoxes": [
43
+ {
44
+ "left": 159,
45
+ "top": 652,
46
+ "right": 356,
47
+ "bottom": 703
48
+ }
49
+ //...
50
+ ]
51
+ },
52
+ "fileData": {
53
+ "actualFilePath": "/Users/wswebcreation/Git/wdio/visual-testing/.tmp/actual/desktop_chrome-headless-shellexamplePageFail-local-chrome-latest-1366x768.png",
54
+ "baselineFilePath": "/Users/wswebcreation/Git/wdio/visual-testing/localBaseline/desktop_chrome-headless-shellexamplePageFail-local-chrome-latest-1366x768.png",
55
+ "diffFilePath": "/Users/wswebcreation/Git/wdio/visual-testing/.tmp/diff/desktop_chrome-headless-shell/examplePageFail-local-chrome-latest-1366x768png",
56
+ "fileName": "examplePageFail-local-chrome-latest-1366x768.png",
57
+ "size": {
58
+ "actual": {
59
+ "height": 768,
60
+ "width": 1366
61
+ },
62
+ "baseline": {
63
+ "height": 768,
64
+ "width": 1366
65
+ },
66
+ "diff": {
67
+ "height": 768,
68
+ "width": 1366
69
+ }
70
+ }
71
+ },
72
+ "misMatchPercentage": "12.90",
73
+ "rawMisMatchPercentage": 12.900729014153246
74
+ }
75
+ ```
76
+
77
+ When all tests are executed, a new JSON file with the collection of the comparisons will be generated and can be found in the root of your actual folder. The data is grouped by:
78
+
79
+ - `describe` for Jasmine/Mocha or `Feature` for CucumberJS
80
+ - `it` for Jasmine/Mocha or `Scenario` for CucumberJS
81
+
82
+ and then sorted by:
83
+
84
+ - `commandName`, which are the compare method names used to compare the images
85
+ - `instanceData`, browser first, then device, then platform
86
+
87
+ it will look like this
88
+
89
+ ```json
90
+ [
91
+ {
92
+ "description": "check methods",
93
+ "data": [
94
+ {
95
+ "test": "should fail comparing with a baseline",
96
+ "data": [
97
+ {
98
+ "tag": "examplePageFail",
99
+ "instanceData": {},
100
+ "commandName": "checkScreen",
101
+ "framework": "mocha",
102
+ "boundingBoxes": {
103
+ "diffBoundingBoxes": [],
104
+ "ignoredBoxes": []
105
+ },
106
+ "fileData": {},
107
+ "misMatchPercentage": "14.34",
108
+ "rawMisMatchPercentage": 14.335403703025868
109
+ },
110
+ {
111
+ "tag": "exampleElementFail",
112
+ "instanceData": {},
113
+ "commandName": "checkElement",
114
+ "framework": "mocha",
115
+ "boundingBoxes": {
116
+ "diffBoundingBoxes": [],
117
+ "ignoredBoxes": []
118
+ },
119
+ "fileData": {},
120
+ "misMatchPercentage": "1.34",
121
+ "rawMisMatchPercentage": 1.335403703025868
122
+ }
123
+ ]
124
+ }
125
+ ]
126
+ }
127
+ ]
128
+ ```
129
+
130
+ The report data will give you the opportunity to build your own visual report without doing all the magic and data collection yourself.
131
+
132
+ ### webdriver-image-comparison
133
+
134
+ #### 🚀 New Features
135
+
136
+ - Add support to generate single JSON report files
137
+
138
+ ### @wdio/ocr-service
139
+
140
+ #### 💅 Polish
141
+
142
+ - Refactored the CLI to use `@inquirer/prompts` instead of `inquirer`
143
+
144
+ ### Committers: 1
145
+
146
+ - Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
147
+
148
+ ```
149
+
150
+ ```
151
+
152
+ ### Patch Changes
153
+
154
+ - Updated dependencies [0b01b64]
155
+ - webdriver-image-comparison@6.1.0
156
+
157
+ ## 5.1.1
158
+
159
+ ### Patch Changes
160
+
161
+ - 26c1a17: This release contains better support for Multiremote tests on the instances and global level, including better Native App support. This is related to issue https://github.com/webdriverio/visual-testing/issues/418 which is fixed by this release
162
+
163
+ # 💅 Polish
164
+
165
+ - cab1219: Update dependencies
166
+ - 2583542 / 8b1f837: add new tests
167
+ - eec29e1 / e2b2d38: update images
168
+
169
+ # 🐛 Bug Fixes
170
+
171
+ - 29f6f82 / 1ba9817: fix issue https://github.com/webdriverio/visual-testing/issues/418
172
+
173
+ # Committers: 1
174
+
175
+ - Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
176
+
177
+ - Updated dependencies [26c1a17]
178
+ - webdriver-image-comparison@6.0.2
179
+
3
180
  ## 5.1.0
4
181
 
5
182
  ### Minor Changes
@@ -1,4 +1,7 @@
1
1
  export declare const V6_CLIP_SELECTOR = "#root > :first-child:not(script):not(style)";
2
2
  export declare const CLIP_SELECTOR = "#storybook-root > :first-child:not(script):not(style)";
3
3
  export declare const NUM_SHARDS = 1;
4
+ export declare const PAGE_OPTIONS_MAP: {
5
+ [key: string]: string;
6
+ };
4
7
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,gDAAgD,CAAA;AAC7E,eAAO,MAAM,aAAa,0DAA0D,CAAA;AACpF,eAAO,MAAM,UAAU,IAAI,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,gDAAgD,CAAA;AAC7E,eAAO,MAAM,aAAa,0DAA0D,CAAA;AACpF,eAAO,MAAM,UAAU,IAAI,CAAA;AAC3B,eAAO,MAAM,gBAAgB,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAOrD,CAAA"}
package/dist/constants.js CHANGED
@@ -1,3 +1,11 @@
1
1
  export const V6_CLIP_SELECTOR = '#root > :first-child:not(script):not(style)';
2
2
  export const CLIP_SELECTOR = '#storybook-root > :first-child:not(script):not(style)';
3
3
  export const NUM_SHARDS = 1;
4
+ export const PAGE_OPTIONS_MAP = {
5
+ 'saveScreen': 'saveScreenOptions',
6
+ 'saveFullPageScreen': 'saveFullPageOptions',
7
+ 'saveTabbablePage': 'saveTabbableOptions',
8
+ 'checkScreen': 'checkScreenOptions',
9
+ 'checkFullPageScreen': 'checkFullPageOptions',
10
+ 'checkTabbablePage': 'checkTabbableOptions'
11
+ };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="./expect-webdriverio.js" />
2
1
  import type { WicElement } from 'webdriver-image-comparison';
3
2
  import WdioImageComparisonService from './service.js';
4
3
  import VisualLauncher from './storybook/launcher.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/matcher.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="./expect-webdriverio.js" />
2
1
  import type { WdioCheckFullPageMethodOptions, WdioCheckElementMethodOptions, WdioCheckScreenMethodOptions } from './types.js';
3
2
  export declare function toMatchScreenSnapshot(browser: WebdriverIO.Browser, tag: string, expectedResultOrOptions?: number | ExpectWebdriverIO.PartialMatcher, optionsOrUndefined?: WdioCheckScreenMethodOptions): Promise<{
4
3
  pass: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EACR,8BAA8B,EAC9B,6BAA6B,EAC7B,4BAA4B,EAC/B,MAAM,YAAY,CAAA;AA2FnB,wBAAsB,qBAAqB,CACvC,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,4BAA4B;;;GAKpD;AAED,wBAAsB,uBAAuB,CACzC,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,8BAA8B;;;GAKtD;AAED,wBAAsB,sBAAsB,CACxC,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,6BAA6B;;;GAMrD;AAED,wBAAsB,2BAA2B,CAC7C,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,8BAA8B;;;GAKtD"}
1
+ {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,8BAA8B,EAC9B,6BAA6B,EAC7B,4BAA4B,EAC/B,MAAM,YAAY,CAAA;AA2FnB,wBAAsB,qBAAqB,CACvC,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,4BAA4B;;;GAKpD;AAED,wBAAsB,uBAAuB,CACzC,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,8BAA8B;;;GAKtD;AAED,wBAAsB,sBAAsB,CACxC,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,6BAA6B;;;GAMrD;AAED,wBAAsB,2BAA2B,CAC7C,OAAO,EAAE,WAAW,CAAC,OAAO,EAC5B,GAAG,EAAE,MAAM,EACX,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,cAAc,EACnE,kBAAkB,CAAC,EAAE,8BAA8B;;;GAKtD"}
@@ -0,0 +1,14 @@
1
+ declare class VisualReportGenerator {
2
+ directoryPath: string;
3
+ constructor({ directoryPath }: {
4
+ directoryPath: string;
5
+ });
6
+ generate(): void;
7
+ private readJsonFilesRecursively;
8
+ private readDirectory;
9
+ private readJsonFile;
10
+ private groupAndSortTestData;
11
+ private writeJsonToFile;
12
+ }
13
+ export default VisualReportGenerator;
14
+ //# sourceMappingURL=reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAiBA,cAAM,qBAAqB;IACvB,aAAa,EAAE,MAAM,CAAA;gBAET,EAAE,aAAa,EAAE,EAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC;IAI9C,QAAQ,IAAI,IAAI;IAcvB,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,oBAAoB;IAsD5B,OAAO,CAAC,eAAe;CAG1B;AAED,eAAe,qBAAqB,CAAA"}
@@ -0,0 +1,97 @@
1
+ import fs from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import logger from '@wdio/logger';
4
+ const log = logger('@wdio/visual-service:webdriver-image-comparison-reporter');
5
+ class VisualReportGenerator {
6
+ directoryPath;
7
+ constructor({ directoryPath }) {
8
+ this.directoryPath = directoryPath;
9
+ }
10
+ generate() {
11
+ try {
12
+ log.info('Generating visual report...');
13
+ const testData = this.readJsonFilesRecursively(this.directoryPath);
14
+ log.info('Read all json files');
15
+ const groupedAndSortedData = this.groupAndSortTestData(testData);
16
+ log.info('Grouped and sorted data');
17
+ this.writeJsonToFile(join(this.directoryPath, 'output.json'), groupedAndSortedData);
18
+ log.info('Report generated');
19
+ }
20
+ catch (e) {
21
+ log.error('Error generating visual report:', e);
22
+ }
23
+ }
24
+ readJsonFilesRecursively(directory) {
25
+ log.info(`Reading json files from ${directory}`);
26
+ const testData = [];
27
+ this.readDirectory(directory, testData);
28
+ return testData;
29
+ }
30
+ readDirectory(dir, testData) {
31
+ const items = fs.readdirSync(dir);
32
+ items.forEach(item => {
33
+ const fullPath = join(dir, item);
34
+ const stat = fs.statSync(fullPath);
35
+ if (stat.isDirectory()) {
36
+ this.readDirectory(fullPath, testData);
37
+ }
38
+ else if (stat.isFile() && item.endsWith('-report.json')) {
39
+ this.readJsonFile(fullPath, testData);
40
+ }
41
+ });
42
+ }
43
+ readJsonFile(filePath, testData) {
44
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
45
+ const jsonContent = JSON.parse(fileContent);
46
+ testData.push(jsonContent); // Add JSON content to testData array
47
+ }
48
+ groupAndSortTestData(testData) {
49
+ const groupedData = [];
50
+ // Grouping by description and test
51
+ testData.forEach(report => {
52
+ const mainTestName = report.description;
53
+ let mainGroup = groupedData.find(group => group.description === mainTestName);
54
+ if (!mainGroup) {
55
+ mainGroup = { description: mainTestName, data: [] };
56
+ groupedData.push(mainGroup);
57
+ }
58
+ let testGroup = mainGroup.data.find(test => test.test === report.test);
59
+ if (!testGroup) {
60
+ testGroup = { test: report.test, data: [] };
61
+ mainGroup.data.push(testGroup);
62
+ }
63
+ testGroup.data.push(report);
64
+ });
65
+ // Sorting within each group
66
+ groupedData.forEach(mainGroup => {
67
+ mainGroup.data.forEach(testGroup => {
68
+ testGroup.data.sort((a, b) => {
69
+ // Sort by commandName
70
+ if (a.commandName !== b.commandName) {
71
+ return a.commandName.localeCompare(b.commandName);
72
+ }
73
+ // Sort by instanceData (browser first, then device, then platform)
74
+ const aBrowserName = a.instanceData.browser?.name || '';
75
+ const bBrowserName = b.instanceData.browser?.name || '';
76
+ if (aBrowserName !== bBrowserName) {
77
+ return aBrowserName.localeCompare(bBrowserName);
78
+ }
79
+ const aDeviceName = a.instanceData.deviceName || '';
80
+ const bDeviceName = b.instanceData.deviceName || '';
81
+ if (aDeviceName !== bDeviceName) {
82
+ return aDeviceName.localeCompare(bDeviceName);
83
+ }
84
+ if (a.instanceData.platform.name !== b.instanceData.platform.name) {
85
+ return a.instanceData.platform.name.localeCompare(b.instanceData.platform.name);
86
+ }
87
+ return 0;
88
+ });
89
+ });
90
+ });
91
+ return groupedData;
92
+ }
93
+ writeJsonToFile(filePath, data) {
94
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
95
+ }
96
+ }
97
+ export default VisualReportGenerator;
package/dist/service.d.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  import type { Frameworks } from '@wdio/types';
2
- import type { ClassOptions } from 'webdriver-image-comparison';
3
2
  import { BaseClass } from 'webdriver-image-comparison';
3
+ import type { VisualServiceOptions } from './types.js';
4
4
  export default class WdioImageComparisonService extends BaseClass {
5
5
  #private;
6
- private _browser?;
7
6
  private _isNativeContext;
8
- constructor(options: ClassOptions, _: WebdriverIO.Capabilities, config: WebdriverIO.Config);
7
+ constructor(options: VisualServiceOptions, _: WebdriverIO.Capabilities, config: WebdriverIO.Config);
9
8
  /**
10
9
  * Set up the service if users want to use it in standalone mode
11
10
  */
12
11
  remoteSetup(browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser): Promise<void>;
13
12
  before(capabilities: WebdriverIO.Capabilities, _specs: string[], browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser): Promise<void>;
14
- beforeTest(test: Frameworks.Test): void;
13
+ beforeTest(test: Frameworks.Test): Promise<void>;
14
+ beforeScenario(world: Frameworks.World): void;
15
15
  afterCommand(commandName: string, _args: string[], result: number | string, error: any): void;
16
16
  }
17
17
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
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;AAwBnC,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;IAM1F;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB;IAIzE,MAAM,CACR,YAAY,EAAE,WAAW,CAAC,YAAY,EACtC,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB;IA4BjE,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI;IAMhC,YAAY,CAAE,WAAW,EAAC,MAAM,EAAE,KAAK,EAAC,MAAM,EAAE,EAAE,MAAM,EAAC,MAAM,GAAC,MAAM,EAAE,KAAK,EAAC,GAAG;CA4IpF"}
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,EACH,SAAS,EAWZ,MAAM,4BAA4B,CAAA;AAYnC,OAAO,KAAK,EAA+C,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAenG,MAAM,CAAC,OAAO,OAAO,0BAA2B,SAAQ,SAAS;;IAM7D,OAAO,CAAC,gBAAgB,CAA+B;gBAE3C,OAAO,EAAE,oBAAoB,EAAE,CAAC,EAAE,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM;IAOlG;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB;IAIzE,MAAM,CACR,YAAY,EAAE,WAAW,CAAC,YAAY,EACtC,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB;IAgC3D,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI;IAOtC,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK;IAItC,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG;CAsWzF"}
package/dist/service.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import logger from '@wdio/logger';
2
2
  import { expect } from '@wdio/globals';
3
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';
4
+ import { BaseClass, checkElement, checkFullPageScreen, checkScreen, saveElement, saveFullPageScreen, saveScreen, saveTabbablePage, checkTabbablePage, FOLDERS, DEFAULT_TEST_CONTEXT, } from 'webdriver-image-comparison';
5
+ import { SevereServiceError } from 'webdriverio';
6
+ import { determineNativeContext, enrichTestContext, getFolders, getInstanceData, getNativeContext } from './utils.js';
6
7
  import { toMatchScreenSnapshot, toMatchFullPageSnapshot, toMatchElementSnapshot, toMatchTabbablePageSnapshot } from './matcher.js';
7
8
  import { waitForStorybookComponentToBeLoaded } from './storybook/utils.js';
9
+ import { PAGE_OPTIONS_MAP } from './constants.js';
8
10
  const log = logger('@wdio/visual-service');
9
11
  const elementCommands = { saveElement, checkElement };
10
12
  const pageCommands = {
@@ -20,12 +22,14 @@ export default class WdioImageComparisonService extends BaseClass {
20
22
  #config;
21
23
  #currentFile;
22
24
  #currentFilePath;
23
- _browser;
25
+ #testContext;
26
+ #browser;
24
27
  _isNativeContext;
25
28
  constructor(options, _, config) {
26
29
  super(options);
27
30
  this.#config = config;
28
31
  this._isNativeContext = undefined;
32
+ this.#testContext = DEFAULT_TEST_CONTEXT;
29
33
  }
30
34
  /**
31
35
  * Set up the service if users want to use it in standalone mode
@@ -34,15 +38,18 @@ export default class WdioImageComparisonService extends BaseClass {
34
38
  await this.before(browser.capabilities, [], browser);
35
39
  }
36
40
  async before(capabilities, _specs, browser) {
37
- this._browser = browser;
38
- this._isNativeContext = determineNativeContext(this._browser);
39
- if (!this._browser.isMultiremote) {
41
+ this.#browser = browser;
42
+ this._isNativeContext = determineNativeContext(this.#browser);
43
+ if (!this.#browser.isMultiremote) {
40
44
  log.info('Adding commands to global browser');
41
- await this.#addCommandsToBrowser(this._browser);
45
+ await this.#addCommandsToBrowser(this.#browser);
42
46
  }
43
47
  else {
44
48
  await this.#extendMultiremoteBrowser(capabilities);
45
49
  }
50
+ if (browser.isMultiremote) {
51
+ this.#setupMultiremoteContextListener();
52
+ }
46
53
  /**
47
54
  * add custom matcher for visual comparison when expect has been added.
48
55
  * this is not the case in standalone mode
@@ -59,14 +66,20 @@ export default class WdioImageComparisonService extends BaseClass {
59
66
  log.warn('Expect package not found. This means that the custom matchers `toMatchScreenSnapshot|toMatchFullPageSnapshot|toMatchElementSnapshot|toMatchTabbablePageSnapshot` are not added and can not be used. Please make sure to add it to your `package.json` if you want to use the Visual custom matchers.');
60
67
  }
61
68
  }
62
- beforeTest(test) {
69
+ async beforeTest(test) {
63
70
  this.#currentFile = (test.file || test.filename);
64
71
  this.#currentFilePath = resolve(dirname(this.#currentFile), FOLDERS.DEFAULT.BASE);
72
+ this.#testContext = this.#getTestContext(test);
73
+ }
74
+ // For Cucumber only
75
+ beforeScenario(world) {
76
+ this.#testContext = this.#getTestContext(world);
65
77
  }
66
78
  afterCommand(commandName, _args, result, error) {
67
79
  // This is for the cases where in the E2E tests we switch to a WEBVIEW or back to NATIVE_APP context
68
80
  if (commandName === 'getContext' && error === undefined && typeof result === 'string') {
69
- this._isNativeContext = result.includes('NATIVE');
81
+ // Multiremote logic is handled in the `before` method during an event listener
82
+ this._isNativeContext = this.#browser?.isMultiremote ? this._isNativeContext : result.includes('NATIVE');
70
83
  }
71
84
  }
72
85
  #getBaselineFolder() {
@@ -86,81 +99,267 @@ export default class WdioImageComparisonService extends BaseClass {
86
99
  }
87
100
  return baselineFolder;
88
101
  }
102
+ /**
103
+ * Add commands to the Multi Remote browser object
104
+ */
89
105
  async #extendMultiremoteBrowser(capabilities) {
90
- const browser = this._browser;
106
+ const browser = this.#browser;
91
107
  const browserNames = Object.keys(capabilities);
92
- log.info(`Adding commands to Multi Browser: ${browserNames.join(', ')}`);
108
+ /**
109
+ * Add all the commands to each browser in the Multi Remote
110
+ */
93
111
  for (const browserName of browserNames) {
94
- const multiremoteBrowser = browser;
95
- const browserInstance = multiremoteBrowser.getInstance(browserName);
112
+ log.info(`Adding commands to Multi Browser: ${browserName}`);
113
+ const browserInstance = browser.getInstance(browserName);
96
114
  await this.#addCommandsToBrowser(browserInstance);
97
115
  }
98
116
  /**
99
117
  * Add all the commands to the global browser object that will execute
100
118
  * on each browser in the Multi Remote
119
+ * Start with the page commands
101
120
  */
102
- for (const command of [
103
- ...Object.keys(elementCommands),
104
- ...Object.keys(pageCommands),
105
- ]) {
106
- if (command === 'waitForStorybookComponentToBeLoaded') {
107
- browser.addCommand(command, waitForStorybookComponentToBeLoaded);
108
- }
109
- else {
110
- browser.addCommand(command, function (...args) {
111
- const returnData = {};
112
- for (const browserName of browserNames) {
113
- const multiremoteBrowser = browser;
114
- const browserInstance = multiremoteBrowser.getInstance(browserName);
115
- /**
116
- * casting command to `checkScreen` to simplify type handling here
117
- */
118
- returnData[browserName] = browserInstance[command].call(browserInstance, ...args);
119
- }
120
- return returnData;
121
- });
122
- }
121
+ for (const [commandName, command] of Object.entries(pageCommands)) {
122
+ this.#addMultiremoteCommand(browser, browserNames, commandName, command);
123
+ }
124
+ /**
125
+ * Add all the element commands to the global browser object that will execute
126
+ * on each browser in the Multi Remote
127
+ */
128
+ for (const [commandName, command] of Object.entries(elementCommands)) {
129
+ this.#addMultiremoteElementCommand(browser, browserNames, commandName, command);
123
130
  }
124
131
  }
132
+ /**
133
+ * Add commands to the "normal" browser object
134
+ */
125
135
  async #addCommandsToBrowser(currentBrowser) {
126
136
  const instanceData = await getInstanceData(currentBrowser);
127
- const self = this;
137
+ const isNativeContext = getNativeContext(this.#browser, currentBrowser, this._isNativeContext);
128
138
  for (const [commandName, command] of Object.entries(elementCommands)) {
129
- log.info(`Adding element command "${commandName}" to browser object`);
130
- currentBrowser.addCommand(commandName, function (element, tag, elementOptions = {}) {
131
- return command({
139
+ this.#addElementCommand(currentBrowser, commandName, command, instanceData, isNativeContext);
140
+ }
141
+ for (const [commandName, command] of Object.entries(pageCommands)) {
142
+ this.#addPageCommand(currentBrowser, commandName, command, instanceData, isNativeContext);
143
+ }
144
+ }
145
+ /**
146
+ * Add new element commands to the browser object
147
+ */
148
+ #addElementCommand(browser, commandName, command, instanceData, isNativeContext) {
149
+ log.info(`Adding element command "${commandName}" to browser object`);
150
+ const self = this;
151
+ browser.addCommand(commandName, function (element, tag, elementOptions = {}) {
152
+ const elementOptionsKey = commandName === 'saveElement' ? 'saveElementOptions' : 'checkElementOptions';
153
+ return command({
154
+ methods: {
132
155
  executor: (script, ...varArgs) => {
133
- return this.execute.bind(currentBrowser)(script, ...varArgs);
156
+ return this.execute.bind(browser)(script, ...varArgs);
134
157
  },
135
- getElementRect: this.getElementRect.bind(currentBrowser),
136
- screenShot: this.takeScreenshot.bind(currentBrowser),
137
- takeElementScreenshot: this.takeElementScreenshot.bind(currentBrowser),
138
- }, instanceData, getFolders(elementOptions, self.folders, self.#getBaselineFolder()), element, tag, {
158
+ getElementRect: this.getElementRect.bind(browser),
159
+ screenShot: this.takeScreenshot.bind(browser),
160
+ takeElementScreenshot: this.takeElementScreenshot.bind(browser),
161
+ },
162
+ instanceData,
163
+ folders: getFolders(elementOptions, self.folders, self.#getBaselineFolder()),
164
+ element,
165
+ tag,
166
+ [elementOptionsKey]: {
139
167
  wic: self.defaultOptions,
140
168
  method: elementOptions,
141
- }, self._isNativeContext);
169
+ },
170
+ isNativeContext,
171
+ testContext: enrichTestContext({
172
+ commandName,
173
+ currentTestContext: self.#testContext,
174
+ instanceData,
175
+ tag,
176
+ })
142
177
  });
178
+ });
179
+ }
180
+ /**
181
+ * Add new page commands to the browser object
182
+ */
183
+ #addPageCommand(browser, commandName, command, instanceData, isNativeContext) {
184
+ log.info(`Adding browser command "${commandName}" to browser object`);
185
+ const self = this;
186
+ const pageOptionsKey = PAGE_OPTIONS_MAP[commandName];
187
+ if (commandName === 'waitForStorybookComponentToBeLoaded') {
188
+ browser.addCommand(commandName, (options) => waitForStorybookComponentToBeLoaded(options));
143
189
  }
144
- for (const [commandName, command] of Object.entries(pageCommands)) {
145
- log.info(`Adding element command "${commandName}" to browser object`);
146
- if (commandName === 'waitForStorybookComponentToBeLoaded') {
147
- currentBrowser.addCommand(commandName, (options) => waitForStorybookComponentToBeLoaded(options));
148
- }
149
- else {
150
- currentBrowser.addCommand(commandName, function (tag, pageOptions = {}) {
151
- const options = {
190
+ else {
191
+ browser.addCommand(commandName, function (tag, pageOptions = {}) {
192
+ return command({
193
+ methods: {
194
+ executor: (script, ...varArgs) => {
195
+ return this.execute.bind(browser)(script, ...varArgs);
196
+ },
197
+ getElementRect: this.getElementRect.bind(browser),
198
+ screenShot: this.takeScreenshot.bind(browser),
199
+ },
200
+ instanceData,
201
+ folders: getFolders(pageOptions, self.folders, self.#getBaselineFolder()),
202
+ tag,
203
+ [pageOptionsKey]: {
204
+ wic: self.defaultOptions,
205
+ method: pageOptions,
206
+ },
207
+ isNativeContext,
208
+ testContext: enrichTestContext({
209
+ commandName,
210
+ currentTestContext: self.#testContext,
211
+ instanceData,
212
+ tag,
213
+ })
214
+ });
215
+ });
216
+ }
217
+ }
218
+ #addMultiremoteElementCommand(browser, browserNames, commandName, command) {
219
+ log.info(`Adding element command "${commandName}" to Multi browser object`);
220
+ const self = this;
221
+ browser.addCommand(commandName, async function (tag, element, pageOptions = {}) {
222
+ const returnData = {};
223
+ const elementOptionsKey = commandName === 'saveElement' ? 'saveElementOptions' : 'checkElementOptions';
224
+ for (const browserName of browserNames) {
225
+ const browserInstance = browser.getInstance(browserName);
226
+ const isNativeContext = getNativeContext(self.#browser, browserInstance, self._isNativeContext);
227
+ const instanceData = await getInstanceData(browserInstance);
228
+ returnData[browserName] = await command({
229
+ methods: {
152
230
  executor: (script, ...varArgs) => {
153
- return this.execute.bind(currentBrowser)(script, ...varArgs);
231
+ return browserInstance.execute.bind(browserInstance)(script, ...varArgs);
154
232
  },
155
- getElementRect: this.getElementRect.bind(currentBrowser),
156
- screenShot: this.takeScreenshot.bind(currentBrowser),
157
- };
158
- return command(options, instanceData, getFolders(pageOptions, self.folders, self.#getBaselineFolder()), tag, {
233
+ getElementRect: browserInstance.getElementRect.bind(browserInstance),
234
+ screenShot: browserInstance.takeScreenshot.bind(browserInstance),
235
+ takeElementScreenshot: browserInstance.takeElementScreenshot.bind(browserInstance),
236
+ },
237
+ instanceData,
238
+ folders: getFolders(pageOptions, self.folders, self.#getBaselineFolder()),
239
+ tag,
240
+ element,
241
+ [elementOptionsKey]: {
159
242
  wic: self.defaultOptions,
160
243
  method: pageOptions,
161
- }, self._isNativeContext);
244
+ },
245
+ isNativeContext,
246
+ testContext: enrichTestContext({
247
+ commandName,
248
+ currentTestContext: self.#testContext,
249
+ instanceData,
250
+ tag,
251
+ })
162
252
  });
163
253
  }
254
+ return returnData;
255
+ });
256
+ }
257
+ #addMultiremoteCommand(browser, browserNames, commandName, command) {
258
+ log.info(`Adding browser command "${commandName}" to Multi browser object`);
259
+ const self = this;
260
+ if (commandName === 'waitForStorybookComponentToBeLoaded') {
261
+ browser.addCommand(commandName, waitForStorybookComponentToBeLoaded);
262
+ }
263
+ else {
264
+ browser.addCommand(commandName, async function (tag, pageOptions = {}) {
265
+ const returnData = {};
266
+ const pageOptionsKey = PAGE_OPTIONS_MAP[commandName];
267
+ for (const browserName of browserNames) {
268
+ const browserInstance = browser.getInstance(browserName);
269
+ const isNativeContext = getNativeContext(self.#browser, browserInstance, self._isNativeContext);
270
+ const instanceData = await getInstanceData(browserInstance);
271
+ returnData[browserName] = await command({
272
+ methods: {
273
+ executor: (script, ...varArgs) => {
274
+ return browserInstance.execute.bind(browserInstance)(script, ...varArgs);
275
+ },
276
+ getElementRect: browserInstance.getElementRect.bind(browserInstance),
277
+ screenShot: browserInstance.takeScreenshot.bind(browserInstance),
278
+ },
279
+ instanceData,
280
+ folders: getFolders(pageOptions, self.folders, self.#getBaselineFolder()),
281
+ tag,
282
+ [pageOptionsKey]: {
283
+ wic: self.defaultOptions,
284
+ method: pageOptions,
285
+ },
286
+ isNativeContext,
287
+ testContext: enrichTestContext({
288
+ commandName,
289
+ currentTestContext: self.#testContext,
290
+ instanceData,
291
+ tag,
292
+ })
293
+ });
294
+ }
295
+ return returnData;
296
+ });
297
+ }
298
+ }
299
+ #setupMultiremoteContextListener() {
300
+ const multiremoteBrowser = this.#browser;
301
+ const browserInstances = multiremoteBrowser.instances;
302
+ for (const instanceName of browserInstances) {
303
+ const instance = multiremoteBrowser[instanceName];
304
+ instance.on('result', (result) => {
305
+ if (result.command === 'getContext') {
306
+ const value = result.result.value;
307
+ const sessionId = instance.sessionId;
308
+ if (typeof this._isNativeContext !== 'object' || this._isNativeContext === null) {
309
+ this._isNativeContext = {};
310
+ }
311
+ this._isNativeContext[sessionId] = value.includes('NATIVE');
312
+ }
313
+ });
314
+ }
315
+ }
316
+ #getTestContext(test) {
317
+ const framework = this.#config?.framework;
318
+ if (framework === 'mocha' && test) {
319
+ return {
320
+ ...this.#testContext,
321
+ framework: 'mocha',
322
+ parent: test.parent,
323
+ title: test.title,
324
+ };
325
+ }
326
+ else if (framework === 'jasmine' && test) {
327
+ /**
328
+ * When using Jasmine as the framework the title/parent are not set as with mocha.
329
+ *
330
+ * `fullName` contains all describe(), and it() separated by a space.
331
+ * `description` contains the current it() statement.
332
+ *
333
+ * e.g.:
334
+ * With the following configuration
335
+ *
336
+ * describe('x', () => {
337
+ * describe('y', () => {
338
+ * it('z', () => {});
339
+ * })
340
+ * })
341
+ *
342
+ * fullName will be "x y z"
343
+ * description will be "z"
344
+ *
345
+ */
346
+ const { description: title, fullName } = test;
347
+ return {
348
+ ...this.#testContext,
349
+ framework: 'jasmine',
350
+ parent: fullName?.replace(` ${title}`, ''),
351
+ title: title,
352
+ };
353
+ }
354
+ else if (framework === 'cucumber' && test) {
355
+ return {
356
+ ...this.#testContext,
357
+ framework: 'cucumber',
358
+ // @ts-ignore
359
+ parent: test?.gherkinDocument?.feature?.name,
360
+ title: test?.pickle?.name,
361
+ };
164
362
  }
363
+ throw new SevereServiceError(`Framework ${framework} is not supported by the Visual Service and should be either "mocha", "jasmine" or "cucumber".`);
165
364
  }
166
365
  }
@@ -1 +1 @@
1
- {"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/storybook/launcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AActD,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,SAAS;;gBAGrC,OAAO,EAAE,YAAY;IAK3B,SAAS,CAAE,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,kBAAkB;IAuEpF,UAAU;CAiBnB"}
1
+ {"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/storybook/launcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAetD,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,SAAS;;gBAGrC,OAAO,EAAE,YAAY;IAK3B,SAAS,CAAE,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,kBAAkB;IAuEpF,UAAU;CAuBnB"}
@@ -4,6 +4,7 @@ import { SevereServiceError } from 'webdriverio';
4
4
  import { BaseClass } from 'webdriver-image-comparison';
5
5
  import { createStorybookCapabilities, createTestFiles, getArgvValue, isCucumberFramework, isStorybookMode, parseSkipStories, scanStorybook, } from './utils.js';
6
6
  import { CLIP_SELECTOR, NUM_SHARDS, V6_CLIP_SELECTOR } from '../constants.js';
7
+ import generateVisualReport from '../reporter.js';
7
8
  const log = logger('@wdio/visual-service');
8
9
  export default class VisualLauncher extends BaseClass {
9
10
  #options;
@@ -91,5 +92,8 @@ export default class VisualLauncher extends BaseClass {
91
92
  delete process.env.VISUAL_STORYBOOK_URL;
92
93
  delete process.env.VISUAL_STORYBOOK_CLIP_SELECTOR;
93
94
  }
95
+ if (this.#options.createJsonReportFiles) {
96
+ new generateVisualReport({ directoryPath: this.folders.actualFolder }).generate();
97
+ }
94
98
  }
95
99
  }
package/dist/types.d.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import type { ScreenshotOutput, ImageCompareResult, CheckScreenMethodOptions, SaveScreenMethodOptions, CheckElementMethodOptions, SaveElementMethodOptions, CheckFullPageMethodOptions, SaveFullPageMethodOptions, ClassOptions } from 'webdriver-image-comparison';
2
- import type { RectanglesOutput } from 'webdriver-image-comparison/dist/methods/rectangles.interfaces.js';
3
- import type { WaitForStorybookComponentToBeLoaded } from './storybook/Types.js';
4
2
  type MultiOutput = {
5
3
  [browserName: string]: ScreenshotOutput;
6
4
  };
@@ -9,6 +7,19 @@ type MultiResult = {
9
7
  [browserName: string]: ImageCompareResult | number;
10
8
  };
11
9
  export type Result = MultiResult | (ImageCompareResult | number);
10
+ export type NativeContextType = boolean | Record<string, boolean>;
11
+ export type MultiremoteCommandResult = {
12
+ command: string;
13
+ method: string;
14
+ endpoint: string;
15
+ body: Record<string, any>;
16
+ result: {
17
+ value: string;
18
+ };
19
+ sessionId: string | undefined;
20
+ cid: string;
21
+ type: string;
22
+ };
12
23
  export interface WdioIcsCommonOptions {
13
24
  hideElements?: WebdriverIO.Element[];
14
25
  removeElements?: WebdriverIO.Element[];
@@ -30,14 +41,5 @@ export interface WdioCheckScreenMethodOptions extends Omit<CheckScreenMethodOpti
30
41
  }
31
42
  export interface VisualServiceOptions extends ClassOptions {
32
43
  }
33
- export type PageCommandOptions = {
34
- executor: <T>(script: string | ((...innerArgs: any[]) => unknown), ...varArgs: any[]) => Promise<T>;
35
- getElementRect: (elementId: string) => Promise<RectanglesOutput>;
36
- screenShot: () => Promise<string>;
37
- };
38
- export type PageCommand = (options: PageCommandOptions, ...args: any[]) => Promise<any>;
39
- export interface PageCommands {
40
- [commandName: string]: PageCommand | ((options: WaitForStorybookComponentToBeLoaded) => Promise<void>);
41
- }
42
44
  export {};
43
45
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,yBAAyB,EACzB,YAAY,EACf,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kEAAkE,CAAA;AACxG,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAE/E,KAAK,WAAW,GAAG;IACf,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC3C,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,WAAW,GAAG,gBAAgB,CAAC;AACpD,KAAK,WAAW,GAAG;IACf,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,GAAG,MAAM,CAAC;CACtD,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,WAAW,GAAG,CAAC,kBAAkB,GAAG,MAAM,CAAC,CAAC;AAEjE,MAAM,WAAW,oBAAoB;IACjC,YAAY,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;IACrC,cAAc,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAC9D,oBAAoB,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,8BACb,SAAQ,IAAI,CAAC,0BAA0B,EAAE,MAAM,oBAAoB,CAAC,EAChE,oBAAoB;CAAG;AAC/B,MAAM,WAAW,6BACb,SAAQ,IAAI,CAAC,yBAAyB,EAAE,MAAM,oBAAoB,CAAC,EAC/D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,4BACb,SAAQ,IAAI,CAAC,wBAAwB,EAAE,MAAM,oBAAoB,CAAC,EAC9D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,2BACb,SAAQ,IAAI,CAAC,uBAAuB,EAAE,MAAM,oBAAoB,CAAC,EAC7D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,6BACb,SAAQ,IAAI,CAAC,yBAAyB,EAAE,MAAM,oBAAoB,CAAC,EAC/D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,4BACb,SAAQ,IAAI,CAAC,wBAAwB,EAAE,MAAM,oBAAoB,CAAC,EAC9D,oBAAoB;CAAG;AAE/B,MAAM,WAAW,oBAAqB,SAAQ,YAAY;CAAG;AAE7D,MAAM,MAAM,kBAAkB,GAAG;IAC7B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACpG,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjE,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AACxF,MAAM,WAAW,YAAY;IACzB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,CAAC,CAAC,OAAO,EAAE,mCAAmC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CAC1G"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,yBAAyB,EACzB,YAAY,EACf,MAAM,4BAA4B,CAAA;AAEnC,KAAK,WAAW,GAAG;IACf,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC3C,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,WAAW,GAAG,gBAAgB,CAAC;AACpD,KAAK,WAAW,GAAG;IACf,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,GAAG,MAAM,CAAC;CACtD,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,WAAW,GAAG,CAAC,kBAAkB,GAAG,MAAM,CAAC,CAAC;AACjE,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AACjE,MAAM,MAAM,wBAAwB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACpB,CAAA;AAED,MAAM,WAAW,oBAAoB;IACjC,YAAY,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;IACrC,cAAc,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAC9D,oBAAoB,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,8BACb,SAAQ,IAAI,CAAC,0BAA0B,EAAE,MAAM,oBAAoB,CAAC,EAChE,oBAAoB;CAAG;AAC/B,MAAM,WAAW,6BACb,SAAQ,IAAI,CAAC,yBAAyB,EAAE,MAAM,oBAAoB,CAAC,EAC/D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,4BACb,SAAQ,IAAI,CAAC,wBAAwB,EAAE,MAAM,oBAAoB,CAAC,EAC9D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,2BACb,SAAQ,IAAI,CAAC,uBAAuB,EAAE,MAAM,oBAAoB,CAAC,EAC7D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,6BACb,SAAQ,IAAI,CAAC,yBAAyB,EAAE,MAAM,oBAAoB,CAAC,EAC/D,oBAAoB;CAAG;AAC/B,MAAM,WAAW,4BACb,SAAQ,IAAI,CAAC,wBAAwB,EAAE,MAAM,oBAAoB,CAAC,EAC9D,oBAAoB;CAAG;AAE/B,MAAM,WAAW,oBAAqB,SAAQ,YAAY;CAAG"}
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { Folders, InstanceData, CheckScreenMethodOptions, SaveScreenMethodOptions, CheckFullPageMethodOptions, SaveFullPageMethodOptions, CheckElementMethodOptions, SaveElementMethodOptions } from 'webdriver-image-comparison';
1
+ import type { Folders, InstanceData, CheckScreenMethodOptions, SaveScreenMethodOptions, CheckFullPageMethodOptions, SaveFullPageMethodOptions, CheckElementMethodOptions, SaveElementMethodOptions, TestContext } from 'webdriver-image-comparison';
2
+ import type { NativeContextType } from './types.js';
2
3
  /**
3
4
  * Get the folders data
4
5
  *
@@ -32,6 +33,19 @@ export declare function getBrowserObject(elem: WebdriverIO.Element | WebdriverIO
32
33
  /**
33
34
  * 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
  */
35
- export declare function determineNativeContext(driver: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser): boolean;
36
+ export declare function determineNativeContext(driver: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser): NativeContextType;
37
+ /**
38
+ * Get the native context for the current browser
39
+ */
40
+ export declare function getNativeContext(browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser, currentBrowser: WebdriverIO.Browser, nativeContext: NativeContextType): boolean;
41
+ /**
42
+ * Make sure we have all the data for the test context
43
+ */
44
+ export declare function enrichTestContext({ commandName, currentTestContext: { framework, parent, title, }, instanceData: { appName, browserName, browserVersion, deviceName, isAndroid, isIOS, isMobile, platformName, platformVersion, }, tag, }: {
45
+ commandName: string;
46
+ currentTestContext: TestContext;
47
+ instanceData: InstanceData;
48
+ tag: string;
49
+ }): TestContext;
36
50
  export {};
37
51
  //# sourceMappingURL=utils.d.ts.map
@@ -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;AAQnC;;;;;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;AA4GD;;GAEG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CA4DhG;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,CAaT"}
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,EACxB,WAAW,EACd,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAOnD;;;;;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;AA4GD;;GAEG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CA4DhG;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,iBAAiB,CA0BnB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB,EAC7D,cAAc,EAAE,WAAW,CAAC,OAAO,EACnC,aAAa,EAAE,iBAAiB,GACjC,OAAO,CAQT;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,EACI,WAAW,EACX,kBAAkB,EAAE,EAChB,SAAS,EACT,MAAM,EACN,KAAK,GACR,EACD,YAAY,EAAE,EACV,OAAO,EACP,WAAW,EACX,cAAc,EACd,UAAU,EACV,SAAS,EACT,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,eAAe,GAClB,EACD,GAAG,GACN,EACD;IACI,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,WAAW,CAAC;IAChC,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;CACf,GACF,WAAW,CAuBb"}
package/dist/utils.js CHANGED
@@ -137,7 +137,7 @@ export async function getInstanceData(currentBrowser) {
137
137
  // @ts-ignore
138
138
  platformVersion: rawPlatformVersion = NOT_KNOWN, } = currentCapabilities;
139
139
  const appName = rawApp !== NOT_KNOWN
140
- ? rawApp.replace(/\\/g, '/').split('/').pop().replace(/[^a-zA-Z0-9]/g, '_')
140
+ ? rawApp.replace(/\\/g, '/').split('/').pop().replace(/[^a-zA-Z0-9.]/g, '_')
141
141
  : NOT_KNOWN;
142
142
  const deviceName = getDeviceName(currentBrowser);
143
143
  const nativeWebScreenshot = !!(requestedCapabilities['appium:nativeWebScreenshot']);
@@ -173,6 +173,17 @@ export function getBrowserObject(elem) {
173
173
  * 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
174
174
  */
175
175
  export function determineNativeContext(driver) {
176
+ // First check if it's multi remote
177
+ if (driver.isMultiremote) {
178
+ return Object.keys(driver).reduce((acc, instanceName) => {
179
+ const instance = driver[instanceName];
180
+ if (instance.sessionId) {
181
+ acc[instance.sessionId] = determineNativeContext(instance);
182
+ }
183
+ return acc;
184
+ }, {});
185
+ }
186
+ // If not check if it's a mobile
176
187
  if (driver.isMobile) {
177
188
  return !!driver.requestedCapabilities?.browserName === false
178
189
  && (driver.requestedCapabilities?.['appium:app'] !== undefined
@@ -180,5 +191,45 @@ export function determineNativeContext(driver) {
180
191
  || driver.requestedCapabilities?.['appium:appPackage'] !== undefined)
181
192
  && driver.requestedCapabilities?.['appium:autoWebview'] !== true;
182
193
  }
194
+ // If not, it's webcontext
183
195
  return false;
184
196
  }
197
+ /**
198
+ * Get the native context for the current browser
199
+ */
200
+ export function getNativeContext(browser, currentBrowser, nativeContext) {
201
+ if (browser.isMultiremote) {
202
+ return nativeContext[currentBrowser.sessionId];
203
+ }
204
+ else if (typeof nativeContext === 'boolean') {
205
+ return nativeContext;
206
+ }
207
+ return false;
208
+ }
209
+ /**
210
+ * Make sure we have all the data for the test context
211
+ */
212
+ export function enrichTestContext({ commandName, currentTestContext: { framework, parent, title, }, instanceData: { appName, browserName, browserVersion, deviceName, isAndroid, isIOS, isMobile, platformName, platformVersion, }, tag, }) {
213
+ return {
214
+ commandName,
215
+ instanceData: {
216
+ app: appName,
217
+ browser: {
218
+ name: browserName,
219
+ version: browserVersion,
220
+ },
221
+ deviceName,
222
+ isMobile,
223
+ isAndroid,
224
+ isIOS,
225
+ platform: {
226
+ name: platformName,
227
+ version: platformVersion,
228
+ },
229
+ },
230
+ framework,
231
+ parent,
232
+ tag,
233
+ title,
234
+ };
235
+ }
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.1.0",
5
+ "version": "5.2.0",
6
6
  "license": "MIT",
7
7
  "homepage": "https://webdriver.io/docs/visual-testing",
8
8
  "repository": {
@@ -20,11 +20,11 @@
20
20
  "type": "module",
21
21
  "types": "./dist/index.d.ts",
22
22
  "dependencies": {
23
- "@wdio/globals": "^8.38.2",
23
+ "@wdio/globals": "^8.39.1",
24
24
  "@wdio/logger": "^8.38.0",
25
- "@wdio/types": "^8.38.2",
25
+ "@wdio/types": "^8.39.0",
26
26
  "node-fetch": "^3.3.2",
27
- "webdriver-image-comparison": "^6.0.1"
27
+ "webdriver-image-comparison": "^6.1.0"
28
28
  },
29
29
  "devDependencies": {},
30
30
  "scripts": {