@applitools/core 2.5.2 → 2.5.3-legacy
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/dist/abort.js +40 -0
- package/dist/automation/abort.js +47 -0
- package/dist/automation/close.js +52 -0
- package/dist/automation/extract-text.js +3 -2
- package/dist/automation/get-nml-client.js +36 -0
- package/dist/automation/get-results.js +21 -0
- package/dist/automation/get-viewport-size.js +3 -2
- package/dist/automation/locate-text.js +3 -2
- package/dist/automation/locate.js +3 -2
- package/dist/automation/set-viewport-size.js +3 -2
- package/dist/automation/types.js +0 -15
- package/dist/automation/utils/report-kobiton.js +21 -0
- package/dist/automation/utils/take-screenshot.js +4 -2
- package/dist/automation/utils/to-base-check-settings.js +69 -7
- package/dist/automation/utils/to-environment-key.js +31 -0
- package/dist/automation/utils/uniquify-environments.js +27 -0
- package/dist/automation/utils/wait-for-lazy-load.js +9 -8
- package/dist/autonomous/core.js +25 -0
- package/dist/autonomous/create-render-results.js +49 -0
- package/dist/autonomous/create-render-target.js +19 -0
- package/dist/autonomous/get-render-results.js +55 -0
- package/dist/autonomous/open-eyes.js +13 -0
- package/dist/autonomous/start-renders.js +67 -0
- package/dist/autonomous/take-snapshots.js +16 -0
- package/dist/autonomous/types.js +17 -0
- package/dist/check-and-close.js +32 -15
- package/dist/check.js +33 -30
- package/dist/classic/check-and-close.js +189 -46
- package/dist/classic/check.js +319 -60
- package/dist/classic/core.js +6 -5
- package/dist/classic/get-base-eyes.js +27 -7
- package/dist/classic/open-eyes.js +47 -57
- package/dist/classic/utils/extract-default-environments.js +73 -0
- package/dist/classic/utils/take-dom-capture.js +2 -1
- package/dist/classic/utils/take-screenshots.js +63 -0
- package/dist/cli/cli.js +99 -3
- package/dist/close-batch.js +8 -7
- package/dist/close.js +32 -4
- package/dist/core.js +43 -9
- package/dist/delete-test.js +9 -7
- package/dist/extract-text.js +9 -5
- package/dist/get-account-info.js +36 -0
- package/dist/get-ec-client.js +3 -2
- package/dist/get-eyes-results.js +29 -3
- package/dist/get-manager-results.js +52 -18
- package/dist/get-typed-eyes.js +5 -6
- package/dist/index.js +3 -3
- package/dist/lang.js +31 -0
- package/dist/locate-text.js +7 -5
- package/dist/locate.js +7 -5
- package/dist/make-manager.js +34 -11
- package/dist/offline/merge-configs.js +42 -0
- package/dist/offline/run-offline-snapshots.js +341 -0
- package/dist/open-eyes.js +192 -48
- package/dist/run-offline-snapshots.js +336 -0
- package/dist/troubleshoot/check-network.js +5 -1
- package/dist/troubleshoot/eyes.js +3 -3
- package/dist/troubleshoot/logs.js +76 -0
- package/dist/troubleshoot/ufg.js +23 -16
- package/dist/ufg/check-and-close.js +109 -174
- package/dist/ufg/check.js +111 -178
- package/dist/ufg/core.js +10 -8
- package/dist/ufg/create-render-target-from-snapshot.js +21 -0
- package/dist/ufg/get-base-eyes.js +40 -12
- package/dist/ufg/get-ufg-client.js +13 -4
- package/dist/ufg/open-eyes.js +33 -57
- package/dist/ufg/take-snapshots.js +92 -0
- package/dist/ufg/utils/extract-default-environment.js +22 -0
- package/dist/ufg/utils/generate-safe-selectors.js +9 -32
- package/dist/ufg/utils/take-dom-snapshot.js +61 -26
- package/dist/ufg/utils/take-dom-snapshots.js +99 -68
- package/dist/ufg/utils/to-generated-selectors.js +67 -0
- package/dist/ufg/utils/to-safe-check-settings.js +69 -0
- package/dist/universal/core-server.js +53 -22
- package/dist/universal/core.js +23 -6
- package/dist/universal/history.js +9 -0
- package/dist/universal/spec-driver.js +46 -50
- package/dist/{ufg/get-nml-client.js → utils/ensure-offline-folder.js} +14 -13
- package/dist/utils/extract-branching-timestamp.js +56 -0
- package/dist/utils/extract-current-commit.js +72 -0
- package/dist/utils/extract-git-info.js +168 -0
- package/dist/utils/extract-start-info.js +100 -0
- package/dist/utils/extract-test-environment.js +70 -0
- package/dist/utils/memory-usage-logging.js +46 -0
- package/dist/utils/populate-eyes-server-settings.js +41 -0
- package/dist/utils/validate-sdk-version.js +89 -0
- package/package.json +3 -3
- package/types/abort.d.ts +13 -0
- package/types/automation/abort.d.ts +16 -0
- package/types/automation/close.d.ts +14 -0
- package/types/automation/extract-text.d.ts +1 -1
- package/types/automation/get-nml-client.d.ts +16 -0
- package/types/automation/get-results.d.ts +11 -0
- package/types/automation/get-viewport-size.d.ts +1 -1
- package/types/automation/locate-text.d.ts +1 -1
- package/types/automation/locate.d.ts +1 -1
- package/types/automation/set-viewport-size.d.ts +1 -1
- package/types/automation/types.d.ts +107 -16
- package/types/automation/utils/report-kobiton.d.ts +12 -0
- package/types/automation/utils/take-screenshot.d.ts +5 -2
- package/types/automation/utils/to-base-check-settings.d.ts +13 -3
- package/types/automation/utils/to-environment-key.d.ts +2 -0
- package/types/automation/utils/uniquify-environments.d.ts +2 -0
- package/types/automation/utils/wait-for-lazy-load.d.ts +4 -8
- package/types/autonomous/core.d.ts +19 -0
- package/types/autonomous/create-render-results.d.ts +10 -0
- package/types/autonomous/create-render-target.d.ts +12 -0
- package/types/autonomous/get-render-results.d.ts +12 -0
- package/types/autonomous/open-eyes.d.ts +13 -0
- package/types/autonomous/start-renders.d.ts +12 -0
- package/types/autonomous/take-snapshots.d.ts +15 -0
- package/types/autonomous/types.d.ts +57 -0
- package/types/check-and-close.d.ts +3 -3
- package/types/check.d.ts +3 -3
- package/types/classic/check-and-close.d.ts +12 -5
- package/types/classic/check.d.ts +6 -3
- package/types/classic/core.d.ts +8 -2
- package/types/classic/get-base-eyes.d.ts +4 -8
- package/types/classic/open-eyes.d.ts +3 -4
- package/types/classic/types.d.ts +4 -5
- package/types/classic/utils/extract-default-environments.d.ts +9 -0
- package/types/classic/utils/take-dom-capture.d.ts +8 -0
- package/types/classic/utils/take-screenshots.d.ts +18 -0
- package/types/close-batch.d.ts +1 -1
- package/types/close.d.ts +3 -2
- package/types/core.d.ts +11 -2
- package/types/delete-test.d.ts +1 -1
- package/types/extract-text.d.ts +1 -1
- package/types/get-account-info.d.ts +11 -0
- package/types/get-ec-client.d.ts +1 -1
- package/types/get-eyes-results.d.ts +1 -1
- package/types/get-manager-results.d.ts +1 -1
- package/types/get-typed-eyes.d.ts +2 -4
- package/types/index.d.ts +2 -1
- package/types/lang.d.ts +32 -0
- package/types/locate-text.d.ts +1 -1
- package/types/locate.d.ts +1 -1
- package/types/make-manager.d.ts +11 -2
- package/types/offline/merge-configs.d.ts +1 -0
- package/types/offline/run-offline-snapshots.d.ts +11 -0
- package/types/open-eyes.d.ts +11 -2
- package/types/run-offline-snapshots.d.ts +6 -0
- package/types/troubleshoot/logs.d.ts +25 -0
- package/types/types.d.ts +19 -11
- package/types/ufg/check-and-close.d.ts +18 -10
- package/types/ufg/check.d.ts +5 -4
- package/types/ufg/core.d.ts +8 -5
- package/types/ufg/create-render-target-from-snapshot.d.ts +8 -0
- package/types/ufg/get-base-eyes.d.ts +4 -9
- package/types/ufg/get-ufg-client.d.ts +6 -3
- package/types/ufg/open-eyes.d.ts +3 -4
- package/types/ufg/take-snapshots.d.ts +17 -0
- package/types/ufg/types.d.ts +39 -39
- package/types/ufg/utils/extract-default-environment.d.ts +5 -0
- package/types/ufg/utils/generate-safe-selectors.d.ts +3 -12
- package/types/ufg/utils/take-dom-snapshot.d.ts +16 -21
- package/types/ufg/utils/take-dom-snapshots.d.ts +14 -10
- package/types/ufg/utils/to-generated-selectors.d.ts +34 -0
- package/types/ufg/utils/to-safe-check-settings.d.ts +22 -0
- package/types/universal/core-server.d.ts +6 -2
- package/types/universal/core.d.ts +6 -2
- package/types/universal/spec-driver.d.ts +6 -5
- package/types/universal/types.d.ts +59 -51
- package/types/utils/ensure-offline-folder.d.ts +1 -0
- package/types/utils/extract-branching-timestamp.d.ts +16 -0
- package/types/utils/extract-current-commit.d.ts +24 -0
- package/types/utils/extract-git-info.d.ts +59 -0
- package/types/utils/extract-start-info.d.ts +29 -0
- package/types/utils/extract-test-environment.d.ts +2 -0
- package/types/utils/memory-usage-logging.d.ts +7 -0
- package/types/utils/populate-eyes-server-settings.d.ts +2 -0
- package/types/utils/validate-sdk-version.d.ts +42 -0
- package/CHANGELOG.md +0 -222
- package/dist/classic/abort.js +0 -22
- package/dist/classic/close.js +0 -22
- package/dist/ufg/abort.js +0 -32
- package/dist/ufg/close.js +0 -37
- package/dist/ufg/get-results.js +0 -27
- package/dist/ufg/utils/take-vhses.js +0 -153
- package/dist/ufg/utils/uniquify-renderers.js +0 -27
- package/dist/utils/extract-ci-provider.js +0 -31
- package/types/classic/abort.d.ts +0 -16
- package/types/classic/close.d.ts +0 -16
- package/types/ufg/abort.d.ts +0 -21
- package/types/ufg/close.d.ts +0 -19
- package/types/ufg/get-nml-client.d.ts +0 -17
- package/types/ufg/get-results.d.ts +0 -16
- package/types/ufg/utils/take-vhses.d.ts +0 -17
- package/types/ufg/utils/uniquify-renderers.d.ts +0 -2
- package/types/utils/extract-ci-provider.d.ts +0 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.runOfflineSnapshots = void 0;
|
|
30
|
+
const fs_1 = __importDefault(require("fs"));
|
|
31
|
+
const path_1 = __importDefault(require("path"));
|
|
32
|
+
const core_1 = require("./core");
|
|
33
|
+
const get_ufg_client_1 = require("./ufg/get-ufg-client");
|
|
34
|
+
const logger_1 = require("@applitools/logger");
|
|
35
|
+
const to_base_check_settings_1 = require("./automation/utils/to-base-check-settings");
|
|
36
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
37
|
+
const utils = __importStar(require("@applitools/utils"));
|
|
38
|
+
async function runOfflineSnapshots(options) {
|
|
39
|
+
if (!options.offlineLocationPath)
|
|
40
|
+
throw new Error('offlineLocationPath is required');
|
|
41
|
+
const startTime = Date.now();
|
|
42
|
+
const logger = (0, logger_1.makeLogger)({ format: { label: 'offline-exec' } });
|
|
43
|
+
const core = (0, core_1.makeCore)();
|
|
44
|
+
const account = await core.getAccountInfo({ settings: options });
|
|
45
|
+
const ufgClient = await (0, get_ufg_client_1.makeGetUFGClient)({ logger })({
|
|
46
|
+
settings: { ...account.eyesServer, ...account.ufgServer },
|
|
47
|
+
});
|
|
48
|
+
const executionFolders = (await fs_1.default.promises.readdir(options.offlineLocationPath)).filter(filename => /execution\-\d+/.test(filename));
|
|
49
|
+
if (executionFolders.length === 1) {
|
|
50
|
+
logger.console.log(`Running execution ${executionFolders[0]} from folder ${path_1.default.resolve(options.offlineLocationPath)}`);
|
|
51
|
+
}
|
|
52
|
+
else if (executionFolders.length === 0) {
|
|
53
|
+
logger.console.log('No execution artifacts were found at', options.offlineLocationPath);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
logger.console.log(`Running the following executions from folder ${options.offlineLocationPath} :\n ${executionFolders.join('\n ')}`);
|
|
57
|
+
}
|
|
58
|
+
if (executionFolders.length === 0) {
|
|
59
|
+
throw new Error(`Unable to find offline executions in ${options.offlineLocationPath}`);
|
|
60
|
+
}
|
|
61
|
+
const allTestResults = (await Promise.all(executionFolders.map(runExecution))).flat();
|
|
62
|
+
const { isSuccess, outputStr } = processResults({ testResults: allTestResults, totalTime: Date.now() - startTime });
|
|
63
|
+
if (!isSuccess && options.failOnDiff) {
|
|
64
|
+
throw new Error(outputStr);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
logger.console.log(outputStr);
|
|
68
|
+
}
|
|
69
|
+
async function runExecution(executionName) {
|
|
70
|
+
const executionLogger = logger.extend({ tags: [executionName] });
|
|
71
|
+
const executionPath = path_1.default.resolve(options.offlineLocationPath, executionName);
|
|
72
|
+
executionLogger.log('handling execution', executionPath);
|
|
73
|
+
const testFolders = (await fs_1.default.promises.readdir(executionPath)).filter(foldername => foldername.startsWith('test-'));
|
|
74
|
+
executionLogger.log('running tests', testFolders);
|
|
75
|
+
const results = await Promise.all(testFolders.map(async (testFolder) => {
|
|
76
|
+
const testLogger = logger.extend({ tags: [testFolder] });
|
|
77
|
+
const testPath = path_1.default.resolve(executionPath, testFolder);
|
|
78
|
+
const openSettings = (await fs_1.default.promises
|
|
79
|
+
.readFile(path_1.default.resolve(testPath, 'settings.json'), 'utf-8')
|
|
80
|
+
.then(JSON.parse));
|
|
81
|
+
logger.console.log(`Running test: ${openSettings.testName} (${formatEnvironment(openSettings.environment)})`);
|
|
82
|
+
return runTest(executionPath, testPath, openSettings, testLogger);
|
|
83
|
+
}));
|
|
84
|
+
const batchIds = [...new Set(results.map(t => t.batchId))];
|
|
85
|
+
const allTestResults = results.map(t => t.results);
|
|
86
|
+
executionLogger.log('done running all tests', allTestResults);
|
|
87
|
+
await core.closeBatch({ settings: batchIds.map(batchId => ({ batchId, ...account.eyesServer })) });
|
|
88
|
+
executionLogger.log('done execution', executionName);
|
|
89
|
+
return allTestResults;
|
|
90
|
+
}
|
|
91
|
+
async function runTest(executionPath, testPath, openSettings, logger) {
|
|
92
|
+
const environment = await ufgClient.getActualEnvironment({
|
|
93
|
+
settings: { environment: openSettings.environment.requested },
|
|
94
|
+
});
|
|
95
|
+
const eyes = await core.base.openEyes({
|
|
96
|
+
settings: {
|
|
97
|
+
...openSettings,
|
|
98
|
+
environment: {
|
|
99
|
+
...openSettings.environment,
|
|
100
|
+
...environment,
|
|
101
|
+
},
|
|
102
|
+
...account.eyesServer,
|
|
103
|
+
},
|
|
104
|
+
logger,
|
|
105
|
+
});
|
|
106
|
+
const checkFolders = (await fs_1.default.promises.readdir(testPath)).filter(folderpath => folderpath.startsWith('check-'));
|
|
107
|
+
logger.log('running checks for test', testPath, ':', checkFolders);
|
|
108
|
+
const targets = (await Promise.all(checkFolders.map(checkFolder => fs_1.default.promises.readFile(path_1.default.resolve(testPath, checkFolder, 'snapshot.json'), 'utf-8').then(JSON.parse))));
|
|
109
|
+
await uploadResources(executionPath, targets, logger);
|
|
110
|
+
// logger.log('resource hashes for test', testFolder, ':', resourceHashes)
|
|
111
|
+
logger.log('uploaded resources for test', testPath);
|
|
112
|
+
await Promise.all(targets.map((target, index) => runCheck(eyes, target, index, logger)));
|
|
113
|
+
await eyes.close({
|
|
114
|
+
settings: await fs_1.default.promises.readFile(path_1.default.resolve(testPath, 'close.json'), 'utf-8').then(JSON.parse),
|
|
115
|
+
});
|
|
116
|
+
logger.log('done running test', logger);
|
|
117
|
+
return { batchId: openSettings.batch.id, results: (await eyes.getResults({ logger }))[0] };
|
|
118
|
+
}
|
|
119
|
+
async function uploadResources(executionPath, targets, logger) {
|
|
120
|
+
const uploadLogger = logger.extend({ tags: ['upload-resources'] });
|
|
121
|
+
const promises = targets.map(async ({ target }) => {
|
|
122
|
+
let resourcePromises = Object.values(target.resources)
|
|
123
|
+
.filter(isHashedResource)
|
|
124
|
+
.map(async (resource) => {
|
|
125
|
+
const contentfuleResource = {
|
|
126
|
+
id: '',
|
|
127
|
+
url: '',
|
|
128
|
+
value: await fs_1.default.promises.readFile(path_1.default.resolve(executionPath, 'resources', resource.hash)),
|
|
129
|
+
contentType: resource.contentType,
|
|
130
|
+
hash: resource,
|
|
131
|
+
};
|
|
132
|
+
return ufgClient.uploadResource({ resource: contentfuleResource, logger: uploadLogger });
|
|
133
|
+
});
|
|
134
|
+
resourcePromises = resourcePromises.concat(ufgClient.uploadResource({
|
|
135
|
+
resource: {
|
|
136
|
+
id: '',
|
|
137
|
+
url: '',
|
|
138
|
+
value: await fs_1.default.promises.readFile(path_1.default.resolve(executionPath, 'resources', target.snapshot.hash)),
|
|
139
|
+
contentType: target.snapshot.contentType,
|
|
140
|
+
hash: target.snapshot,
|
|
141
|
+
},
|
|
142
|
+
logger: uploadLogger,
|
|
143
|
+
}));
|
|
144
|
+
return Promise.all(resourcePromises);
|
|
145
|
+
});
|
|
146
|
+
await Promise.all(promises);
|
|
147
|
+
function isHashedResource(resource) {
|
|
148
|
+
return 'hash' in resource;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function runCheck(eyes, target, index, logger) {
|
|
152
|
+
var _a;
|
|
153
|
+
const checkLogger = logger.extend({ tags: [`check-${index}`] });
|
|
154
|
+
const { elementReferences: selectors, getBaseCheckSettings } = (0, to_base_check_settings_1.toBaseCheckSettings)({
|
|
155
|
+
settings: target.settings,
|
|
156
|
+
});
|
|
157
|
+
const { renderId, selectorRegions, ...baseTarget } = await ufgClient.render({
|
|
158
|
+
target: target.target,
|
|
159
|
+
settings: {
|
|
160
|
+
...target.settings,
|
|
161
|
+
region: (_a = selectors.target) !== null && _a !== void 0 ? _a : target.settings.region,
|
|
162
|
+
scrollRootElement: selectors.scrolling,
|
|
163
|
+
selectorsToCalculate: selectors.calculate,
|
|
164
|
+
includeFullPageSize: Boolean(target.settings.pageId),
|
|
165
|
+
environment: target.settings.environment,
|
|
166
|
+
uploadUrl: account.uploadUrl,
|
|
167
|
+
stitchingServiceUrl: account.stitchingServiceUrl,
|
|
168
|
+
},
|
|
169
|
+
logger: checkLogger,
|
|
170
|
+
});
|
|
171
|
+
const baseSettings = getBaseCheckSettings({
|
|
172
|
+
calculatedRegions: selectors.calculate.map((_, index) => {
|
|
173
|
+
var _a;
|
|
174
|
+
return ({
|
|
175
|
+
regions: (_a = selectorRegions === null || selectorRegions === void 0 ? void 0 : selectorRegions[index]) !== null && _a !== void 0 ? _a : [],
|
|
176
|
+
});
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
baseSettings.renderId = renderId;
|
|
180
|
+
baseSettings.stepIndex = index;
|
|
181
|
+
baseTarget.source = target.target.source; // TODO verify
|
|
182
|
+
// baseTarget.name = snapshot.title // TODO figure out
|
|
183
|
+
await eyes.check({
|
|
184
|
+
target: { ...baseTarget, isTransformed: true },
|
|
185
|
+
settings: baseSettings,
|
|
186
|
+
logger: checkLogger,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.runOfflineSnapshots = runOfflineSnapshots;
|
|
191
|
+
function uniq(arr) {
|
|
192
|
+
return [...new Set(arr)];
|
|
193
|
+
}
|
|
194
|
+
function processResults({ testResults, totalTime, saveNewTests = true, failOnDiff = true, }) {
|
|
195
|
+
let outputStr = '\n';
|
|
196
|
+
const pluralize = utils.general.pluralize;
|
|
197
|
+
const testResultsWithErrors = testResults.filter(r => r && r.reason);
|
|
198
|
+
const unresolved = testResults.filter(r => r.isDifferent && !r.isAborted);
|
|
199
|
+
const passedOrNew = testResults.filter(r => r.status === 'Passed' || (r.isNew && !r.isAborted));
|
|
200
|
+
const aborted = testResults.filter(r => r.isAborted);
|
|
201
|
+
const newTests = testResults.filter(r => r.isNew && !r.isAborted);
|
|
202
|
+
const failedTests = testResults.filter(r => !r.reason && !r.isNew && !r.isAborted && !r.isDifferent && r.status === 'Failed');
|
|
203
|
+
const newTestsSize = newTests.length;
|
|
204
|
+
const warnForUnsavedNewTests = !!(!saveNewTests && newTestsSize);
|
|
205
|
+
const errMessagesToExclude = ['detected differences', 'Please approve the new baseline', 'is failed! See details at'];
|
|
206
|
+
const errors = testResultsWithErrors
|
|
207
|
+
.filter(r => !errMessagesToExclude.some(msg => { var _a, _b; return (_b = (_a = r.reason) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.includes(msg); }))
|
|
208
|
+
.map(r => ({
|
|
209
|
+
error: r.reason,
|
|
210
|
+
title: r.name,
|
|
211
|
+
}));
|
|
212
|
+
const hasResults = unresolved.length || passedOrNew.length || aborted.length;
|
|
213
|
+
const seeDetailsStr = hasResults && `See details at ${(passedOrNew[0] || unresolved[0] || aborted[0]).appUrls.batch}`;
|
|
214
|
+
if (hasResults) {
|
|
215
|
+
outputStr += `${seeDetailsStr}\n\n`;
|
|
216
|
+
}
|
|
217
|
+
outputStr += '[EYES: TEST RESULTS]:\n\n';
|
|
218
|
+
if (passedOrNew.length > 0) {
|
|
219
|
+
outputStr += testResultsOutput(passedOrNew, warnForUnsavedNewTests);
|
|
220
|
+
}
|
|
221
|
+
if (failedTests.length > 0) {
|
|
222
|
+
outputStr += testResultsOutput(failedTests, warnForUnsavedNewTests);
|
|
223
|
+
}
|
|
224
|
+
if (unresolved.length > 0) {
|
|
225
|
+
outputStr += testResultsOutput(unresolved, warnForUnsavedNewTests);
|
|
226
|
+
}
|
|
227
|
+
if (aborted.length > 0) {
|
|
228
|
+
outputStr += testResultsOutput(aborted, warnForUnsavedNewTests);
|
|
229
|
+
}
|
|
230
|
+
if (errors.length) {
|
|
231
|
+
const sortedErrors = errors.sort((a, b) => a.title.localeCompare(b.title));
|
|
232
|
+
outputStr += uniq(sortedErrors.map(({ title, error }) => `${title} - ${chalk_1.default.red('Failed')}. ${error.message || error.toString()}`)).join('\n');
|
|
233
|
+
outputStr += '\n';
|
|
234
|
+
}
|
|
235
|
+
if (!errors.length && !hasResults) {
|
|
236
|
+
outputStr += 'Test is finished but no results returned.\n';
|
|
237
|
+
}
|
|
238
|
+
const unresolvedOrFailed = unresolved.concat(failedTests);
|
|
239
|
+
if (errors.length && !unresolvedOrFailed.length) {
|
|
240
|
+
outputStr += chalk_1.default.red(`\nA total of ${errors.length} test${pluralize(errors.length)} failed for unexpected error${pluralize(errors.length)}.`);
|
|
241
|
+
}
|
|
242
|
+
else if (unresolvedOrFailed.length && !errors.length) {
|
|
243
|
+
outputStr += chalk_1.default.keyword(failedTests.length ? 'red' : 'orange')(`\nA total of ${unresolvedOrFailed.length} difference${pluralize(unresolvedOrFailed.length, [
|
|
244
|
+
's were',
|
|
245
|
+
' was',
|
|
246
|
+
])} found.`);
|
|
247
|
+
}
|
|
248
|
+
else if (unresolvedOrFailed.length || errors.length) {
|
|
249
|
+
outputStr += chalk_1.default.red(`\nA total of ${unresolvedOrFailed.length} difference${pluralize(unresolvedOrFailed.length, [
|
|
250
|
+
's were',
|
|
251
|
+
' was',
|
|
252
|
+
])} found and ${errors.length} test${pluralize(errors.length)} failed for ${pluralize(errors.length, [
|
|
253
|
+
'',
|
|
254
|
+
'an ',
|
|
255
|
+
])}unexpected error${pluralize(errors.length)}.`);
|
|
256
|
+
}
|
|
257
|
+
else if (warnForUnsavedNewTests) {
|
|
258
|
+
const countText = newTestsSize > 1 ? `are ${newTestsSize} new tests` : `is a new test: '${newTests[0].name}'`;
|
|
259
|
+
outputStr += chalk_1.default.red(`\n'saveNewTests' was set to false and there ${countText}. Please approve ${pluralize(newTestsSize, [
|
|
260
|
+
'their',
|
|
261
|
+
'its',
|
|
262
|
+
])} baseline${pluralize(newTestsSize)} in Eyes dashboard.\n`);
|
|
263
|
+
}
|
|
264
|
+
else if (passedOrNew.length) {
|
|
265
|
+
outputStr += chalk_1.default.green(`\nNo differences were found!`);
|
|
266
|
+
}
|
|
267
|
+
if (hasResults) {
|
|
268
|
+
outputStr += `\n${seeDetailsStr}\nTotal time: ${Math.round(totalTime / 1000)} seconds\n`;
|
|
269
|
+
}
|
|
270
|
+
// if (Number(testConcurrency) === 5) {
|
|
271
|
+
// outputStr += `\n${concurrencyMsg}\n`
|
|
272
|
+
// }
|
|
273
|
+
let isSuccess;
|
|
274
|
+
if (errors.length) {
|
|
275
|
+
isSuccess = false;
|
|
276
|
+
}
|
|
277
|
+
else if (failOnDiff) {
|
|
278
|
+
isSuccess = !warnForUnsavedNewTests && passedOrNew.length && !unresolvedOrFailed.length;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
isSuccess = true;
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
outputStr,
|
|
285
|
+
isSuccess,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function testResultsOutput(results, warnForUnsavedNewTests) {
|
|
289
|
+
let outputStr = '';
|
|
290
|
+
const sortedTestResults = results.sort((a, b) => a.name.localeCompare(b.name));
|
|
291
|
+
sortedTestResults.forEach(result => {
|
|
292
|
+
const testTitle = `${result.name} [${result.hostApp}] [${result.hostDisplaySize.width}x${result.hostDisplaySize.height}] - `;
|
|
293
|
+
if (result.isAborted) {
|
|
294
|
+
outputStr += `${testTitle}${chalk_1.default.keyword('red')(`Aborted`)}\n`;
|
|
295
|
+
}
|
|
296
|
+
else if (result.isNew) {
|
|
297
|
+
const newResColor = warnForUnsavedNewTests ? 'orange' : 'blue';
|
|
298
|
+
outputStr += `${testTitle}${chalk_1.default.keyword(newResColor)('New')}\n`;
|
|
299
|
+
}
|
|
300
|
+
else if (result.status === 'Passed') {
|
|
301
|
+
outputStr += `${testTitle}${chalk_1.default.green('Passed')}\n`;
|
|
302
|
+
}
|
|
303
|
+
else if (result.status === 'Failed') {
|
|
304
|
+
outputStr += `${testTitle}${chalk_1.default.keyword('red')('Failed')}\n`;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
outputStr += `${testTitle}${chalk_1.default.keyword('orange')(`Unresolved`)}\n`;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
outputStr += '\n';
|
|
311
|
+
return outputStr;
|
|
312
|
+
}
|
|
313
|
+
function formatEnvironment(environment) {
|
|
314
|
+
const env = environment.requested;
|
|
315
|
+
if (isChromeEmulation(env)) {
|
|
316
|
+
return `${env.chromeEmulationInfo.deviceName}${env.chromeEmulationInfo.screenOrientation ? ` [${env.chromeEmulationInfo.screenOrientation}]` : ''}`;
|
|
317
|
+
}
|
|
318
|
+
else if (isIOSDevice(env)) {
|
|
319
|
+
return `${env.iosDeviceInfo.deviceName}${env.iosDeviceInfo.screenOrientation ? ` [${env.iosDeviceInfo.screenOrientation}]` : ''}`;
|
|
320
|
+
}
|
|
321
|
+
else if (isAndroidDevice(env)) {
|
|
322
|
+
return `${env.androidDeviceInfo.deviceName}${env.androidDeviceInfo.screenOrientation ? ` [${env.androidDeviceInfo.screenOrientation}]` : ''}`;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
return `${env.name} [${env.width}x${env.height}]`;
|
|
326
|
+
}
|
|
327
|
+
function isChromeEmulation(env) {
|
|
328
|
+
return env.chromeEmulationInfo;
|
|
329
|
+
}
|
|
330
|
+
function isIOSDevice(env) {
|
|
331
|
+
return env.iosDeviceInfo;
|
|
332
|
+
}
|
|
333
|
+
function isAndroidDevice(env) {
|
|
334
|
+
return env.androidDeviceInfo;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -65,9 +65,11 @@ async function checkNetwork({ stream = process.stdout, eyes = eyes_1.default, uf
|
|
|
65
65
|
const proxyMsg = '\nYour proxy seems to be blocking requests to Applitools. Please make sure the following command succeeds:';
|
|
66
66
|
if (curlRenderErr) {
|
|
67
67
|
printErr(proxyMsg, '\n', eyes.cmd, '\n');
|
|
68
|
+
throw new Error('Your proxy seems to be blocking requests to Applitools.');
|
|
68
69
|
}
|
|
69
70
|
else if (curlVgErr) {
|
|
70
71
|
printErr(proxyMsg, '\n', await ufg.getCmd());
|
|
72
|
+
throw new Error('Your proxy seems to be blocking requests to Applitools.');
|
|
71
73
|
}
|
|
72
74
|
async function doTest(func, name) {
|
|
73
75
|
const delimiterLength = 30 - name.length;
|
|
@@ -111,4 +113,6 @@ async function checkNetwork({ stream = process.stdout, eyes = eyes_1.default, uf
|
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
exports.checkNetwork = checkNetwork;
|
|
114
|
-
|
|
116
|
+
if (require.main === module) {
|
|
117
|
+
void checkNetwork();
|
|
118
|
+
}
|
|
@@ -40,7 +40,7 @@ const validateRawAccountInfo = (res) => {
|
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
42
|
const validateAccountInfo = (res) => {
|
|
43
|
-
if (!res || !res.
|
|
43
|
+
if (!res || !res.ufgServer || !res.ufgServer.accessToken || !res.uploadUrl) {
|
|
44
44
|
throw new Error(`bad render info result ${JSON.stringify(res)}`);
|
|
45
45
|
}
|
|
46
46
|
};
|
|
@@ -58,8 +58,8 @@ exports.default = {
|
|
|
58
58
|
validateRawAccountInfo(data);
|
|
59
59
|
},
|
|
60
60
|
async testServer() {
|
|
61
|
-
const server = (0, core_base_1.
|
|
62
|
-
const result = await server.getAccountInfo({ settings: utils_1.config });
|
|
61
|
+
const server = (0, core_base_1.makeCoreRequestsWithCache)();
|
|
62
|
+
const result = await server.getAccountInfo({ settings: { eyesServerUrl: utils_1.config.serverUrl, ...utils_1.config } });
|
|
63
63
|
validateAccountInfo(result);
|
|
64
64
|
},
|
|
65
65
|
testHttps: async () => {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeLogs = exports.structureLogs = exports.stringifyLog = exports.parseLogs = void 0;
|
|
4
|
+
function parseLogs(logs) {
|
|
5
|
+
const regexp = /^(?<label>[^\s]+) (?:\((?<tags>[^\)]+)\) )?\| (?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)? (?:\[(?<level>[A-Z]+)\s*\])? (?<message>.+)$/;
|
|
6
|
+
const lines = logs.split('\n');
|
|
7
|
+
return lines.reduce((logs, line) => {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
const match = line.match(regexp);
|
|
10
|
+
if (match) {
|
|
11
|
+
logs.push({
|
|
12
|
+
...match.groups,
|
|
13
|
+
tags: (_b = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.tags) === null || _b === void 0 ? void 0 : _b.split(' & ').map(tags => tags.split('/')),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
else if (logs[logs.length - 1]) {
|
|
17
|
+
logs[logs.length - 1].message += line;
|
|
18
|
+
}
|
|
19
|
+
return logs;
|
|
20
|
+
}, []);
|
|
21
|
+
}
|
|
22
|
+
exports.parseLogs = parseLogs;
|
|
23
|
+
function stringifyLog(log) {
|
|
24
|
+
var _a;
|
|
25
|
+
// eslint-disable-next-line
|
|
26
|
+
return `${log.label} (${(_a = log.tags) === null || _a === void 0 ? void 0 : _a.map(tags => tags.join('/')).join(' & ')}) ${log.timestamp} [${log.level}] ${log.message}`;
|
|
27
|
+
}
|
|
28
|
+
exports.stringifyLog = stringifyLog;
|
|
29
|
+
function structureLogs(logs) {
|
|
30
|
+
const groups = {};
|
|
31
|
+
groups.logs;
|
|
32
|
+
logs.forEach(log => {
|
|
33
|
+
var _a;
|
|
34
|
+
const tags = (_a = log.tags) !== null && _a !== void 0 ? _a : [[]];
|
|
35
|
+
tags.forEach(tags => {
|
|
36
|
+
var _a;
|
|
37
|
+
const group = tags.reduce((object, key) => {
|
|
38
|
+
var _a;
|
|
39
|
+
(_a = object[key]) !== null && _a !== void 0 ? _a : (object[key] = {});
|
|
40
|
+
return object[key];
|
|
41
|
+
}, groups);
|
|
42
|
+
(_a = group.logs) !== null && _a !== void 0 ? _a : (group.logs = []);
|
|
43
|
+
group.logs.push(log);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return groups;
|
|
47
|
+
}
|
|
48
|
+
exports.structureLogs = structureLogs;
|
|
49
|
+
function analyzeLogs({ logs, ...groups }) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
const analysis = {};
|
|
52
|
+
analysis.logs = logs === null || logs === void 0 ? void 0 : logs.map(stringifyLog);
|
|
53
|
+
const groupAnalyses = Object.fromEntries(Object.entries(groups).map(([name, group]) => [name, analyzeLogs(group)]));
|
|
54
|
+
analysis.startedAt = Math.min(Date.parse((_a = logs === null || logs === void 0 ? void 0 : logs[0]) === null || _a === void 0 ? void 0 : _a.timestamp) || Infinity, ...Object.values(groupAnalyses).map(group => group.startedAt));
|
|
55
|
+
analysis.finishedAt = Math.max(Date.parse((_b = logs === null || logs === void 0 ? void 0 : logs[logs.length - 1]) === null || _b === void 0 ? void 0 : _b.timestamp) || 0, ...Object.values(groupAnalyses).map(group => group.finishedAt));
|
|
56
|
+
analysis.time = analysis.finishedAt - analysis.startedAt;
|
|
57
|
+
analysis.conclusion = '';
|
|
58
|
+
analysis.conclusion += `Time taken: ${formatTime(analysis.time)};`;
|
|
59
|
+
if (Object.keys(groupAnalyses).length > 0) {
|
|
60
|
+
analysis.conclusion += `Longest group: ${Object.keys(groupAnalyses).reduce((prev, cur) => groupAnalyses[cur].time > groupAnalyses[prev].time ? cur : prev)}`;
|
|
61
|
+
}
|
|
62
|
+
return { ...analysis, ...groupAnalyses };
|
|
63
|
+
}
|
|
64
|
+
exports.analyzeLogs = analyzeLogs;
|
|
65
|
+
function formatTime(ms) {
|
|
66
|
+
const h = Math.floor(ms / 3600000);
|
|
67
|
+
if (h)
|
|
68
|
+
ms -= h * 3600000;
|
|
69
|
+
const m = Math.floor(ms / 60000);
|
|
70
|
+
if (m)
|
|
71
|
+
ms -= m * 60000;
|
|
72
|
+
const s = Math.floor(ms / 1000);
|
|
73
|
+
if (s)
|
|
74
|
+
ms -= s * 1000;
|
|
75
|
+
return [h && `${h}h`, m && `${m}m`, s && `${s}s`, ms && `${ms}ms`].filter(Boolean).join(' ');
|
|
76
|
+
}
|
package/dist/troubleshoot/ufg.js
CHANGED
|
@@ -35,27 +35,34 @@ const utils_1 = require("./utils");
|
|
|
35
35
|
const eyes_1 = require("./eyes");
|
|
36
36
|
const utils = __importStar(require("@applitools/utils"));
|
|
37
37
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
38
|
-
const
|
|
39
|
-
const
|
|
38
|
+
const rawValue = JSON.stringify({ resources: {}, domNodes: [] });
|
|
39
|
+
const encodedValue = new TextEncoder().encode(rawValue);
|
|
40
|
+
const hash = (0, crypto_1.createHash)('sha256').update(encodedValue).digest('hex');
|
|
40
41
|
const contentType = 'x-applitools-html/cdt';
|
|
41
42
|
const resource = {
|
|
42
43
|
id: 'id',
|
|
43
44
|
url: 'https://localhost:2107',
|
|
44
|
-
value,
|
|
45
|
+
value: encodedValue,
|
|
46
|
+
rawValue: rawValue,
|
|
45
47
|
hash: { hashFormat: 'sha256', hash, contentType },
|
|
46
48
|
contentType,
|
|
47
49
|
};
|
|
48
50
|
const UFG_PUT_RESOURCE_URL = `https://render-wus.applitools.com/sha256/${resource.hash.hash}?render-id=fake`;
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
const getAccessToken = (() => {
|
|
52
|
+
let accessToken;
|
|
53
|
+
return async () => {
|
|
54
|
+
if (!accessToken) {
|
|
55
|
+
const { stdout } = await utils.process.execute(`curl -s ${eyes_1.RENDER_INFO_URL} ${(0, utils_1.getProxyCurlArg)()}`, {
|
|
56
|
+
maxBuffer: 10000000,
|
|
57
|
+
});
|
|
58
|
+
accessToken = JSON.parse(stdout).accessToken;
|
|
59
|
+
if (!accessToken)
|
|
60
|
+
throw new Error('could not receive auth token since cURL command to get it failed.');
|
|
61
|
+
}
|
|
62
|
+
return accessToken;
|
|
63
|
+
};
|
|
64
|
+
})();
|
|
65
|
+
const getCmd = async () => `curl -X PUT -H "Content-Type: application/json" -H "X-Auth-Token: ${await getAccessToken()}" -d '${resource.rawValue}' ${UFG_PUT_RESOURCE_URL} ${(0, utils_1.getProxyCurlArg)()}`;
|
|
59
66
|
exports.getCmd = getCmd;
|
|
60
67
|
const validateVgResult = (res, sha) => {
|
|
61
68
|
if (!res || res.hash !== sha) {
|
|
@@ -68,7 +75,7 @@ exports.default = {
|
|
|
68
75
|
method: 'PUT',
|
|
69
76
|
headers: {
|
|
70
77
|
'Content-Type': 'x-applitools-html/cdt',
|
|
71
|
-
'X-Auth-Token': await
|
|
78
|
+
'X-Auth-Token': await getAccessToken(),
|
|
72
79
|
},
|
|
73
80
|
body: resource.value,
|
|
74
81
|
});
|
|
@@ -91,7 +98,7 @@ exports.default = {
|
|
|
91
98
|
testServer: async () => {
|
|
92
99
|
const url = new URL(UFG_PUT_RESOURCE_URL);
|
|
93
100
|
const requests = (0, ufg_client_1.makeUFGRequests)({
|
|
94
|
-
|
|
101
|
+
settings: { ufgServerUrl: url.origin, accessToken: await getAccessToken() },
|
|
95
102
|
logger: (0, logger_1.makeLogger)(),
|
|
96
103
|
});
|
|
97
104
|
await requests.uploadResource({ resource });
|
|
@@ -105,7 +112,7 @@ exports.default = {
|
|
|
105
112
|
method: 'PUT',
|
|
106
113
|
headers: {
|
|
107
114
|
'Content-Type': 'x-applitools-html/cdt',
|
|
108
|
-
'X-Auth-Token': await
|
|
115
|
+
'X-Auth-Token': await getAccessToken(),
|
|
109
116
|
},
|
|
110
117
|
});
|
|
111
118
|
request.on('response', response => {
|