@wdio/visual-service 5.1.1 → 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,159 @@
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
+
3
157
  ## 5.1.1
4
158
 
5
159
  ### Patch 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
+ };
@@ -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,CAA+B;gBAE3C,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;IAgCjE,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI;IAKhC,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG;CAkQzF"}
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, getNativeContext } 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,11 +38,11 @@ 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);
@@ -62,15 +66,20 @@ export default class WdioImageComparisonService extends BaseClass {
62
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.');
63
67
  }
64
68
  }
65
- beforeTest(test) {
69
+ async beforeTest(test) {
66
70
  this.#currentFile = (test.file || test.filename);
67
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);
68
77
  }
69
78
  afterCommand(commandName, _args, result, error) {
70
79
  // This is for the cases where in the E2E tests we switch to a WEBVIEW or back to NATIVE_APP context
71
80
  if (commandName === 'getContext' && error === undefined && typeof result === 'string') {
72
81
  // Multiremote logic is handled in the `before` method during an event listener
73
- this._isNativeContext = this._browser?.isMultiremote ? this._isNativeContext : result.includes('NATIVE');
82
+ this._isNativeContext = this.#browser?.isMultiremote ? this._isNativeContext : result.includes('NATIVE');
74
83
  }
75
84
  }
76
85
  #getBaselineFolder() {
