creevey 0.9.0-beta.18 → 0.9.0-beta.19

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.
Binary file
@@ -14,6 +14,7 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
14
14
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
15
  const defaultBrowser = exports.defaultBrowser = 'chrome';
16
16
  const defaultConfig = exports.defaultConfig = {
17
+ disableTelemetry: false,
17
18
  useDocker: true,
18
19
  dockerImage: 'aerokube/selenoid:latest-release',
19
20
  dockerImagePlatform: '',
@@ -13,6 +13,7 @@ var _types = require("../../types");
13
13
  var _utils = require("../utils");
14
14
  var _messages = require("../messages");
15
15
  var _logger = require("../logger");
16
+ var _telemetry = require("../telemetry");
16
17
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
18
  const writeFileAsync = (0, _util.promisify)(_fs.writeFile);
18
19
  const copyFileAsync = (0, _util.promisify)(_fs.copyFile);
@@ -97,7 +98,7 @@ async function _default(config, options, resolveApi) {
97
98
  return;
98
99
  }
99
100
  runner.once('stop', () => {
100
- var _runner6;
101
+ var _runner6, _runner7;
101
102
  const tests = Object.values(((_runner6 = runner) === null || _runner6 === void 0 ? void 0 : _runner6.status.tests) ?? {});
102
103
  const isSuccess = tests.filter(_types.isDefined).filter(({
103
104
  skip
@@ -107,8 +108,13 @@ async function _default(config, options, resolveApi) {
107
108
  // TODO output summary
108
109
  process.exitCode = isSuccess ? 0 : -1;
109
110
  if (!config.failFast) outputUnnecessaryImages(config.screenDir, (0, _utils.testsToImages)(tests));
110
- // eslint-disable-next-line no-process-exit
111
- void (0, _utils.shutdownWorkers)().then(() => process.exit());
111
+ (0, _telemetry.sendScreenshotsCount)(config, options, (_runner7 = runner) === null || _runner7 === void 0 ? void 0 : _runner7.status).catch(reason => {
112
+ const error = reason instanceof Error ? reason.stack ?? reason.message : reason;
113
+ _logger.logger.warn(`Can't send telemetry: ${error}`);
114
+ }).finally(() => {
115
+ // eslint-disable-next-line no-process-exit
116
+ void (0, _utils.shutdownWorkers)().then(() => process.exit());
117
+ });
112
118
  });
113
119
  // TODO grep
114
120
  runner.start(Object.keys(runner.status.tests));
@@ -26,15 +26,16 @@ async function createSelenoidConfig(browsers, {
26
26
  browsers.forEach(({
27
27
  browserName,
28
28
  version = 'latest',
29
- dockerImage = `selenoid/${browserName}:${version}`,
29
+ browserVersion = version,
30
+ dockerImage = `selenoid/${browserName}:${browserVersion}`,
30
31
  webdriverCommand = []
31
32
  }) => {
32
33
  if (!selenoidConfig[browserName]) selenoidConfig[browserName] = {
33
- default: version,
34
+ default: browserVersion,
34
35
  versions: {}
35
36
  };
36
37
  if (!useDocker && webdriverCommand.length == 0) throw new Error('Please specify "webdriverCommand" browser option with path to browser webdriver');
37
- selenoidConfig[browserName].versions[version] = {
38
+ selenoidConfig[browserName].versions[browserVersion] = {
38
39
  image: useDocker ? dockerImage : webdriverCommand,
39
40
  port: '4444',
40
41
  path: !useDocker || ['chrome', 'opera', 'webkit', 'MicrosoftEdge'].includes(browserName) ? '/' : '/wd/hub'
@@ -60,4 +60,5 @@ const loadStories = async (_config, _options, storiesListener) => {
60
60
  return stories;
61
61
  }
62
62
  };
63
- exports.loadStories = loadStories;
63
+ exports.loadStories = loadStories;
64
+ loadStories.providerName = 'browser';
@@ -59,4 +59,5 @@ async function parseParams(config, listener) {
59
59
  });
60
60
  }
61
61
  return (0, _parser.default)(testFiles);
62
- }
62
+ }
63
+ loadStories.providerName = 'hybrid';
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.sendScreenshotsCount = sendScreenshotsCount;
7
+ var _path = _interopRequireDefault(require("path"));
8
+ var _https = _interopRequireDefault(require("https"));
9
+ var _shelljs = require("shelljs");
10
+ var _qs = require("qs");
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ const konturGitHost = 'git.skbkontur.ru';
13
+ const trackId = 232; // front_infra
14
+ const origin = 'http://localhost/';
15
+ const category = 'screenshots';
16
+ const action = 'count';
17
+ const label = 'full_run';
18
+ function buildPathname(info) {
19
+ return `/track-event?${(0, _qs.stringify)({
20
+ id: trackId,
21
+ c: category,
22
+ a: action,
23
+ l: label,
24
+ cv: JSON.stringify(info),
25
+ ts: new Date().toISOString(),
26
+ url: origin
27
+ })}`;
28
+ }
29
+ function sanitizeGridUrl(gridUrl) {
30
+ const url = new URL(gridUrl);
31
+ url.username = url.username ? '********' : '';
32
+ url.password = url.password ? '********' : '';
33
+ return url.toString();
34
+ }
35
+ function tryGetRepoUrl() {
36
+ try {
37
+ const gitRemoteOutput = (0, _shelljs.exec)('git remote -v', {
38
+ silent: true
39
+ });
40
+ const [, repoUrl] = gitRemoteOutput.stdout.match(/origin\s+(.*)\s+\(fetch\)/) ?? [];
41
+ return [repoUrl, null];
42
+ } catch (error) {
43
+ return [undefined, error];
44
+ }
45
+ }
46
+ function tryGetStorybookVersion() {
47
+ try {
48
+ const storybookPackageOutput = (0, _shelljs.exec)(`node -e "console.log(JSON.stringify(require('storybook/package.json')))"`, {
49
+ silent: true
50
+ });
51
+ const storybookPackage = JSON.parse(storybookPackageOutput);
52
+ return [storybookPackage.version, null];
53
+ } catch (error) {
54
+ return [undefined, error];
55
+ }
56
+ }
57
+ function tryGetCreeveyVersion() {
58
+ try {
59
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, node/no-missing-require
60
+ const creeveyPackage = require('creevey/package.json');
61
+ return [creeveyPackage.version, null];
62
+ } catch (error) {
63
+ return [undefined, error];
64
+ }
65
+ }
66
+ async function sendScreenshotsCount(config, options, status) {
67
+ var _config$storiesProvid;
68
+ const [repoUrl] = tryGetRepoUrl();
69
+ const isKonturRepo = repoUrl === null || repoUrl === void 0 ? void 0 : repoUrl.includes(konturGitHost);
70
+ if (!isKonturRepo || config.disableTelemetry) return;
71
+ const [creeveyVersion, creeveyVersionError] = tryGetCreeveyVersion();
72
+ const [storybookVersion, storybookVersionError] = tryGetStorybookVersion();
73
+ const gridUrl = config.gridUrl ? sanitizeGridUrl(config.gridUrl) : undefined;
74
+ const info = {
75
+ repoUrl: repoUrl ?? 'unknown',
76
+ creeveyVersion: creeveyVersion ?? 'unknown',
77
+ storybookVersion: storybookVersion ?? 'unknown',
78
+ options: options._,
79
+ gridUrl,
80
+ screenDir: config.screenDir ? _path.default.relative(process.cwd(), config.screenDir) : undefined,
81
+ useDocker: config.useDocker,
82
+ dockerImage: config.dockerImage,
83
+ maxRetries: config.maxRetries,
84
+ diffOptions: config.diffOptions,
85
+ storiesProvider: ((_config$storiesProvid = config.storiesProvider) === null || _config$storiesProvid === void 0 ? void 0 : _config$storiesProvid.providerName) ?? 'unknown',
86
+ browsers: Object.fromEntries(Object.entries(config.browsers ?? {}).map(([name, browser]) => [name, typeof browser === 'object' ? {
87
+ name: browser.name,
88
+ gridUrl: browser.gridUrl ? sanitizeGridUrl(browser.gridUrl) : undefined,
89
+ browserName: browser.browserName,
90
+ browserVersion: browser.browserVersion,
91
+ platformName: browser.platformName,
92
+ viewport: browser.viewport,
93
+ limit: browser.limit,
94
+ dockerImage: browser.dockerImage,
95
+ 'se:teamname': browser['se:teamname']
96
+ } : browser])),
97
+ tests: Object.values((status === null || status === void 0 ? void 0 : status.tests) ?? {}).filter(x => Boolean(x)).map(test => ({
98
+ id: test.id,
99
+ browser: test.browser,
100
+ testName: test.testName,
101
+ storyPath: test.storyPath,
102
+ status: test.status
103
+ })),
104
+ errors: [creeveyVersionError, storybookVersionError].some(Boolean) ? [creeveyVersionError ? `Error while getting creevey version: ${creeveyVersionError.message}` : undefined, storybookVersionError ? `Error while getting storybook version: ${storybookVersionError.message}` : undefined].filter(Boolean) : undefined
105
+ };
106
+ return new Promise((resolve, reject) => {
107
+ const req = _https.default.request({
108
+ host: 'metrika.kontur.ru',
109
+ path: buildPathname(info),
110
+ protocol: 'https:'
111
+ }, res => {
112
+ if (res.statusCode) {
113
+ if (res.statusCode >= 200 && res.statusCode < 300) {
114
+ resolve();
115
+ } else if (res.statusCode >= 300 && res.statusCode < 400) {
116
+ reject(new Error(`Redirection error: ${res.statusCode}`));
117
+ } else if (res.statusCode >= 400 && res.statusCode < 500) {
118
+ reject(new Error(`Client error: ${res.statusCode}`));
119
+ } else if (res.statusCode >= 500 && res.statusCode < 600) {
120
+ reject(new Error(`Server error: ${res.statusCode}`));
121
+ } else {
122
+ reject(new Error(`Unexpected status code: ${res.statusCode}`));
123
+ }
124
+ } else {
125
+ reject(new Error('No status code received'));
126
+ }
127
+ });
128
+ req.on('error', reject);
129
+ req.end();
130
+ });
131
+ }
@@ -4,6 +4,7 @@ import { loadStories as browserStoriesProvider } from './storybook/providers/bro
4
4
  import { isDefined } from '../types';
5
5
  export const defaultBrowser = 'chrome';
6
6
  export const defaultConfig = {
7
+ disableTelemetry: false,
7
8
  useDocker: true,
8
9
  dockerImage: 'aerokube/selenoid:latest-release',
9
10
  dockerImagePlatform: '',
@@ -7,6 +7,7 @@ import { isDefined } from '../../types';
7
7
  import { shutdown, shutdownWorkers, testsToImages, readDirRecursive } from '../utils';
8
8
  import { subscribeOn } from '../messages';
9
9
  import { logger } from '../logger';
10
+ import { sendScreenshotsCount } from '../telemetry';
10
11
  const writeFileAsync = promisify(writeFile);
11
12
  const copyFileAsync = promisify(copyFile);
12
13
  const readdirAsync = promisify(readdir);
@@ -90,7 +91,7 @@ export default async function (config, options, resolveApi) {
90
91
  return;
91
92
  }
92
93
  runner.once('stop', () => {
93
- var _runner6;
94
+ var _runner6, _runner7;
94
95
  const tests = Object.values(((_runner6 = runner) === null || _runner6 === void 0 ? void 0 : _runner6.status.tests) ?? {});
95
96
  const isSuccess = tests.filter(isDefined).filter(({
96
97
  skip
@@ -100,8 +101,13 @@ export default async function (config, options, resolveApi) {
100
101
  // TODO output summary
101
102
  process.exitCode = isSuccess ? 0 : -1;
102
103
  if (!config.failFast) outputUnnecessaryImages(config.screenDir, testsToImages(tests));
103
- // eslint-disable-next-line no-process-exit
104
- void shutdownWorkers().then(() => process.exit());
104
+ sendScreenshotsCount(config, options, (_runner7 = runner) === null || _runner7 === void 0 ? void 0 : _runner7.status).catch(reason => {
105
+ const error = reason instanceof Error ? reason.stack ?? reason.message : reason;
106
+ logger.warn(`Can't send telemetry: ${error}`);
107
+ }).finally(() => {
108
+ // eslint-disable-next-line no-process-exit
109
+ void shutdownWorkers().then(() => process.exit());
110
+ });
105
111
  });
106
112
  // TODO grep
107
113
  runner.start(Object.keys(runner.status.tests));
@@ -18,15 +18,16 @@ async function createSelenoidConfig(browsers, {
18
18
  browsers.forEach(({
19
19
  browserName,
20
20
  version = 'latest',
21
- dockerImage = `selenoid/${browserName}:${version}`,
21
+ browserVersion = version,
22
+ dockerImage = `selenoid/${browserName}:${browserVersion}`,
22
23
  webdriverCommand = []
23
24
  }) => {
24
25
  if (!selenoidConfig[browserName]) selenoidConfig[browserName] = {
25
- default: version,
26
+ default: browserVersion,
26
27
  versions: {}
27
28
  };
28
29
  if (!useDocker && webdriverCommand.length == 0) throw new Error('Please specify "webdriverCommand" browser option with path to browser webdriver');
29
- selenoidConfig[browserName].versions[version] = {
30
+ selenoidConfig[browserName].versions[browserVersion] = {
30
31
  image: useDocker ? dockerImage : webdriverCommand,
31
32
  port: '4444',
32
33
  path: !useDocker || ['chrome', 'opera', 'webkit', 'MicrosoftEdge'].includes(browserName) ? '/' : '/wd/hub'
@@ -52,4 +52,5 @@ export const loadStories = async (_config, _options, storiesListener) => {
52
52
  });
53
53
  return stories;
54
54
  }
55
- };
55
+ };
56
+ loadStories.providerName = 'browser';
@@ -49,4 +49,5 @@ async function parseParams(config, listener) {
49
49
  });
50
50
  }
51
51
  return parse(testFiles);
52
- }
52
+ }
53
+ loadStories.providerName = 'hybrid';
@@ -0,0 +1,124 @@
1
+ import path from 'path';
2
+ import https from 'https';
3
+ import { exec } from 'shelljs';
4
+ import { stringify } from 'qs';
5
+ const konturGitHost = 'git.skbkontur.ru';
6
+ const trackId = 232; // front_infra
7
+ const origin = 'http://localhost/';
8
+ const category = 'screenshots';
9
+ const action = 'count';
10
+ const label = 'full_run';
11
+ function buildPathname(info) {
12
+ return `/track-event?${stringify({
13
+ id: trackId,
14
+ c: category,
15
+ a: action,
16
+ l: label,
17
+ cv: JSON.stringify(info),
18
+ ts: new Date().toISOString(),
19
+ url: origin
20
+ })}`;
21
+ }
22
+ function sanitizeGridUrl(gridUrl) {
23
+ const url = new URL(gridUrl);
24
+ url.username = url.username ? '********' : '';
25
+ url.password = url.password ? '********' : '';
26
+ return url.toString();
27
+ }
28
+ function tryGetRepoUrl() {
29
+ try {
30
+ const gitRemoteOutput = exec('git remote -v', {
31
+ silent: true
32
+ });
33
+ const [, repoUrl] = gitRemoteOutput.stdout.match(/origin\s+(.*)\s+\(fetch\)/) ?? [];
34
+ return [repoUrl, null];
35
+ } catch (error) {
36
+ return [undefined, error];
37
+ }
38
+ }
39
+ function tryGetStorybookVersion() {
40
+ try {
41
+ const storybookPackageOutput = exec(`node -e "console.log(JSON.stringify(require('storybook/package.json')))"`, {
42
+ silent: true
43
+ });
44
+ const storybookPackage = JSON.parse(storybookPackageOutput);
45
+ return [storybookPackage.version, null];
46
+ } catch (error) {
47
+ return [undefined, error];
48
+ }
49
+ }
50
+ function tryGetCreeveyVersion() {
51
+ try {
52
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, node/no-missing-require
53
+ const creeveyPackage = require('creevey/package.json');
54
+ return [creeveyPackage.version, null];
55
+ } catch (error) {
56
+ return [undefined, error];
57
+ }
58
+ }
59
+ export async function sendScreenshotsCount(config, options, status) {
60
+ var _config$storiesProvid;
61
+ const [repoUrl] = tryGetRepoUrl();
62
+ const isKonturRepo = repoUrl === null || repoUrl === void 0 ? void 0 : repoUrl.includes(konturGitHost);
63
+ if (!isKonturRepo || config.disableTelemetry) return;
64
+ const [creeveyVersion, creeveyVersionError] = tryGetCreeveyVersion();
65
+ const [storybookVersion, storybookVersionError] = tryGetStorybookVersion();
66
+ const gridUrl = config.gridUrl ? sanitizeGridUrl(config.gridUrl) : undefined;
67
+ const info = {
68
+ repoUrl: repoUrl ?? 'unknown',
69
+ creeveyVersion: creeveyVersion ?? 'unknown',
70
+ storybookVersion: storybookVersion ?? 'unknown',
71
+ options: options._,
72
+ gridUrl,
73
+ screenDir: config.screenDir ? path.relative(process.cwd(), config.screenDir) : undefined,
74
+ useDocker: config.useDocker,
75
+ dockerImage: config.dockerImage,
76
+ maxRetries: config.maxRetries,
77
+ diffOptions: config.diffOptions,
78
+ storiesProvider: ((_config$storiesProvid = config.storiesProvider) === null || _config$storiesProvid === void 0 ? void 0 : _config$storiesProvid.providerName) ?? 'unknown',
79
+ browsers: Object.fromEntries(Object.entries(config.browsers ?? {}).map(([name, browser]) => [name, typeof browser === 'object' ? {
80
+ name: browser.name,
81
+ gridUrl: browser.gridUrl ? sanitizeGridUrl(browser.gridUrl) : undefined,
82
+ browserName: browser.browserName,
83
+ browserVersion: browser.browserVersion,
84
+ platformName: browser.platformName,
85
+ viewport: browser.viewport,
86
+ limit: browser.limit,
87
+ dockerImage: browser.dockerImage,
88
+ 'se:teamname': browser['se:teamname']
89
+ } : browser])),
90
+ tests: Object.values((status === null || status === void 0 ? void 0 : status.tests) ?? {}).filter(x => Boolean(x)).map(test => ({
91
+ id: test.id,
92
+ browser: test.browser,
93
+ testName: test.testName,
94
+ storyPath: test.storyPath,
95
+ status: test.status
96
+ })),
97
+ errors: [creeveyVersionError, storybookVersionError].some(Boolean) ? [creeveyVersionError ? `Error while getting creevey version: ${creeveyVersionError.message}` : undefined, storybookVersionError ? `Error while getting storybook version: ${storybookVersionError.message}` : undefined].filter(Boolean) : undefined
98
+ };
99
+ return new Promise((resolve, reject) => {
100
+ const req = https.request({
101
+ host: 'metrika.kontur.ru',
102
+ path: buildPathname(info),
103
+ protocol: 'https:'
104
+ }, res => {
105
+ if (res.statusCode) {
106
+ if (res.statusCode >= 200 && res.statusCode < 300) {
107
+ resolve();
108
+ } else if (res.statusCode >= 300 && res.statusCode < 400) {
109
+ reject(new Error(`Redirection error: ${res.statusCode}`));
110
+ } else if (res.statusCode >= 400 && res.statusCode < 500) {
111
+ reject(new Error(`Client error: ${res.statusCode}`));
112
+ } else if (res.statusCode >= 500 && res.statusCode < 600) {
113
+ reject(new Error(`Server error: ${res.statusCode}`));
114
+ } else {
115
+ reject(new Error(`Unexpected status code: ${res.statusCode}`));
116
+ }
117
+ } else {
118
+ reject(new Error('No status code received'));
119
+ }
120
+ });
121
+ req.on('error', reject);
122
+ req.end();
123
+ });
124
+ }
@@ -0,0 +1,2 @@
1
+ import { Config, CreeveyStatus, Options } from '../types';
2
+ export declare function sendScreenshotsCount(config: Partial<Config>, options: Options, status?: CreeveyStatus): Promise<void>;
@@ -72,6 +72,11 @@ export interface CreeveyStory {
72
72
  }
73
73
  export interface Capabilities {
74
74
  browserName: string;
75
+ browserVersion?: string;
76
+ platformName?: string;
77
+ /**
78
+ * @deprecated use `browserVersion` instead
79
+ */
75
80
  version?: string;
76
81
  [prop: string]: unknown;
77
82
  }
@@ -86,7 +91,7 @@ export declare type BrowserConfig = Capabilities & {
86
91
  _storybookGlobals?: StorybookGlobals;
87
92
  /**
88
93
  * Specify custom docker image. Used only with `useDocker == true`
89
- * @default `selenoid/${browserName}:${version ?? 'latest'}`
94
+ * @default `selenoid/${browserName}:${browserVersion ?? 'latest'}`
90
95
  */
91
96
  dockerImage?: string;
92
97
  /**
@@ -227,10 +232,20 @@ export interface Config {
227
232
  testsRegex?: RegExp;
228
233
  testsDir?: string;
229
234
  tsConfig?: string;
235
+ /**
236
+ * Telemetry contains information about Creevey and Storybook versions, used Creevey config, browsers and tests meta.
237
+ * It's being sent only for projects from git.skbkontur.ru
238
+ * @default false
239
+ */
240
+ disableTelemetry?: boolean;
241
+ }
242
+ export interface StoriesProvider<T = any> {
243
+ (config: Config, options: T, storiesListener: (stories: Map<string, StoryInput[]>) => void): Promise<StoriesRaw>;
244
+ providerName?: string;
230
245
  }
231
- export declare type StoriesProvider<T = any> = (config: Config, options: T, storiesListener: (stories: Map<string, StoryInput[]>) => void) => Promise<StoriesRaw>;
232
246
  export declare type CreeveyConfig = Partial<Config>;
233
247
  export interface Options {
248
+ _: string[];
234
249
  config?: string;
235
250
  port: number;
236
251
  ui: boolean;
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "addon",
14
14
  "test"
15
15
  ],
16
- "version": "0.9.0-beta.18",
16
+ "version": "0.9.0-beta.19",
17
17
  "bin": "./lib/cjs/cli.js",
18
18
  "exports": {
19
19
  ".": {
@@ -32,7 +32,8 @@
32
32
  },
33
33
  "./lib/cjs/client/addon/preview.js": {
34
34
  "default": "./lib/cjs/client/addon/preview.js"
35
- }
35
+ },
36
+ "./package.json": "./package.json"
36
37
  },
37
38
  "main": "lib/cjs/index.js",
38
39
  "module": "lib/esm/index.js",