@applitools/eyes-storybook 3.57.2 → 3.58.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,60 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.58.0](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.57.2...js/eyes-storybook@3.58.0) (2025-08-21)
4
+
5
+
6
+ ### Features
7
+
8
+ * enhance storybook tsdocs and types | AD-10816 ([#3180](https://github.com/Applitools-Dev/sdk/issues/3180)) ([df2c28d](https://github.com/Applitools-Dev/sdk/commit/df2c28d7f3d661596d0f8d6f9389593527bddc71))
9
+ * storybook sharding | AD-10879 ([#3176](https://github.com/Applitools-Dev/sdk/issues/3176)) ([73e5f3f](https://github.com/Applitools-Dev/sdk/commit/73e5f3fce69044696a2457deb82f576542a2589e))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * configuration parameters and documentation | AD-10816 ([#3181](https://github.com/Applitools-Dev/sdk/issues/3181)) ([fded470](https://github.com/Applitools-Dev/sdk/commit/fded4705d7fea0c1b523dceec5f1128c68c1d76d))
15
+
16
+
17
+ ### Code Refactoring
18
+
19
+ * remove dist folder from eyes-sb ([#3182](https://github.com/Applitools-Dev/sdk/issues/3182)) ([a2696b9](https://github.com/Applitools-Dev/sdk/commit/a2696b969bbc7b686d6ebe03d45b9400e27f65ff))
20
+
21
+
22
+ ### Dependencies
23
+
24
+ * @applitools/dom-snapshot bumped to 4.13.3
25
+
26
+ * @applitools/socket bumped to 1.3.3
27
+ #### Bug Fixes
28
+
29
+ * tunnel uncaught error | FLD-3356 ([#3128](https://github.com/Applitools-Dev/sdk/issues/3128)) ([ed5fb8a](https://github.com/Applitools-Dev/sdk/commit/ed5fb8aad596ec0d8b45a89077a7765c24ae8a8e))
30
+ * @applitools/driver bumped to 1.23.3
31
+ #### Bug Fixes
32
+
33
+ * make orientation more robust | FLD-3470 ([#3166](https://github.com/Applitools-Dev/sdk/issues/3166)) ([3745427](https://github.com/Applitools-Dev/sdk/commit/37454279234b085dc9a159077b3f278cdccc203a))
34
+ * @applitools/spec-driver-webdriver bumped to 1.4.3
35
+
36
+ * @applitools/spec-driver-selenium bumped to 1.7.3
37
+
38
+ * @applitools/spec-driver-puppeteer bumped to 1.6.3
39
+
40
+ * @applitools/screenshoter bumped to 3.12.3
41
+
42
+ * @applitools/nml-client bumped to 1.11.3
43
+
44
+ * @applitools/tunnel-client bumped to 1.10.4
45
+ #### Bug Fixes
46
+
47
+ * tunnel uncaught error | FLD-3356 ([#3128](https://github.com/Applitools-Dev/sdk/issues/3128)) ([ed5fb8a](https://github.com/Applitools-Dev/sdk/commit/ed5fb8aad596ec0d8b45a89077a7765c24ae8a8e))
48
+
49
+
50
+
51
+ * @applitools/ec-client bumped to 1.12.4
52
+
53
+ * @applitools/core bumped to 4.44.4
54
+
55
+ * @applitools/eyes bumped to 1.36.2
56
+
57
+
3
58
  ## [3.57.2](https://github.com/Applitools-Dev/sdk/compare/js/eyes-storybook@3.57.1...js/eyes-storybook@3.57.2) (2025-08-12)
4
59
 
5
60
 
package/dist/index.d.ts CHANGED
@@ -1,39 +1,246 @@
1
1
  import type { ConfigurationPlain, DesktopBrowserInfo, ChromeEmulationInfo, IOSDeviceInfo, IOSMultiDeviceInfo } from '@applitools/eyes';
2
+ type irrelevantToStorybook = 'waitBeforeScreenshots' | 'agentId' | 'captureStatusBar' | 'concurrentSessions' | 'connectionTimeout' | 'debugScreenshots' | 'defaultMatchSettings' | 'disableNMLUrlCache' | 'forceFullPageScreenshot' | 'hideCaret' | 'hideScrollbars' | 'hostApp' | 'hostAppInfo' | 'hostOS' | 'hostOSInfo' | 'ignoreBaseline' | 'ignoreCaret' | 'latestCommitInfo' | 'isDisabled' | 'matchTimeout' | 'mobileOptions' | 'removeSession' | 'rotation' | 'scaleRatio' | 'scrollRootElement' | 'sessionType' | 'stitchMode' | 'stitchOverlap' | 'viewportSize';
2
3
  /**
3
- * https://applitools.com/tutorials/sdks/storybook/config
4
+ * Configuration options for Applitools Eyes Storybook integration.
5
+ *
6
+ * This configuration can be specified in three ways:
7
+ * - Command line arguments
8
+ * - Environment variables (uppercase with APPLITOOLS_ prefix)
9
+ * - applitools.config.js file (CommonJS module export)
10
+ *
11
+ * Environment variables override applitools.config.js values.
12
+ *
13
+ * @see https://applitools.com/tutorials/sdks/storybook/config
4
14
  */
5
- export type ApplitoolsConfig = Omit<ConfigurationPlain, 'waitBeforeScreenshots'> & {
15
+ export type ApplitoolsConfig = Omit<ConfigurationPlain, irrelevantToStorybook | 'waitBeforeCapture'> & {
16
+ /**
17
+ * URL for Storybook instance.
18
+ * @example 'http://localhost:9001'
19
+ */
6
20
  storybookUrl?: string;
21
+ /**
22
+ * Port to run Storybook on.
23
+ * @default 9000
24
+ */
7
25
  storybookPort?: number;
26
+ /**
27
+ * Host to run Storybook on.
28
+ * @default 'localhost'
29
+ */
8
30
  storybookHost?: string;
31
+ /**
32
+ * Path to Storybook's config folder.
33
+ * @default '.storybook'
34
+ */
9
35
  storybookConfigDir?: string;
36
+ /**
37
+ * Path to Storybook's static files folder.
38
+ */
10
39
  storybookStaticDir?: string;
40
+ /**
41
+ * Whether to display Storybook output in console.
42
+ */
11
43
  showStorybookOutput?: boolean;
44
+ /**
45
+ * Set to true if running the SDK in Docker to resolve potential issues.
46
+ * @default false
47
+ */
12
48
  runInDocker?: boolean;
49
+ /**
50
+ * Predicate function, string, or regex specifying which stories should be visually tested.
51
+ * Visual baselines will be created only for specified components.
52
+ * Function receives object with name, kind, storyTitle, and parameters properties.
53
+ *
54
+ * @example
55
+ * // Exclude stories with names starting with [SKIP]
56
+ * ({name, kind, storyTitle, parameters}) => !/^\\[SKIP\\]/.test(name)
57
+ *
58
+ * @default true (includes all stories)
59
+ */
13
60
  include?: ((story: {
14
61
  name: string;
15
62
  kind: string;
16
63
  storyTitle?: string;
17
64
  parameters?: any;
18
65
  }) => boolean) | string | RegExp;
66
+ /**
67
+ * Specifies additional variations for all or some stories (e.g., RTL).
68
+ */
19
69
  variations?: Record<string, any>;
70
+ /**
71
+ * Time in milliseconds that Eyes-Storybook waits for Storybook to load.
72
+ * For Storybook versions 2 and 3, this is also the acknowledgment time.
73
+ * Recommended to use small values (e.g., 3000) for versions 2-3.
74
+ *
75
+ * @default 60000
76
+ */
20
77
  readStoriesTimeout?: number;
78
+ /**
79
+ * Low-level options to send to puppeteer.launch().
80
+ * Use with great care.
81
+ *
82
+ * @example
83
+ * { args: ['--no-sandbox'], headless: false, devtools: true }
84
+ */
21
85
  puppeteerOptions?: Record<string, any>;
86
+ /**
87
+ * Options to send to page.setExtraHTTPHeaders().
88
+ * @see https://pptr.dev/api/puppeteer.page.setextrahttpheaders
89
+ */
22
90
  puppeteerExtraHTTPHeaders?: Record<string, string>;
91
+ /**
92
+ * When to consider navigation finished.
93
+ * @default 'load'
94
+ */
23
95
  navigationWaitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
96
+ /**
97
+ * Cache all requests from browser for faster performance.
98
+ * @default false
99
+ */
24
100
  browserCacheRequests?: boolean;
101
+ /**
102
+ * Array of browser configurations for screenshot generation.
103
+ * Defines size and browser type for generated screenshots.
104
+ *
105
+ * @default [{ width: 1024, height: 768, name: 'chrome' }]
106
+ */
25
107
  browser?: (DesktopBrowserInfo | ChromeEmulationInfo | IOSDeviceInfo | IOSMultiDeviceInfo)[];
108
+ /**
109
+ * Name for the environment in which the application under test is running.
110
+ */
26
111
  envName?: string;
112
+ /**
113
+ * Maximum number of tests that can run concurrently.
114
+ * Default value is the allowed amount for free accounts.
115
+ * For paid accounts, set to your account quota.
116
+ *
117
+ * @default 5
118
+ */
27
119
  testConcurrency?: number;
120
+ /**
121
+ * Whether to display logs of the Eyes-Storybook plugin.
122
+ * @default false
123
+ */
28
124
  showLogs?: boolean;
125
+ /**
126
+ * If tests failed or have visual differences, close with non-zero exit code.
127
+ * @default true
128
+ */
29
129
  exitcode?: boolean;
130
+ /**
131
+ * Array of regions to ignore when comparing checkpoint with baseline screenshot.
132
+ */
30
133
  ignoreRegions?: any[];
134
+ /**
135
+ * Array of regions to consider as floating when comparing checkpoint with baseline.
136
+ */
31
137
  floatingRegions?: any[];
138
+ /**
139
+ * Array of regions to consider as match level Layout when comparing screenshots.
140
+ */
32
141
  layoutRegions?: any[];
142
+ /**
143
+ * Array of regions to consider as match level Strict when comparing screenshots.
144
+ */
33
145
  strictRegions?: any[];
146
+ /**
147
+ * Array of regions to consider as match level Content when comparing screenshots.
148
+ */
34
149
  contentRegions?: any[];
150
+ /**
151
+ * Array of regions to validate accessibility according to configured accessibilityValidation.
152
+ */
35
153
  accessibilityRegions?: any[];
154
+ /**
155
+ * Directory path for JSON results file.
156
+ * If set, creates eyes.json file with test results.
157
+ */
36
158
  jsonFilePath?: string;
159
+ /**
160
+ * Directory path for TAP results file.
161
+ * If set, creates eyes.tap file with test results.
162
+ */
37
163
  tapFilePath?: string;
164
+ /**
165
+ * Directory path for XUnit XML results file.
166
+ * If set, creates eyes.xml file with test results.
167
+ */
38
168
  xmlFilePath?: string;
169
+ /**
170
+ * Selector, function, or timeout to wait before capturing.
171
+ *
172
+ * * number: Milliseconds to wait before capturing.
173
+ * * string: CSS selector to wait for before capturing.
174
+ * * function: Predicate or async function to wait for before capturing.
175
+ *
176
+ * @example
177
+ * waitBeforeCapture: '#container.ready',
178
+ *
179
+ * @example
180
+ * waitBeforeCapture: () => document.querySelector('#container.ready') !== null,
181
+ *
182
+ * @example
183
+ * waitBeforeCapture: 3_000
184
+ *
185
+ * @see https://applitools.com/tutorials/sdks/storybook/component-config#waitBeforeCapture
186
+ */
187
+ waitBeforeCapture?: number | string | (() => boolean | Promise<boolean>);
188
+ /**
189
+ * Unique identifier for the batch of tests - set to the same value if you want to group tests together.
190
+ */
191
+ batchId?: string;
192
+ /**
193
+ * Name for the batch of tests - will appear in the Eyes dashboard.
194
+ */
195
+ batchName?: string;
196
+ /**
197
+ * Sequence name for the batch of tests - will appear in the Eyes dashboard (in the 'insights' page and in the batch details).
198
+ */
199
+ batchSequenceName?: string;
200
+ /**
201
+ * Name for the baseline branch - use it if you want to compare with a specific branch that is different from the current branch.
202
+ *
203
+ * @see for more information and recommendations regarding baseline branches, see the [documentation](https://applitools.com/tutorials/concepts/best-practices/branching).
204
+ */
205
+ baselineBranchName?: string;
206
+ /**
207
+ * Dry run - if true, no actual visual testing will be performed.
208
+ * @default false
209
+ */
210
+ isDisabled?: boolean;
211
+ /**
212
+ * Whether to ignore the baseline when comparing with the checkpoint (will make all checkpoints `new`)
213
+ * @default false
214
+ */
215
+ ignoreBaseline?: boolean;
216
+ /**
217
+ * Settings for accessibility validation.
218
+ */
219
+ accessibilitySettings?: {
220
+ /**
221
+ * Level of accessibility validation to perform.
222
+ */
223
+ level?: 'AA' | 'AAA';
224
+ /**
225
+ * Array of specific accessibility guidelines to check.
226
+ */
227
+ guidelinesVersion?: "WCAG_2_0" | "WCAG_2_1";
228
+ };
229
+ /**
230
+ * Whether to notify when the test is complete (requires to turn on notifications in the Applitools dashboard).
231
+ * @see https://applitools.com/tutorials/integrations/chat-&-notifications/email
232
+ */
233
+ notifyOnCompletion?: boolean;
234
+ /**
235
+ * Whether to show browser logs in the test results.
236
+ * @default false
237
+ */
238
+ showBrowserLogs?: boolean;
239
+ /**
240
+ * A string formatted as `<ShardIndex>/<TotalShards>` to enable test sharding (running tests in parallel).
241
+ * @default undefined
242
+ */
243
+ shard?: string;
39
244
  };
245
+ export type configKeys = keyof ApplitoolsConfig;
246
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/eyes-storybook",
3
- "version": "3.57.2",
3
+ "version": "3.58.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "applitools",
@@ -58,13 +58,13 @@
58
58
  "up:framework": "cd test/fixtures/storybook-versions/${APPLITOOLS_FRAMEWORK_VERSION} && npm ci"
59
59
  },
60
60
  "dependencies": {
61
- "@applitools/core": "4.44.3",
62
- "@applitools/driver": "1.23.2",
63
- "@applitools/eyes": "1.36.1",
61
+ "@applitools/core": "4.44.4",
62
+ "@applitools/driver": "1.23.3",
63
+ "@applitools/eyes": "1.36.2",
64
64
  "@applitools/functional-commons": "1.6.0",
65
65
  "@applitools/logger": "2.2.2",
66
66
  "@applitools/monitoring-commons": "1.0.19",
67
- "@applitools/spec-driver-puppeteer": "1.6.2",
67
+ "@applitools/spec-driver-puppeteer": "1.6.3",
68
68
  "@applitools/ufg-client": "1.17.2",
69
69
  "@applitools/utils": "1.11.1",
70
70
  "@inquirer/prompts": "7.0.1",
package/src/cli.js CHANGED
@@ -32,6 +32,15 @@ const {performance, timeItAsync} = makeTiming();
32
32
 
33
33
  console.log(`Using @applitools/eyes-storybook version ${VERSION}.\n`);
34
34
  const config = generateConfig({argv, defaultConfig, externalConfigParams});
35
+
36
+ if (config.shard) {
37
+ console.log(`Running with shard: ${config.shard.current}/${config.shard.total}`);
38
+ // Log concurrency and shard configuration to console
39
+ if (config.testConcurrency && config.testConcurrency !== 5) {
40
+ console.log(`this shard has concurrency of ${config.testConcurrency} parallel tests`);
41
+ }
42
+ }
43
+
35
44
  const logger = makeLogger({level: config.showLogs ? 'info' : 'silent', label: 'eyes'});
36
45
  await validateAndPopulateConfig({
37
46
  config,
@@ -1,31 +1,31 @@
1
+ // @ts-check
1
2
  'use strict';
2
3
 
4
+ /**
5
+ * @type {Partial<(
6
+ * import('./index').configKeys
7
+ * | 'concurrency'
8
+ * )[]>}
9
+ */
3
10
  const configParams = [
4
11
  'appName',
5
12
  'testName',
6
13
  'displayName',
7
14
  'browser',
8
- 'url',
9
15
  'apiKey',
10
16
  'showLogs',
11
17
  'batch',
12
18
  'batchId',
13
19
  'batchName',
14
20
  'batchSequenceName',
15
- 'batchSequence',
16
21
  'properties',
17
22
  'baselineBranchName',
18
- 'baselineBranch',
19
23
  'baselineEnvName',
20
- 'baselineName',
21
24
  'envName',
22
- 'ignoreCaret',
23
25
  'isDisabled',
24
26
  'matchLevel',
25
27
  'parentBranchName',
26
- 'parentBranch',
27
28
  'branchName',
28
- 'branch',
29
29
  'proxy',
30
30
  'autProxy',
31
31
  'saveDiffs',
@@ -41,9 +41,9 @@ const configParams = [
41
41
  'ignoreDisplacements',
42
42
  'accessibilitySettings',
43
43
  'notifyOnCompletion',
44
- 'batchNotify',
45
44
  'dontCloseBatches',
46
45
  'showBrowserLogs',
46
+ 'shard',
47
47
  ];
48
48
 
49
49
  module.exports = {configParams};
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  'use strict';
2
3
 
3
4
  module.exports = {
@@ -23,4 +24,6 @@ module.exports = {
23
24
  browserRequestsTimeout: undefined,
24
25
  browserHeadersOverride: undefined,
25
26
  browserCacheRequests: undefined,
27
+ showBrowserLogs: undefined,
28
+ shard: undefined,
26
29
  };
@@ -138,6 +138,17 @@ async function eyesStorybook({
138
138
  const stories = await getStoriesWithSpinner();
139
139
 
140
140
  const filteredStories = filterStories({stories, config});
141
+
142
+ // Log filtering and sharding results
143
+ logger.log(`${stories.length} total stories found`);
144
+ if (config.shard) {
145
+ logger.log(
146
+ `${filteredStories.length} stories after filtering and sharding (shard ${config.shard.current}/${config.shard.total})`,
147
+ );
148
+ } else {
149
+ logger.log(`${filteredStories.length} stories after filtering`);
150
+ }
151
+
141
152
  const storiesIncludingVariations = addVariationStories({
142
153
  stories: filteredStories,
143
154
  config,
@@ -2,7 +2,16 @@
2
2
  const getStoryTitle = require('./getStoryTitle');
3
3
 
4
4
  function filterStories({stories, config}) {
5
- return stories.filter(story => filterStory(story, config));
5
+ // Apply existing filters first
6
+ let filteredStories = stories.filter(story => filterStory(story, config));
7
+
8
+ // Apply sharding after other filters
9
+ if (config.shard) {
10
+ const {current, total} = config.shard;
11
+ filteredStories = filteredStories.filter((_story, index) => index % total === current - 1);
12
+ }
13
+
14
+ return filteredStories;
6
15
  }
7
16
 
8
17
  function filterStory(story, config) {
@@ -55,6 +55,15 @@ function generateConfig({argv = {}, defaultConfig = {}, externalConfigParams = [
55
55
  result.viewportSize = result.viewportSize ? result.viewportSize : {width: 1024, height: 600};
56
56
 
57
57
  result.saveNewTests = result.saveNewTests === undefined ? true : result.saveNewTests;
58
+
59
+ // Auto-enable dontCloseBatches when sharding is used (unless explicitly set by user)
60
+ if (result.shard && result.dontCloseBatches === undefined) {
61
+ result.dontCloseBatches = true;
62
+ console.log(
63
+ 'Auto-enabling dontCloseBatches due to sharding configuration - please make sure to close batches manually ( https://applitools.com/tutorials/concepts/best-practices/batching#manually-close-the-shared-batch ).',
64
+ );
65
+ }
66
+
58
67
  result.keepBatchOpen = result.dontCloseBatches;
59
68
  result.fully = result.fully === undefined ? true : false;
60
69
 
package/src/index.ts CHANGED
@@ -1,35 +1,312 @@
1
- import type {ConfigurationPlain, DesktopBrowserInfo, ChromeEmulationInfo, IOSDeviceInfo, IOSMultiDeviceInfo} from '@applitools/eyes'
1
+ import type { ConfigurationPlain, DesktopBrowserInfo, ChromeEmulationInfo, IOSDeviceInfo, IOSMultiDeviceInfo } from '@applitools/eyes';
2
+
3
+ type irrelevantToStorybook = 'waitBeforeScreenshots'
4
+ | 'agentId'
5
+ | 'captureStatusBar'
6
+ | 'concurrentSessions'
7
+ | 'connectionTimeout'
8
+ | 'debugScreenshots'
9
+ | 'defaultMatchSettings'
10
+ | 'disableNMLUrlCache'
11
+ | 'forceFullPageScreenshot'
12
+ | 'hideCaret'
13
+ | 'hideScrollbars'
14
+ | 'hostApp'
15
+ | 'hostAppInfo'
16
+ | 'hostOS'
17
+ | 'hostOSInfo'
18
+ | 'ignoreBaseline'
19
+ | 'ignoreCaret'
20
+ | 'latestCommitInfo'
21
+ | 'isDisabled'
22
+ | 'matchTimeout'
23
+ | 'mobileOptions'
24
+ | 'removeSession'
25
+ | 'rotation'
26
+ | 'scaleRatio'
27
+ | 'scrollRootElement'
28
+ | 'sessionType'
29
+ | 'stitchMode'
30
+ | 'stitchOverlap'
31
+ | 'viewportSize'
32
+ ;
33
+
2
34
 
3
35
  /**
4
- * https://applitools.com/tutorials/sdks/storybook/config
36
+ * Configuration options for Applitools Eyes Storybook integration.
37
+ *
38
+ * This configuration can be specified in three ways:
39
+ * - Command line arguments
40
+ * - Environment variables (uppercase with APPLITOOLS_ prefix)
41
+ * - applitools.config.js file (CommonJS module export)
42
+ *
43
+ * Environment variables override applitools.config.js values.
44
+ *
45
+ * @see https://applitools.com/tutorials/sdks/storybook/config
5
46
  */
6
- export type ApplitoolsConfig = Omit<ConfigurationPlain, 'waitBeforeScreenshots'> & {
7
- storybookUrl?: string;
8
- storybookPort?: number;
9
- storybookHost?: string;
10
- storybookConfigDir?: string;
11
- storybookStaticDir?: string;
12
- showStorybookOutput?: boolean;
13
- runInDocker?: boolean;
14
- include?: ((story: { name: string; kind: string; storyTitle?: string; parameters?: any }) => boolean) | string | RegExp;
15
- variations?: Record<string, any>;
16
- readStoriesTimeout?: number;
17
- puppeteerOptions?: Record<string, any>;
18
- puppeteerExtraHTTPHeaders?: Record<string, string>;
19
- navigationWaitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
20
- browserCacheRequests?: boolean;
21
- browser?: (DesktopBrowserInfo | ChromeEmulationInfo | IOSDeviceInfo | IOSMultiDeviceInfo)[];
22
- envName?: string;
23
- testConcurrency?: number;
24
- showLogs?: boolean;
25
- exitcode?: boolean;
26
- ignoreRegions?: any[];
27
- floatingRegions?: any[];
28
- layoutRegions?: any[];
29
- strictRegions?: any[];
30
- contentRegions?: any[];
31
- accessibilityRegions?: any[];
32
- jsonFilePath?: string;
33
- tapFilePath?: string;
34
- xmlFilePath?: string;
47
+ export type ApplitoolsConfig = Omit<ConfigurationPlain, irrelevantToStorybook | 'waitBeforeCapture'> & {
48
+ /**
49
+ * URL for Storybook instance.
50
+ * @example 'http://localhost:9001'
51
+ */
52
+ storybookUrl?: string;
53
+
54
+ /**
55
+ * Port to run Storybook on.
56
+ * @default 9000
57
+ */
58
+ storybookPort?: number;
59
+
60
+ /**
61
+ * Host to run Storybook on.
62
+ * @default 'localhost'
63
+ */
64
+ storybookHost?: string;
65
+
66
+ /**
67
+ * Path to Storybook's config folder.
68
+ * @default '.storybook'
69
+ */
70
+ storybookConfigDir?: string;
71
+
72
+ /**
73
+ * Path to Storybook's static files folder.
74
+ */
75
+ storybookStaticDir?: string;
76
+
77
+ /**
78
+ * Whether to display Storybook output in console.
79
+ */
80
+ showStorybookOutput?: boolean;
81
+
82
+ /**
83
+ * Set to true if running the SDK in Docker to resolve potential issues.
84
+ * @default false
85
+ */
86
+ runInDocker?: boolean;
87
+
88
+ /**
89
+ * Predicate function, string, or regex specifying which stories should be visually tested.
90
+ * Visual baselines will be created only for specified components.
91
+ * Function receives object with name, kind, storyTitle, and parameters properties.
92
+ *
93
+ * @example
94
+ * // Exclude stories with names starting with [SKIP]
95
+ * ({name, kind, storyTitle, parameters}) => !/^\\[SKIP\\]/.test(name)
96
+ *
97
+ * @default true (includes all stories)
98
+ */
99
+ include?: ((story: { name: string; kind: string; storyTitle?: string; parameters?: any }) => boolean) | string | RegExp;
100
+
101
+ /**
102
+ * Specifies additional variations for all or some stories (e.g., RTL).
103
+ */
104
+ variations?: Record<string, any>;
105
+
106
+ /**
107
+ * Time in milliseconds that Eyes-Storybook waits for Storybook to load.
108
+ * For Storybook versions 2 and 3, this is also the acknowledgment time.
109
+ * Recommended to use small values (e.g., 3000) for versions 2-3.
110
+ *
111
+ * @default 60000
112
+ */
113
+ readStoriesTimeout?: number;
114
+
115
+ /**
116
+ * Low-level options to send to puppeteer.launch().
117
+ * Use with great care.
118
+ *
119
+ * @example
120
+ * { args: ['--no-sandbox'], headless: false, devtools: true }
121
+ */
122
+ puppeteerOptions?: Record<string, any>;
123
+
124
+ /**
125
+ * Options to send to page.setExtraHTTPHeaders().
126
+ * @see https://pptr.dev/api/puppeteer.page.setextrahttpheaders
127
+ */
128
+ puppeteerExtraHTTPHeaders?: Record<string, string>;
129
+
130
+ /**
131
+ * When to consider navigation finished.
132
+ * @default 'load'
133
+ */
134
+ navigationWaitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
135
+
136
+ /**
137
+ * Cache all requests from browser for faster performance.
138
+ * @default false
139
+ */
140
+ browserCacheRequests?: boolean;
141
+
142
+ /**
143
+ * Array of browser configurations for screenshot generation.
144
+ * Defines size and browser type for generated screenshots.
145
+ *
146
+ * @default [{ width: 1024, height: 768, name: 'chrome' }]
147
+ */
148
+ browser?: (DesktopBrowserInfo | ChromeEmulationInfo | IOSDeviceInfo | IOSMultiDeviceInfo)[];
149
+
150
+ /**
151
+ * Name for the environment in which the application under test is running.
152
+ */
153
+ envName?: string;
154
+
155
+ /**
156
+ * Maximum number of tests that can run concurrently.
157
+ * Default value is the allowed amount for free accounts.
158
+ * For paid accounts, set to your account quota.
159
+ *
160
+ * @default 5
161
+ */
162
+ testConcurrency?: number;
163
+
164
+ /**
165
+ * Whether to display logs of the Eyes-Storybook plugin.
166
+ * @default false
167
+ */
168
+ showLogs?: boolean;
169
+
170
+ /**
171
+ * If tests failed or have visual differences, close with non-zero exit code.
172
+ * @default true
173
+ */
174
+ exitcode?: boolean;
175
+
176
+ /**
177
+ * Array of regions to ignore when comparing checkpoint with baseline screenshot.
178
+ */
179
+ ignoreRegions?: any[];
180
+
181
+ /**
182
+ * Array of regions to consider as floating when comparing checkpoint with baseline.
183
+ */
184
+ floatingRegions?: any[];
185
+
186
+ /**
187
+ * Array of regions to consider as match level Layout when comparing screenshots.
188
+ */
189
+ layoutRegions?: any[];
190
+
191
+ /**
192
+ * Array of regions to consider as match level Strict when comparing screenshots.
193
+ */
194
+ strictRegions?: any[];
195
+
196
+ /**
197
+ * Array of regions to consider as match level Content when comparing screenshots.
198
+ */
199
+ contentRegions?: any[];
200
+
201
+ /**
202
+ * Array of regions to validate accessibility according to configured accessibilityValidation.
203
+ */
204
+ accessibilityRegions?: any[];
205
+
206
+ /**
207
+ * Directory path for JSON results file.
208
+ * If set, creates eyes.json file with test results.
209
+ */
210
+ jsonFilePath?: string;
211
+
212
+ /**
213
+ * Directory path for TAP results file.
214
+ * If set, creates eyes.tap file with test results.
215
+ */
216
+ tapFilePath?: string;
217
+
218
+ /**
219
+ * Directory path for XUnit XML results file.
220
+ * If set, creates eyes.xml file with test results.
221
+ */
222
+ xmlFilePath?: string;
223
+
224
+ /**
225
+ * Selector, function, or timeout to wait before capturing.
226
+ *
227
+ * * number: Milliseconds to wait before capturing.
228
+ * * string: CSS selector to wait for before capturing.
229
+ * * function: Predicate or async function to wait for before capturing.
230
+ *
231
+ * @example
232
+ * waitBeforeCapture: '#container.ready',
233
+ *
234
+ * @example
235
+ * waitBeforeCapture: () => document.querySelector('#container.ready') !== null,
236
+ *
237
+ * @example
238
+ * waitBeforeCapture: 3_000
239
+ *
240
+ * @see https://applitools.com/tutorials/sdks/storybook/component-config#waitBeforeCapture
241
+ */
242
+ waitBeforeCapture?: number | string | (() => boolean | Promise<boolean>);
243
+
244
+ /**
245
+ * Unique identifier for the batch of tests - set to the same value if you want to group tests together.
246
+ */
247
+ batchId?: string;
248
+
249
+ /**
250
+ * Name for the batch of tests - will appear in the Eyes dashboard.
251
+ */
252
+ batchName?: string;
253
+
254
+ /**
255
+ * Sequence name for the batch of tests - will appear in the Eyes dashboard (in the 'insights' page and in the batch details).
256
+ */
257
+ batchSequenceName?: string;
258
+
259
+ /**
260
+ * Name for the baseline branch - use it if you want to compare with a specific branch that is different from the current branch.
261
+ *
262
+ * @see for more information and recommendations regarding baseline branches, see the [documentation](https://applitools.com/tutorials/concepts/best-practices/branching).
263
+ */
264
+ baselineBranchName?: string;
265
+
266
+ /**
267
+ * Dry run - if true, no actual visual testing will be performed.
268
+ * @default false
269
+ */
270
+ isDisabled?: boolean;
271
+
272
+ /**
273
+ * Whether to ignore the baseline when comparing with the checkpoint (will make all checkpoints `new`)
274
+ * @default false
275
+ */
276
+ ignoreBaseline?: boolean
277
+
278
+ /**
279
+ * Settings for accessibility validation.
280
+ */
281
+ accessibilitySettings?: {
282
+ /**
283
+ * Level of accessibility validation to perform.
284
+ */
285
+ level?: 'AA' | 'AAA';
286
+
287
+ /**
288
+ * Array of specific accessibility guidelines to check.
289
+ */
290
+ guidelinesVersion?: "WCAG_2_0" | "WCAG_2_1";
291
+ };
292
+
293
+ /**
294
+ * Whether to notify when the test is complete (requires to turn on notifications in the Applitools dashboard).
295
+ * @see https://applitools.com/tutorials/integrations/chat-&-notifications/email
296
+ */
297
+ notifyOnCompletion?: boolean;
298
+
299
+ /**
300
+ * Whether to show browser logs in the test results.
301
+ * @default false
302
+ */
303
+ showBrowserLogs?: boolean;
304
+
305
+ /**
306
+ * A string formatted as `<ShardIndex>/<TotalShards>` to enable test sharding (running tests in parallel).
307
+ * @default undefined
308
+ */
309
+ shard?: string;
35
310
  }
311
+
312
+ export type configKeys = keyof ApplitoolsConfig;
@@ -1,6 +1,8 @@
1
+ // @ts-check
1
2
  'use strict';
2
3
  const chalk = require('chalk');
3
4
  const {Configuration} = require('@applitools/eyes');
5
+ const defaultConfig = require('./../defaultConfig');
4
6
 
5
7
  // Get all keys directly from the base Configuration class
6
8
  const dynamicBaseKeys = [
@@ -30,31 +32,30 @@ const knownAliases = [
30
32
  const applitoolsBaseKeys = [...dynamicBaseKeys, ...knownAliases];
31
33
 
32
34
  // A list of all keys specific to the eyes-storybook package.
35
+ /**
36
+ * @type {Array<keyof (import('../index').ApplitoolsConfig) |
37
+ * 'reloadPagePerStory' |
38
+ * 'startStorybookServerTimeout' |
39
+ * 'networkBlockPatterns' |
40
+ * 'browserRequestsTimeout' |
41
+ * 'browserHeadersOverride' |
42
+ * 'waitBeforeScreenshot' |
43
+ * 'waitBeforeScreenshots' |
44
+ * 'fakeIE' |
45
+ * 'storyConfiguration' |
46
+ * 'storyDataGap' |
47
+ * 'packagePath'
48
+ * >}
49
+ */
33
50
  const storybookSpecificKeys = [
34
- 'storybookPort',
35
- 'storybookHost',
36
- 'storybookConfigDir',
37
- 'storybookUrl',
38
- 'storybookStaticDir',
39
- 'showStorybookOutput',
40
- 'exitcode',
41
- 'include',
42
- 'variations',
51
+ 'variations', // TODO - are we sure it should be supported from the applitools config file? https://applitools.com/tutorials/sdks/storybook/component-config#variations
43
52
  'runInDocker',
44
- 'readStoriesTimeout',
45
- 'reloadPagePerStory',
46
- 'startStorybookServerTimeout',
47
53
  'showLogs',
48
54
 
49
55
  // Browser & Puppeteer Control
50
56
  'browser',
51
57
  'puppeteerOptions',
52
- 'puppeteerExtraHTTPHeaders',
53
- 'navigationWaitUntil',
54
- 'networkBlockPatterns',
55
- 'browserRequestsTimeout',
56
- 'browserHeadersOverride',
57
- 'browserCacheRequests',
58
+ 'puppeteerExtraHTTPHeaders', // created for a specific user, TODO - consider removing
58
59
 
59
60
  // Region Matching
60
61
  'ignoreRegions',
@@ -72,7 +73,6 @@ const storybookSpecificKeys = [
72
73
  // Legacy & Internal
73
74
  'waitBeforeScreenshot', // backward compatibility
74
75
  'waitBeforeScreenshots', // backward compatibility
75
- 'waitBeforeCapture',
76
76
  'fakeIE',
77
77
  'storyConfiguration',
78
78
  'storyDataGap',
@@ -80,7 +80,11 @@ const storybookSpecificKeys = [
80
80
  ];
81
81
 
82
82
  // Combine all known keys into a single Set for efficient O(1) lookups.
83
- const knownKeys = new Set([...storybookSpecificKeys, ...applitoolsBaseKeys]);
83
+ const knownKeys = new Set([
84
+ ...storybookSpecificKeys,
85
+ ...applitoolsBaseKeys,
86
+ ...Object.keys(defaultConfig),
87
+ ]);
84
88
 
85
89
  /**
86
90
  * Checks the user's config object for any keys that are not recognized
@@ -101,6 +105,7 @@ function logUnrecognizedKeys(config, logger) {
101
105
  unrecognizedKeys.forEach(key => {
102
106
  console.log(chalk.yellow(` - ${key}`));
103
107
  });
108
+ // @ts-ignore
104
109
  logger.log('\n');
105
110
 
106
111
  const docsUrl = 'https://applitools.com/tutorials/sdks/storybook/config#properties';
@@ -83,6 +83,27 @@ module.exports = {
83
83
  choices: ['load', 'domcontentloaded', 'networkidle0', 'networkidle2'], // PuppeteerLifeCycleEvent values - https://pptr.dev/api/puppeteer.puppeteerlifecycleevent
84
84
  },
85
85
 
86
+ shard: {
87
+ description: 'Shard tests (format: current/total, e.g., "1/3")',
88
+ requiresArg: true,
89
+ string: true,
90
+ coerce: function (arg) {
91
+ const match = arg.match(/^(\d+)\/(\d+)$/);
92
+ if (!match) {
93
+ throw new Error('shard must be in format "current/total" (e.g., "1/3")');
94
+ }
95
+ const current = parseInt(match[1], 10);
96
+ const total = parseInt(match[2], 10);
97
+ if (current < 1 || current > total) {
98
+ throw new Error(`shard current (${current}) must be between 1 and total (${total})`);
99
+ }
100
+ if (total < 1) {
101
+ throw new Error(`shard total (${total}) must be at least 1`);
102
+ }
103
+ return {current, total};
104
+ },
105
+ },
106
+
86
107
  // general
87
108
  exitcode: {
88
109
  alias: 'e',
@@ -1,138 +0,0 @@
1
-
2
- function __getStoryByIndex(...args) {
3
- var getStoryByIndex = (function () {
4
- 'use strict';
5
-
6
- const API_VERSIONS = {
7
- v4: 'v4',
8
- v5: 'v5',
9
- v5_2: 'v5_2',
10
- };
11
-
12
- function getClientAPI() {
13
- const frameWindow = getFrameWindow();
14
- const clientAPI = frameWindow.__STORYBOOK_CLIENT_API__;
15
- const addons = frameWindow.__STORYBOOK_ADDONS;
16
-
17
- return getAPI(getStorybookVersion());
18
-
19
- function getStorybookVersion() {
20
- const addons = frameWindow.__STORYBOOK_ADDONS;
21
-
22
- if (frameWindow.__STORYBOOK_STORY_STORE__) {
23
- return API_VERSIONS.v5_2;
24
- } else if (frameWindow.__STORYBOOK_CLIENT_API__ && frameWindow.__STORYBOOK_CLIENT_API__.raw) {
25
- return API_VERSIONS.v5;
26
- } else if (
27
- addons &&
28
- addons.channel &&
29
- addons.channel._listeners &&
30
- addons.channel._listeners.setCurrentStory &&
31
- addons.channel._listeners.setCurrentStory[0]
32
- ) {
33
- return API_VERSIONS.v4;
34
- } else {
35
- throw new Error("Cannot get client API: couldn't detect storybook version");
36
- }
37
- }
38
-
39
- function getAPI(version) {
40
- if (version) {
41
- let api;
42
- switch (version) {
43
- case API_VERSIONS.v4: {
44
- api = {
45
- getStories: () => {
46
- if (!frameWindow.__APPLITOOLS_STORIES) {
47
- frameWindow.__APPLITOOLS_STORIES = Object.values(clientAPI._storyStore._data)
48
- .map(({stories, kind}) => Object.values(stories).map(s => ({...s, kind})))
49
- .flat();
50
- }
51
- return frameWindow.__APPLITOOLS_STORIES;
52
- },
53
- selectStory: i => {
54
- const {kind, name: story} = api.getStories()[i];
55
- addons.channel._listeners.setCurrentStory[0]({kind, story});
56
- },
57
- };
58
- break;
59
- }
60
-
61
- case API_VERSIONS.v5: {
62
- api = {
63
- getStories: () => {
64
- return clientAPI.raw();
65
- },
66
- selectStory: i => {
67
- clientAPI._storyStore.setSelection(clientAPI.raw()[i]);
68
- },
69
- };
70
- break;
71
- }
72
-
73
- case API_VERSIONS.v5_2: {
74
- api = {
75
- getStories: () => {
76
- return clientAPI.raw();
77
- },
78
- selectStory: i => {
79
- frameWindow.__STORYBOOK_STORY_STORE__.setSelection({storyId: clientAPI.raw()[i].id});
80
- },
81
- };
82
- break;
83
- }
84
- }
85
-
86
- return {version, ...api};
87
- }
88
- }
89
- }
90
-
91
- function getFrameWindow() {
92
- if (/iframe.html/.test(window.location.href)) {
93
- return window;
94
- }
95
-
96
- const innerFrameWindow = Array.prototype.find.call(window.frames, frame => {
97
- try {
98
- return /\/iframe.html/.test(frame.location.href);
99
- } catch (e) {}
100
- });
101
-
102
- if (innerFrameWindow) {
103
- return innerFrameWindow;
104
- }
105
-
106
- if (window.__STORYBOOK_CLIENT_API__) {
107
- return window;
108
- }
109
-
110
- throw new Error('Cannot get client API: no frameWindow');
111
- }
112
-
113
- var getClientAPI_1 = getClientAPI;
114
-
115
- function getStoryIndex(index) {
116
- let api;
117
- try {
118
- api = getClientAPI_1();
119
- const story = api.getStories()[index];
120
- if (!story) {
121
- console.log('error cannot get story', index);
122
- return;
123
- }
124
- return story;
125
- } catch (ex) {
126
- return {message: ex.message, version: api ? api.version : undefined};
127
- }
128
- }
129
-
130
- var getStoryByIndex = getStoryIndex;
131
-
132
- return getStoryByIndex;
133
-
134
- }());
135
-
136
- return getStoryByIndex.apply(this, args);
137
- }
138
- module.exports = __getStoryByIndex