@@ -94,7 +103,7 @@ export default class WdioImageComparisonService extends BaseClass {
94
103
  * Add commands to the Multi Remote browser object
95
104
  */
96
105
  async #extendMultiremoteBrowser(capabilities) {
97
- const browser = this._browser;
106
+ const browser = this.#browser;
98
107
  const browserNames = Object.keys(capabilities);
99
108
  /**
100
109
  * Add all the commands to each browser in the Multi Remote
@@ -125,7 +134,7 @@ export default class WdioImageComparisonService extends BaseClass {
125
134
  */
126
135
  async #addCommandsToBrowser(currentBrowser) {
127
136
  const instanceData = await getInstanceData(currentBrowser);
128
- const isNativeContext = getNativeContext(this._browser, currentBrowser, this._isNativeContext);
137
+ const isNativeContext = getNativeContext(this.#browser, currentBrowser, this._isNativeContext);
129
138
  for (const [commandName, command] of Object.entries(elementCommands)) {
130
139
  this.#addElementCommand(currentBrowser, commandName, command, instanceData, isNativeContext);
131
140
  }
@@ -140,17 +149,32 @@ export default class WdioImageComparisonService extends BaseClass {
140
149
  log.info(`Adding element command "${commandName}" to browser object`);
141
150
  const self = this;
142
151
  browser.addCommand(commandName, function (element, tag, elementOptions = {}) {
152
+ const elementOptionsKey = commandName === 'saveElement' ? 'saveElementOptions' : 'checkElementOptions';
143
153
  return command({
144
- executor: (script, ...varArgs) => {
145
- return this.execute.bind(browser)(script, ...varArgs);
154
+ methods: {
155
+ executor: (script, ...varArgs) => {
156
+ return this.execute.bind(browser)(script, ...varArgs);
157
+ },
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]: {
167
+ wic: self.defaultOptions,
168
+ method: elementOptions,
146
169
  },
147
- getElementRect: this.getElementRect.bind(browser),
148
- screenShot: this.takeScreenshot.bind(browser),
149
- takeElementScreenshot: this.takeElementScreenshot.bind(browser),
150
- }, instanceData, getFolders(elementOptions, self.folders, self.#getBaselineFolder()), element, tag, {
151
- wic: self.defaultOptions,
152
- method: elementOptions,
153
- }, isNativeContext);
170
+ isNativeContext,
171
+ testContext: enrichTestContext({
172
+ commandName,
173
+ currentTestContext: self.#testContext,
174
+ instanceData,
175
+ tag,
176
+ })
177
+ });
154
178
  });
155
179
  }
156
180
  /**
@@ -159,24 +183,77 @@ export default class WdioImageComparisonService extends BaseClass {
159
183
  #addPageCommand(browser, commandName, command, instanceData, isNativeContext) {
160
184
  log.info(`Adding browser command "${commandName}" to browser object`);
161
185
  const self = this;
186
+ const pageOptionsKey = PAGE_OPTIONS_MAP[commandName];
162
187
  if (commandName === 'waitForStorybookComponentToBeLoaded') {
163
188
  browser.addCommand(commandName, (options) => waitForStorybookComponentToBeLoaded(options));
164
189
  }
165
190
  else {
166
191
  browser.addCommand(commandName, function (tag, pageOptions = {}) {
167
192
  return command({
168
- executor: (script, ...varArgs) => {
169
- return this.execute.bind(browser)(script, ...varArgs);
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),
170
199
  },
171
- getElementRect: this.getElementRect.bind(browser),
172
- screenShot: this.takeScreenshot.bind(browser),
173
- }, instanceData, getFolders(pageOptions, self.folders, self.#getBaselineFolder()), tag, {
174
- wic: self.defaultOptions,
175
- method: pageOptions,
176
- }, isNativeContext);
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
+ });
177
215
  });
178
216
  }
179
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: {
230
+ executor: (script, ...varArgs) => {
231
+ return browserInstance.execute.bind(browserInstance)(script, ...varArgs);
232
+ },
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]: {
242
+ wic: self.defaultOptions,
243
+ method: pageOptions,
244
+ },
245
+ isNativeContext,
246
+ testContext: enrichTestContext({
247
+ commandName,
248
+ currentTestContext: self.#testContext,
249
+ instanceData,
250
+ tag,
251
+ })
252
+ });
253
+ }
254
+ return returnData;
255
+ });
256
+ }
180
257
  #addMultiremoteCommand(browser, browserNames, commandName, command) {
181
258
  log.info(`Adding browser command "${commandName}" to Multi browser object`);
182
259
  const self = this;
@@ -186,51 +263,41 @@ export default class WdioImageComparisonService extends BaseClass {
186
263
  else {
187
264
  browser.addCommand(commandName, async function (tag, pageOptions = {}) {
188
265
  const returnData = {};
266
+ const pageOptionsKey = PAGE_OPTIONS_MAP[commandName];
189
267
  for (const browserName of browserNames) {
190
268
  const browserInstance = browser.getInstance(browserName);
191
- const isNativeContext = getNativeContext(self._browser, browserInstance, self._isNativeContext);
269
+ const isNativeContext = getNativeContext(self.#browser, browserInstance, self._isNativeContext);
192
270
  const instanceData = await getInstanceData(browserInstance);
193
271
  returnData[browserName] = await command({
194
- executor: (script, ...varArgs) => {
195
- return browserInstance.execute.bind(browserInstance)(script, ...varArgs);
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),
196
278
  },
197
- getElementRect: browserInstance.getElementRect.bind(browserInstance),
198
- screenShot: browserInstance.takeScreenshot.bind(browserInstance),
199
- }, instanceData, getFolders(pageOptions, self.folders, self.#getBaselineFolder()), tag, {
200
- wic: self.defaultOptions,
201
- method: pageOptions,
202
- }, isNativeContext);
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
+ });
203
294
  }
204
295
  return returnData;
205
296
  });
206
297
  }
207
298
  }
208
- #addMultiremoteElementCommand(browser, browserNames, commandName, command) {
209
- log.info(`Adding element command "${commandName}" to Multi browser object`);
210
- const self = this;
211
- browser.addCommand(commandName, async function (tag, element, pageOptions = {}) {
212
- const returnData = {};
213
- for (const browserName of browserNames) {
214
- const browserInstance = browser.getInstance(browserName);
215
- const isNativeContext = getNativeContext(self._browser, browserInstance, self._isNativeContext);
216
- const instanceData = await getInstanceData(browserInstance);
217
- returnData[browserName] = await command({
218
- executor: (script, ...varArgs) => {
219
- return browserInstance.execute.bind(browserInstance)(script, ...varArgs);
220
- },
221
- getElementRect: browserInstance.getElementRect.bind(browserInstance),
222
- screenShot: browserInstance.takeScreenshot.bind(browserInstance),
223
- takeElementScreenshot: browserInstance.takeElementScreenshot.bind(browserInstance),
224
- }, instanceData, getFolders(pageOptions, self.folders, self.#getBaselineFolder()), tag, element, {
225
- wic: self.defaultOptions,
226
- method: pageOptions,
227
- }, isNativeContext);
228
- }
229
- return returnData;
230
- });
231
- }
232
299
  #setupMultiremoteContextListener() {
233
- const multiremoteBrowser = this._browser;
300
+ const multiremoteBrowser = this.#browser;
234
301
  const browserInstances = multiremoteBrowser.instances;
235
302
  for (const instanceName of browserInstances) {
236
303
  const instance = multiremoteBrowser[instanceName];
@@ -246,4 +313,53 @@ export default class WdioImageComparisonService extends BaseClass {
246
313
  });
247
314
  }
248
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
+ };
362
+ }
363
+ throw new SevereServiceError(`Framework ${framework} is not supported by the Visual Service and should be either "mocha", "jasmine" or "cucumber".`);
364
+ }
249
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
  }
@@ -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;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;CAAI"}
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,4 @@
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
2
  import type { NativeContextType } from './types.js';
3
3
  /**
4
4
  * Get the folders data
@@ -38,5 +38,14 @@ export declare function determineNativeContext(driver: WebdriverIO.Browser | Web
38
38
  * Get the native context for the current browser
39
39
  */
40
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;
41
50
  export {};
42
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;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"}
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']);
@@ -206,3 +206,30 @@ export function getNativeContext(browser, currentBrowser, nativeContext) {
206
206
  }
207
207
  return false;
208
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.1",
5
+ "version": "5.2.0",
6
6
  "license": "MIT",
7
7
  "homepage": "https://webdriver.io/docs/visual-testing",
8
8
  "repository": {
@@ -24,7 +24,7 @@
24
24
  "@wdio/logger": "^8.38.0",
25
25
  "@wdio/types": "^8.39.0",
26
26
  "node-fetch": "^3.3.2",
27
- "webdriver-image-comparison": "^6.0.2"
27
+ "webdriver-image-comparison": "^6.1.0"
28
28
  },
29
29
  "devDependencies": {},
30
30
  "scripts": {