@wdio/visual-service 9.1.4 → 9.1.6
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 +46 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +22 -0
- package/dist/service.js +1 -1
- package/dist/storybook/hooks.d.ts +5 -0
- package/dist/storybook/hooks.d.ts.map +1 -0
- package/dist/storybook/hooks.js +100 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +11 -2
- package/package.json +2 -2
- package/dist/storybook/launcher.d.ts.map +0 -1
- package/dist/storybook/launcher.js +0 -122
- /package/dist/{storybook/launcher.d.ts → launcher.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @wdio/visual-service
|
|
2
2
|
|
|
3
|
+
## 9.1.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0a19d78: Fix `clearRuntimeFolder` clearing the actual and diff folders after each spec/feature execution instead of once before all workers start. This caused only the last spec's visual data to be present in the output when running multiple specs.
|
|
8
|
+
|
|
9
|
+
# Committers: 1
|
|
10
|
+
|
|
11
|
+
- Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
|
|
12
|
+
|
|
13
|
+
- ed0bea6: Fix `EISDIR` error when using `resolveSnapshotPath` with the visual service. The service now uses `dirname()` of the resolved path as the baseline folder, preventing it from creating a directory at a path that `expect-webdriverio`'s snapshot service expects to be a file. Fixes #984.
|
|
14
|
+
|
|
15
|
+
# Committers: 1
|
|
16
|
+
|
|
17
|
+
- Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
|
|
18
|
+
|
|
19
|
+
- cbf1d22: Fix incomplete `wdio-ics:options` type augmentation on `WebdriverIO.Capabilities`. The global type declaration now uses the `WdioIcsOptions` interface directly, ensuring all supported properties (`logName`, `name`) are available to TypeScript users in both standalone and multiremote configurations. Fixes #732.
|
|
20
|
+
|
|
21
|
+
# Committers: 1
|
|
22
|
+
|
|
23
|
+
- Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [0a19d78]
|
|
26
|
+
- Updated dependencies [ce74703]
|
|
27
|
+
- @wdio/image-comparison-core@1.1.4
|
|
28
|
+
|
|
29
|
+
## 9.1.5
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 6ed0469: ## Fix: support `appium:options` nested capability format and `avd` fallback (#1118)
|
|
34
|
+
|
|
35
|
+
Appium caps need to be prefixed with `appium:`, but this can feel redundant when you have a lot of caps. So you can also put them inside the `appium:options`-object. This was not supported by the visual module and was reported in #1118. It is now supported.
|
|
36
|
+
|
|
37
|
+
The following capabilities are now correctly read from both `appium:`-prefixed top-level format and the nested `appium:options` format:
|
|
38
|
+
|
|
39
|
+
- `deviceName`
|
|
40
|
+
- `nativeWebScreenshot`
|
|
41
|
+
- `avd` (new, see below)
|
|
42
|
+
|
|
43
|
+
Second issue that is fixed is that for Android the `deviceName` could be left away and the `avd` could be provided. This is now also supported where `deviceName` takes priority over `avd` if both are provided.
|
|
44
|
+
|
|
45
|
+
# Committers: 1
|
|
46
|
+
|
|
47
|
+
- Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
|
|
48
|
+
|
|
3
49
|
## 9.1.4
|
|
4
50
|
|
|
5
51
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WicElement } from '@wdio/image-comparison-core';
|
|
2
2
|
import WdioImageComparisonService from './service.js';
|
|
3
|
-
import VisualLauncher from './
|
|
4
|
-
import type { Output, Result, VisualServiceOptions, WdioCheckFullPageMethodOptions, WdioSaveFullPageMethodOptions, WdioSaveElementMethodOptions, WdioSaveScreenMethodOptions, WdioCheckElementMethodOptions, WdioCheckScreenMethodOptions } from './types.js';
|
|
3
|
+
import VisualLauncher from './launcher.js';
|
|
4
|
+
import type { Output, Result, VisualServiceOptions, WdioIcsOptions, WdioCheckFullPageMethodOptions, WdioSaveFullPageMethodOptions, WdioSaveElementMethodOptions, WdioSaveScreenMethodOptions, WdioCheckElementMethodOptions, WdioCheckScreenMethodOptions } from './types.js';
|
|
5
5
|
import type { WaitForStorybookComponentToBeLoaded } from './storybook/Types.js';
|
|
6
6
|
declare global {
|
|
7
7
|
namespace WebdriverIO {
|
|
@@ -50,9 +50,7 @@ declare global {
|
|
|
50
50
|
interface Element {
|
|
51
51
|
}
|
|
52
52
|
interface Capabilities {
|
|
53
|
-
'wdio-ics:options'?:
|
|
54
|
-
logName?: string;
|
|
55
|
-
};
|
|
53
|
+
'wdio-ics:options'?: WdioIcsOptions;
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
56
|
namespace ExpectWebdriverIO {
|
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,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,0BAA0B,MAAM,cAAc,CAAA;AACrD,OAAO,cAAc,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAIvD,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,SAAS;;gBAGrC,OAAO,EAAE,YAAY;IAK3B,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,sBAAsB;IAQvF,UAAU;CASnB"}
|
package/dist/launcher.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BaseClass } from '@wdio/image-comparison-core';
|
|
2
|
+
import { prepareStorybook, cleanupStorybook } from './storybook/hooks.js';
|
|
3
|
+
import generateVisualReport from './reporter.js';
|
|
4
|
+
export default class VisualLauncher extends BaseClass {
|
|
5
|
+
#options;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super(options);
|
|
8
|
+
this.#options = options;
|
|
9
|
+
}
|
|
10
|
+
async onPrepare(config, capabilities) {
|
|
11
|
+
if (this.#options.clearRuntimeFolder) {
|
|
12
|
+
this._clearRuntimeFolders();
|
|
13
|
+
}
|
|
14
|
+
await prepareStorybook(config, capabilities, this.#options, this.folders);
|
|
15
|
+
}
|
|
16
|
+
async onComplete() {
|
|
17
|
+
cleanupStorybook();
|
|
18
|
+
if (this.#options.createJsonReportFiles) {
|
|
19
|
+
new generateVisualReport({ directoryPath: this.folders.actualFolder }).generate();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/service.js
CHANGED
|
@@ -91,7 +91,7 @@ export default class WdioImageComparisonService extends BaseClass {
|
|
|
91
91
|
* We also check `this.#config` because for standalone usage of the service, the config is not available
|
|
92
92
|
*/
|
|
93
93
|
if (this.#config && typeof this.#config.resolveSnapshotPath === 'function' && this.#currentFile && isDefaultBaselineFolder) {
|
|
94
|
-
return this.#config.resolveSnapshotPath(this.#currentFile, '.png');
|
|
94
|
+
return dirname(this.#config.resolveSnapshotPath(this.#currentFile, '.png'));
|
|
95
95
|
}
|
|
96
96
|
return baselineFolder;
|
|
97
97
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Capabilities } from '@wdio/types';
|
|
2
|
+
import type { ClassOptions, Folders } from '@wdio/image-comparison-core';
|
|
3
|
+
export declare function prepareStorybook(config: WebdriverIO.Config, capabilities: Capabilities.TestrunnerCapabilities, options: ClassOptions, folders: Folders): Promise<void>;
|
|
4
|
+
export declare function cleanupStorybook(): void;
|
|
5
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/storybook/hooks.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAA6B,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAcnG,wBAAsB,gBAAgB,CAClC,MAAM,EAAE,WAAW,CAAC,MAAM,EAC1B,YAAY,EAAE,YAAY,CAAC,sBAAsB,EACjD,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,OAAO,GACjB,OAAO,CAAC,IAAI,CAAC,CAsFf;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAiBvC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { rmdirSync } from 'node:fs';
|
|
2
|
+
import logger from '@wdio/logger';
|
|
3
|
+
import { SevereServiceError } from 'webdriverio';
|
|
4
|
+
import { createStorybookCapabilities, createTestFiles, getArgvValue, isCucumberFramework, isStorybookMode, parseSkipStories, scanStorybook, } from './utils.js';
|
|
5
|
+
import { CLIP_SELECTOR, NUM_SHARDS, V6_CLIP_SELECTOR } from '../constants.js';
|
|
6
|
+
const log = logger('@wdio/visual-service');
|
|
7
|
+
export async function prepareStorybook(config, capabilities, options, folders) {
|
|
8
|
+
const isStorybook = isStorybookMode();
|
|
9
|
+
const framework = config.framework;
|
|
10
|
+
const isCucumber = isCucumberFramework(framework);
|
|
11
|
+
if (isCucumber && isStorybook) {
|
|
12
|
+
throw new SevereServiceError('\n\nRunning Storybook in combination with the cucumber framework adapter is not supported.\nOnly Jasmine and Mocha are supported.\n\n');
|
|
13
|
+
}
|
|
14
|
+
if (!isStorybook) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
log.info('Running `@wdio/visual-service` in Storybook mode.');
|
|
18
|
+
const { storiesJson, storybookUrl, tempDir } = await scanStorybook(config, options);
|
|
19
|
+
process.env.VISUAL_STORYBOOK_TEMP_SPEC_FOLDER = tempDir;
|
|
20
|
+
process.env.VISUAL_STORYBOOK_URL = storybookUrl;
|
|
21
|
+
if (typeof capabilities === 'object' && !Array.isArray(capabilities)) {
|
|
22
|
+
throw new SevereServiceError('\n\nRunning Storybook in combination with Multiremote is not supported.\nRemove your `capabilities` property from your config or assign an empty array to it like `capabilities: [],`.\n\n');
|
|
23
|
+
}
|
|
24
|
+
capabilities.length = 0;
|
|
25
|
+
log.info('Clearing the current capabilities.');
|
|
26
|
+
const compareOptions = {
|
|
27
|
+
blockOutSideBar: options.blockOutSideBar,
|
|
28
|
+
blockOutStatusBar: options.blockOutStatusBar,
|
|
29
|
+
blockOutToolBar: options.blockOutToolBar,
|
|
30
|
+
ignoreAlpha: options.ignoreAlpha,
|
|
31
|
+
ignoreAntialiasing: options.ignoreAntialiasing,
|
|
32
|
+
ignoreColors: options.ignoreColors,
|
|
33
|
+
ignoreLess: options.ignoreLess,
|
|
34
|
+
ignoreNothing: options.ignoreNothing,
|
|
35
|
+
rawMisMatchPercentage: options.rawMisMatchPercentage,
|
|
36
|
+
returnAllCompareData: options.returnAllCompareData,
|
|
37
|
+
saveAboveTolerance: options.saveAboveTolerance,
|
|
38
|
+
scaleImagesToSameSize: options.scaleImagesToSameSize,
|
|
39
|
+
};
|
|
40
|
+
// --version
|
|
41
|
+
const versionOption = options?.storybook?.version;
|
|
42
|
+
const versionArgv = getArgvValue('--version', value => Math.floor(parseFloat(value)));
|
|
43
|
+
const version = versionOption ?? versionArgv ?? 7;
|
|
44
|
+
// --numShards
|
|
45
|
+
const maxInstances = config?.maxInstances ?? 1;
|
|
46
|
+
const numShardsOption = options?.storybook?.numShards;
|
|
47
|
+
const numShardsArgv = getArgvValue('--numShards', value => parseInt(value, 10));
|
|
48
|
+
const numShards = Math.min(numShardsOption || numShardsArgv || NUM_SHARDS, maxInstances);
|
|
49
|
+
// --clip
|
|
50
|
+
const clipOption = options?.storybook?.clip;
|
|
51
|
+
const clipArgv = getArgvValue('--clip', value => value !== 'false');
|
|
52
|
+
const clip = clipOption ?? clipArgv ?? true;
|
|
53
|
+
// --clipSelector
|
|
54
|
+
const clipSelectorOption = options?.storybook?.clipSelector;
|
|
55
|
+
const clipSelectorArgv = getArgvValue('--clipSelector', value => value);
|
|
56
|
+
const clipSelector = (clipSelectorOption ?? clipSelectorArgv) ?? (version === 6 ? V6_CLIP_SELECTOR : CLIP_SELECTOR);
|
|
57
|
+
process.env.VISUAL_STORYBOOK_CLIP_SELECTOR = clipSelector;
|
|
58
|
+
// --skipStories
|
|
59
|
+
const skipStoriesOption = options?.storybook?.skipStories;
|
|
60
|
+
const skipStoriesArgv = getArgvValue('--skipStories', value => value);
|
|
61
|
+
const skipStories = skipStoriesOption ?? skipStoriesArgv ?? [];
|
|
62
|
+
const parsedSkipStories = parseSkipStories(skipStories);
|
|
63
|
+
// --additionalSearchParams
|
|
64
|
+
const additionalSearchParamsOption = options?.storybook?.additionalSearchParams;
|
|
65
|
+
const additionalSearchParamsArgv = getArgvValue('--additionalSearchParams', value => new URLSearchParams(value));
|
|
66
|
+
const additionalSearchParams = additionalSearchParamsOption ?? additionalSearchParamsArgv ?? new URLSearchParams();
|
|
67
|
+
const getStoriesBaselinePath = options?.storybook?.getStoriesBaselinePath;
|
|
68
|
+
createTestFiles({
|
|
69
|
+
additionalSearchParams,
|
|
70
|
+
clip,
|
|
71
|
+
clipSelector,
|
|
72
|
+
compareOptions,
|
|
73
|
+
directoryPath: tempDir,
|
|
74
|
+
folders,
|
|
75
|
+
framework,
|
|
76
|
+
getStoriesBaselinePath,
|
|
77
|
+
numShards,
|
|
78
|
+
skipStories: parsedSkipStories,
|
|
79
|
+
storiesJson,
|
|
80
|
+
storybookUrl,
|
|
81
|
+
});
|
|
82
|
+
createStorybookCapabilities(capabilities);
|
|
83
|
+
}
|
|
84
|
+
export function cleanupStorybook() {
|
|
85
|
+
const tempDir = process.env.VISUAL_STORYBOOK_TEMP_SPEC_FOLDER;
|
|
86
|
+
if (!tempDir) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
log.info(`Cleaning up temporary folder for storybook specs: ${tempDir}`);
|
|
90
|
+
try {
|
|
91
|
+
rmdirSync(tempDir, { recursive: true });
|
|
92
|
+
log.info(`Temporary folder for storybook specs has been removed: ${tempDir}`);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
log.error(`Failed to remove temporary folder for storybook specs: ${tempDir} due to: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
delete process.env.VISUAL_STORYBOOK_TEMP_SPEC_FOLDER;
|
|
98
|
+
delete process.env.VISUAL_STORYBOOK_URL;
|
|
99
|
+
delete process.env.VISUAL_STORYBOOK_CLIP_SELECTOR;
|
|
100
|
+
}
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACrF,OAAO,KAAK,EACR,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EAIzB,MAAM,YAAY,CAAA;AAEnB;;;;;GAKG;AAEH,wBAAgB,UAAU,CACtB,aAAa,EAAE,sBAAsB,EACrC,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,MAAM,GACxB,OAAO,CAMT;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,iBAAiB,SAAI,GAAG;IAChF,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;AAuFD;;GAEG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,YAAY,GAAG,GAAG,GAAG,SAAS,CAMpF;AA+CD;;GAEG;AACH,wBAAsB,eAAe,CAAC,EAClC,eAAe,EACf,uBAAuB,EACvB,eAAe,EAClB,EAAE,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoEhD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAE,IAAI,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,CAGtG;AAOD,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,EACvD;IAAE,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAC9D,OAAO,CAuBT;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,EAAE,wBAAwB,GAAG,WAAW,CAuB5C"}
|
package/dist/utils.js
CHANGED
|
@@ -115,6 +115,14 @@ export function getLtOptions(capabilities) {
|
|
|
115
115
|
const key = Object.keys(capabilities).find((k) => k.toLowerCase() === 'lt:options');
|
|
116
116
|
return key ? capabilities[key] : undefined;
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Get a requested Appium capability value by checking both the `appium:`-prefixed
|
|
120
|
+
* top-level format and the nested `appium:options` format.
|
|
121
|
+
*/
|
|
122
|
+
function getRequestedAppiumCapability(requestedCapabilities, capName) {
|
|
123
|
+
return requestedCapabilities[`appium:${capName}`]
|
|
124
|
+
?? requestedCapabilities['appium:options']?.[capName];
|
|
125
|
+
}
|
|
118
126
|
/**
|
|
119
127
|
* Get the device name
|
|
120
128
|
*/
|
|
@@ -140,7 +148,8 @@ function getDeviceName(browserInstance) {
|
|
|
140
148
|
if (isLambdaTest && ltOptions && capName in ltOptions) {
|
|
141
149
|
deviceName = ltOptions[capName];
|
|
142
150
|
}
|
|
143
|
-
const
|
|
151
|
+
const requestedDeviceName = (getRequestedAppiumCapability(requestedCapabilities, 'deviceName')
|
|
152
|
+
|| getRequestedAppiumCapability(requestedCapabilities, 'avd'));
|
|
144
153
|
return (deviceName !== NOT_KNOWN ? deviceName : requestedDeviceName || returnedDeviceName || NOT_KNOWN).toLowerCase();
|
|
145
154
|
}
|
|
146
155
|
/**
|
|
@@ -179,7 +188,7 @@ export async function getInstanceData({ browserInstance, initialDeviceRectangles
|
|
|
179
188
|
const ltOptions = getLtOptions(requestedCapabilities);
|
|
180
189
|
// @TODO: Figure this one out in the future when we know more about the Appium capabilities from LT
|
|
181
190
|
// 20241216: LT doesn't have the option to take a ChromeDriver screenshot, so if it's Android it's always native
|
|
182
|
-
const nativeWebScreenshot = isAndroid && ltOptions || !!(requestedCapabilities
|
|
191
|
+
const nativeWebScreenshot = isAndroid && ltOptions || !!getRequestedAppiumCapability(requestedCapabilities, 'nativeWebScreenshot');
|
|
183
192
|
const platformVersion = (rawPlatformVersion === undefined || rawPlatformVersion === '') ? NOT_KNOWN : rawPlatformVersion.toLowerCase();
|
|
184
193
|
const { devicePixelRatio: mobileDevicePixelRatio, deviceRectangles, } = await getMobileInstanceData({ browserInstance, initialDeviceRectangles, isNativeContext, nativeWebScreenshot });
|
|
185
194
|
devicePixelRatio = isMobile ? mobileDevicePixelRatio : devicePixelRatio;
|
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": "9.1.
|
|
5
|
+
"version": "9.1.6",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://webdriver.io/docs/visual-testing",
|
|
8
8
|
"repository": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@wdio/logger": "^9.18.0",
|
|
25
25
|
"@wdio/types": "^9.20.0",
|
|
26
26
|
"expect-webdriverio": "^5.6.1",
|
|
27
|
-
"@wdio/image-comparison-core": "1.1.
|
|
27
|
+
"@wdio/image-comparison-core": "1.1.4"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "run-s clean build:*",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/storybook/launcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,6BAA6B,CAAA;AAC3F,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAevD,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,SAAS;;gBAGrC,OAAO,EAAE,YAAY;IAK3B,SAAS,CAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,sBAAsB;IA+FxF,UAAU;CAuBnB"}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { rmdirSync } from 'node:fs';
|
|
2
|
-
import logger from '@wdio/logger';
|
|
3
|
-
import { SevereServiceError } from 'webdriverio';
|
|
4
|
-
import { BaseClass } from '@wdio/image-comparison-core';
|
|
5
|
-
import { createStorybookCapabilities, createTestFiles, getArgvValue, isCucumberFramework, isStorybookMode, parseSkipStories, scanStorybook, } from './utils.js';
|
|
6
|
-
import { CLIP_SELECTOR, NUM_SHARDS, V6_CLIP_SELECTOR } from '../constants.js';
|
|
7
|
-
import generateVisualReport from '../reporter.js';
|
|
8
|
-
const log = logger('@wdio/visual-service');
|
|
9
|
-
export default class VisualLauncher extends BaseClass {
|
|
10
|
-
#options;
|
|
11
|
-
constructor(options) {
|
|
12
|
-
super(options);
|
|
13
|
-
this.#options = options;
|
|
14
|
-
}
|
|
15
|
-
async onPrepare(config, capabilities) {
|
|
16
|
-
const isStorybook = isStorybookMode();
|
|
17
|
-
const framework = config.framework;
|
|
18
|
-
const isCucumber = isCucumberFramework(framework);
|
|
19
|
-
if (isCucumber && isStorybook) {
|
|
20
|
-
throw new SevereServiceError('\n\nRunning Storybook in combination with the cucumber framework adapter is not supported.\nOnly Jasmine and Mocha are supported.\n\n');
|
|
21
|
-
}
|
|
22
|
-
else if (isStorybook) {
|
|
23
|
-
log.info('Running `@wdio/visual-service` in Storybook mode.');
|
|
24
|
-
const { storiesJson, storybookUrl, tempDir } = await scanStorybook(config, this.#options);
|
|
25
|
-
// Set an environment variable so it can be used in the onComplete hook
|
|
26
|
-
process.env.VISUAL_STORYBOOK_TEMP_SPEC_FOLDER = tempDir;
|
|
27
|
-
// Add the storybook URL to the environment variables
|
|
28
|
-
process.env.VISUAL_STORYBOOK_URL = storybookUrl;
|
|
29
|
-
// Check the capabilities
|
|
30
|
-
// Multiremote capabilities are not supported
|
|
31
|
-
if (typeof capabilities === 'object' && !Array.isArray(capabilities)) {
|
|
32
|
-
throw new SevereServiceError('\n\nRunning Storybook in combination with Multiremote is not supported.\nRemove your `capabilities` property from your config or assign an empty array to it like `capabilities: [],`.\n\n');
|
|
33
|
-
}
|
|
34
|
-
// Clear the capabilities
|
|
35
|
-
capabilities.length = 0;
|
|
36
|
-
log.info('Clearing the current capabilities.');
|
|
37
|
-
// Get compare options from config
|
|
38
|
-
const compareOptions = {
|
|
39
|
-
blockOutSideBar: this.#options.blockOutSideBar,
|
|
40
|
-
blockOutStatusBar: this.#options.blockOutStatusBar,
|
|
41
|
-
blockOutToolBar: this.#options.blockOutToolBar,
|
|
42
|
-
ignoreAlpha: this.#options.ignoreAlpha,
|
|
43
|
-
ignoreAntialiasing: this.#options.ignoreAntialiasing,
|
|
44
|
-
ignoreColors: this.#options.ignoreColors,
|
|
45
|
-
ignoreLess: this.#options.ignoreLess,
|
|
46
|
-
ignoreNothing: this.#options.ignoreNothing,
|
|
47
|
-
rawMisMatchPercentage: this.#options.rawMisMatchPercentage,
|
|
48
|
-
returnAllCompareData: this.#options.returnAllCompareData,
|
|
49
|
-
saveAboveTolerance: this.#options.saveAboveTolerance,
|
|
50
|
-
scaleImagesToSameSize: this.#options.scaleImagesToSameSize,
|
|
51
|
-
};
|
|
52
|
-
// Determine some run options
|
|
53
|
-
// --version
|
|
54
|
-
const versionOption = this.#options?.storybook?.version;
|
|
55
|
-
const versionArgv = getArgvValue('--version', value => Math.floor(parseFloat(value)));
|
|
56
|
-
const version = versionOption ?? versionArgv ?? 7;
|
|
57
|
-
// --numShards
|
|
58
|
-
const maxInstances = config?.maxInstances ?? 1;
|
|
59
|
-
const numShardsOption = this.#options?.storybook?.numShards;
|
|
60
|
-
const numShardsArgv = getArgvValue('--numShards', value => parseInt(value, 10));
|
|
61
|
-
const numShards = Math.min(numShardsOption || numShardsArgv || NUM_SHARDS, maxInstances);
|
|
62
|
-
// --clip
|
|
63
|
-
const clipOption = this.#options?.storybook?.clip;
|
|
64
|
-
const clipArgv = getArgvValue('--clip', value => value !== 'false');
|
|
65
|
-
const clip = clipOption ?? clipArgv ?? true;
|
|
66
|
-
// --clipSelector
|
|
67
|
-
const clipSelectorOption = this.#options?.storybook?.clipSelector;
|
|
68
|
-
const clipSelectorArgv = getArgvValue('--clipSelector', value => value);
|
|
69
|
-
// V6 has '#root' as the root element, V7 has '#storybook-root'
|
|
70
|
-
const clipSelector = (clipSelectorOption ?? clipSelectorArgv) ?? (version === 6 ? V6_CLIP_SELECTOR : CLIP_SELECTOR);
|
|
71
|
-
// Add the clip selector to the environment variables
|
|
72
|
-
process.env.VISUAL_STORYBOOK_CLIP_SELECTOR = clipSelector;
|
|
73
|
-
// --skipStories
|
|
74
|
-
const skipStoriesOption = this.#options?.storybook?.skipStories;
|
|
75
|
-
const skipStoriesArgv = getArgvValue('--skipStories', value => value);
|
|
76
|
-
const skipStories = skipStoriesOption ?? skipStoriesArgv ?? [];
|
|
77
|
-
const parsedSkipStories = parseSkipStories(skipStories);
|
|
78
|
-
// --additionalSearchParams
|
|
79
|
-
const additionalSearchParamsOption = this.#options?.storybook?.additionalSearchParams;
|
|
80
|
-
const additionalSearchParamsArgv = getArgvValue('--additionalSearchParams', value => new URLSearchParams(value));
|
|
81
|
-
const additionalSearchParams = additionalSearchParamsOption ?? additionalSearchParamsArgv ?? new URLSearchParams();
|
|
82
|
-
const getStoriesBaselinePath = this.#options?.storybook?.getStoriesBaselinePath;
|
|
83
|
-
// Create the test files
|
|
84
|
-
createTestFiles({
|
|
85
|
-
additionalSearchParams,
|
|
86
|
-
clip,
|
|
87
|
-
clipSelector,
|
|
88
|
-
compareOptions,
|
|
89
|
-
directoryPath: tempDir,
|
|
90
|
-
folders: this.folders,
|
|
91
|
-
framework,
|
|
92
|
-
getStoriesBaselinePath,
|
|
93
|
-
numShards,
|
|
94
|
-
skipStories: parsedSkipStories,
|
|
95
|
-
storiesJson,
|
|
96
|
-
storybookUrl,
|
|
97
|
-
});
|
|
98
|
-
// Create the capabilities
|
|
99
|
-
createStorybookCapabilities(capabilities);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async onComplete() {
|
|
103
|
-
const tempDir = process.env.VISUAL_STORYBOOK_TEMP_SPEC_FOLDER;
|
|
104
|
-
if (tempDir) {
|
|
105
|
-
log.info(`Cleaning up temporary folder for storybook specs: ${tempDir}`);
|
|
106
|
-
try {
|
|
107
|
-
rmdirSync(tempDir, { recursive: true });
|
|
108
|
-
log.info(`Temporary folder for storybook specs has been removed: ${tempDir}`);
|
|
109
|
-
}
|
|
110
|
-
catch (err) {
|
|
111
|
-
log.error(`Failed to remove temporary folder for storybook specs: ${tempDir} due to: ${err.message}`);
|
|
112
|
-
}
|
|
113
|
-
// Remove the environment variables
|
|
114
|
-
delete process.env.VISUAL_STORYBOOK_TEMP_SPEC_FOLDER;
|
|
115
|
-
delete process.env.VISUAL_STORYBOOK_URL;
|
|
116
|
-
delete process.env.VISUAL_STORYBOOK_CLIP_SELECTOR;
|
|
117
|
-
}
|
|
118
|
-
if (this.#options.createJsonReportFiles) {
|
|
119
|
-
new generateVisualReport({ directoryPath: this.folders.actualFolder }).generate();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
File without changes
|