@applitools/core 1.0.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/LICENSE +26 -0
- package/dist/automation/get-viewport-size.js +12 -0
- package/dist/automation/locate.js +22 -0
- package/dist/automation/set-viewport-size.js +12 -0
- package/dist/automation/utils/take-screenshot.js +55 -0
- package/dist/check-and-close.js +11 -0
- package/dist/check.js +55 -0
- package/dist/classic/check-and-close.js +67 -0
- package/dist/classic/check.js +78 -0
- package/dist/classic/core.js +50 -0
- package/dist/classic/extract-text.js +71 -0
- package/dist/classic/locate-text.js +53 -0
- package/dist/classic/open-eyes.js +78 -0
- package/dist/classic/utils/take-dom-capture.js +135 -0
- package/dist/classic/utils/transform-check-settings.js +68 -0
- package/dist/close-manager.js +62 -0
- package/dist/close.js +20 -0
- package/dist/core.js +51 -0
- package/dist/errors/test-error.js +29 -0
- package/dist/extract-text.js +37 -0
- package/dist/index.js +32 -0
- package/dist/locate-text.js +11 -0
- package/dist/locate.js +11 -0
- package/dist/make-manager.js +65 -0
- package/dist/open-eyes.js +84 -0
- package/dist/troubleshoot/check-network.js +106 -0
- package/dist/troubleshoot/eyes.js +85 -0
- package/dist/troubleshoot/ufg.js +130 -0
- package/dist/troubleshoot/utils.js +68 -0
- package/dist/ufg/abort.js +18 -0
- package/dist/ufg/check-and-close.js +7 -0
- package/dist/ufg/check.js +192 -0
- package/dist/ufg/close.js +31 -0
- package/dist/ufg/core.js +78 -0
- package/dist/ufg/open-eyes.js +123 -0
- package/dist/ufg/utils/generate-safe-selectors.js +60 -0
- package/dist/ufg/utils/take-dom-snapshot.js +132 -0
- package/dist/ufg/utils/take-dom-snapshots.js +141 -0
- package/dist/ufg/utils/take-snapshots.js +38 -0
- package/dist/ufg/utils/take-vhses.js +258 -0
- package/dist/utils/execute-poll-script.js +75 -0
- package/dist/utils/extract-broker-url.js +19 -0
- package/dist/utils/format-results.js +185 -0
- package/dist/utils/to-base-check-settings.js +69 -0
- package/dist/utils/wait-for-lazy-load.js +25 -0
- package/package.json +105 -0
- package/types/automation/get-viewport-size.d.ts +11 -0
- package/types/automation/locate.d.ts +16 -0
- package/types/automation/set-viewport-size.d.ts +12 -0
- package/types/automation/utils/take-screenshot.d.ts +15 -0
- package/types/check-and-close.d.ts +15 -0
- package/types/check.d.ts +15 -0
- package/types/classic/check-and-close.d.ts +16 -0
- package/types/classic/check.d.ts +16 -0
- package/types/classic/core.d.ts +13 -0
- package/types/classic/extract-text.d.ts +16 -0
- package/types/classic/locate-text.d.ts +16 -0
- package/types/classic/open-eyes.d.ts +15 -0
- package/types/classic/utils/take-dom-capture.d.ts +13 -0
- package/types/classic/utils/transform-check-settings.d.ts +11 -0
- package/types/close-manager.d.ts +18 -0
- package/types/close.d.ts +14 -0
- package/types/core.d.ts +14 -0
- package/types/errors/test-error.d.ts +5 -0
- package/types/extract-text.d.ts +15 -0
- package/types/index.d.ts +3 -0
- package/types/locate-text.d.ts +15 -0
- package/types/locate.d.ts +15 -0
- package/types/make-manager.d.ts +19 -0
- package/types/open-eyes.d.ts +19 -0
- package/types/troubleshoot/check-network.d.ts +20 -0
- package/types/troubleshoot/eyes.d.ts +11 -0
- package/types/troubleshoot/ufg.d.ts +10 -0
- package/types/troubleshoot/utils.d.ts +4 -0
- package/types/ufg/abort.d.ts +17 -0
- package/types/ufg/check-and-close.d.ts +17 -0
- package/types/ufg/check.d.ts +27 -0
- package/types/ufg/close.d.ts +16 -0
- package/types/ufg/core.d.ts +16 -0
- package/types/ufg/open-eyes.d.ts +18 -0
- package/types/ufg/utils/generate-safe-selectors.d.ts +12 -0
- package/types/ufg/utils/take-dom-snapshot.d.ts +23 -0
- package/types/ufg/utils/take-dom-snapshots.d.ts +24 -0
- package/types/ufg/utils/take-snapshots.d.ts +21 -0
- package/types/ufg/utils/take-vhses.d.ts +17 -0
- package/types/utils/execute-poll-script.d.ts +20 -0
- package/types/utils/extract-broker-url.d.ts +2 -0
- package/types/utils/format-results.d.ts +16 -0
- package/types/utils/to-base-check-settings.d.ts +17 -0
- package/types/utils/wait-for-lazy-load.d.ts +13 -0
|
@@ -0,0 +1,258 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.takeVHSes = void 0;
|
|
27
|
+
const utils = __importStar(require("@applitools/utils"));
|
|
28
|
+
async function takeVHSes({ driver, settings, hooks, logger, }) {
|
|
29
|
+
var _a;
|
|
30
|
+
logger.log('taking VHS');
|
|
31
|
+
if (!driver.isAndroid && !driver.isIOS) {
|
|
32
|
+
throwError('cannot take VHS on mobile device other than iOS or Android');
|
|
33
|
+
}
|
|
34
|
+
await ((_a = hooks === null || hooks === void 0 ? void 0 : hooks.beforeSnapshots) === null || _a === void 0 ? void 0 : _a.call(hooks));
|
|
35
|
+
const trigger = await findElement({
|
|
36
|
+
driver,
|
|
37
|
+
selector: 'UFG_TriggerArea',
|
|
38
|
+
type: 'Button',
|
|
39
|
+
timeout: 30000,
|
|
40
|
+
});
|
|
41
|
+
if (driver.isAndroid) {
|
|
42
|
+
const apiKeyInput = await findElement({
|
|
43
|
+
driver,
|
|
44
|
+
selector: 'UFG_Apikey',
|
|
45
|
+
type: 'EditText',
|
|
46
|
+
throwErr: false,
|
|
47
|
+
});
|
|
48
|
+
if (apiKeyInput) {
|
|
49
|
+
// in case 'apiKeyInput' does not exist, it means it was already triggered on previous cycle
|
|
50
|
+
// this condition is to avoid re-sending 'inputJson' multiple times
|
|
51
|
+
const inputString = JSON.stringify({
|
|
52
|
+
serverUrl: settings.serverUrl || undefined,
|
|
53
|
+
apiKey: settings.apiKey,
|
|
54
|
+
proxy: settings.proxy ? transformProxy(settings.proxy) : undefined,
|
|
55
|
+
});
|
|
56
|
+
log('sending API key to UFG lib', inputString);
|
|
57
|
+
await apiKeyInput.type(inputString);
|
|
58
|
+
const ready = await findElement({
|
|
59
|
+
driver,
|
|
60
|
+
selector: 'UFG_ApikeyReady',
|
|
61
|
+
type: 'Button',
|
|
62
|
+
});
|
|
63
|
+
await ready.click();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
log('UFG_Apikey was skipped');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
await trigger.click(); // TODO handle stale element exception and then find the trigger again and click it
|
|
70
|
+
let label = await findElement({
|
|
71
|
+
driver,
|
|
72
|
+
selector: 'UFG_SecondaryLabel',
|
|
73
|
+
type: 'TextView',
|
|
74
|
+
timeout: 10000,
|
|
75
|
+
throwErr: false,
|
|
76
|
+
});
|
|
77
|
+
if (!label) {
|
|
78
|
+
// This might happen if the tap on the trigger area didn't happen due to Appium bug. So we try to find the trigger again and if it's present, we'll tap it.
|
|
79
|
+
// If the trigger area is not present, then we're probably at the middle of taking the VHS - give it 50 seconds more until we give up
|
|
80
|
+
log('UFG_SecondaryLabel was not found after 10 seconds, trying to click UFG_TriggerArea again');
|
|
81
|
+
const triggerRetry = await findElement({
|
|
82
|
+
driver,
|
|
83
|
+
selector: 'UFG_TriggerArea',
|
|
84
|
+
type: 'Button',
|
|
85
|
+
timeout: 30000,
|
|
86
|
+
throwErr: false,
|
|
87
|
+
});
|
|
88
|
+
if (triggerRetry) {
|
|
89
|
+
log('UFG_TriggerArea was found on retry. clicking it.');
|
|
90
|
+
await triggerRetry.click();
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
log('UFG_TriggerArea was NOT found on retry. Probably VHS is being taken.');
|
|
94
|
+
}
|
|
95
|
+
label = await findElement({
|
|
96
|
+
driver,
|
|
97
|
+
selector: 'UFG_SecondaryLabel',
|
|
98
|
+
type: 'TextView',
|
|
99
|
+
timeout: 50000,
|
|
100
|
+
});
|
|
101
|
+
if (!label) {
|
|
102
|
+
log('UFG_SecondaryLabel was not found eventually. Giving up.');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const info = JSON.parse(await label.getText());
|
|
106
|
+
log('VHS info', info);
|
|
107
|
+
if (info.error) {
|
|
108
|
+
throwError(info.error);
|
|
109
|
+
}
|
|
110
|
+
let vhs;
|
|
111
|
+
if (driver.isIOS) {
|
|
112
|
+
vhs = await extractVHS();
|
|
113
|
+
}
|
|
114
|
+
else if (info.mode === 'labels') {
|
|
115
|
+
vhs = await collectChunkedVHS({ count: info.partsCount });
|
|
116
|
+
}
|
|
117
|
+
else if (info.mode === 'network') {
|
|
118
|
+
// do nothing
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throwError(`unknown mode for android: ${info.mode}`);
|
|
122
|
+
}
|
|
123
|
+
const clear = await findElement({
|
|
124
|
+
driver,
|
|
125
|
+
selector: 'UFG_ClearArea',
|
|
126
|
+
type: 'Button',
|
|
127
|
+
timeout: 30000,
|
|
128
|
+
});
|
|
129
|
+
await clear.click();
|
|
130
|
+
let snapshot;
|
|
131
|
+
if (driver.isAndroid) {
|
|
132
|
+
snapshot = {
|
|
133
|
+
platformName: 'android',
|
|
134
|
+
vhsType: info.flavorName,
|
|
135
|
+
vhsHash: {
|
|
136
|
+
hashFormat: 'sha256',
|
|
137
|
+
hash: info.vhsHash,
|
|
138
|
+
contentType: `x-applitools-vhs/${info.flavorName}`,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
snapshot = {
|
|
144
|
+
platformName: 'ios',
|
|
145
|
+
resourceContents: {
|
|
146
|
+
vhs: {
|
|
147
|
+
value: Buffer.from(vhs, 'base64'),
|
|
148
|
+
type: 'x-applitools-vhs/ios',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
vhsCompatibilityParams: {
|
|
152
|
+
UIKitLinkTimeVersionNumber: info.UIKitLinkTimeVersionNumber,
|
|
153
|
+
UIKitRunTimeVersionNumber: info.UIKitRunTimeVersionNumber,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return Array(settings.renderers.length).fill(snapshot);
|
|
158
|
+
async function extractVHS() {
|
|
159
|
+
const label = await findElement({
|
|
160
|
+
driver,
|
|
161
|
+
selector: 'UFG_Label',
|
|
162
|
+
type: 'TextView',
|
|
163
|
+
});
|
|
164
|
+
return await label.getText();
|
|
165
|
+
}
|
|
166
|
+
async function collectChunkedVHS({ count }) {
|
|
167
|
+
const labels = [
|
|
168
|
+
await findElement({
|
|
169
|
+
driver,
|
|
170
|
+
selector: 'UFG_Label_0',
|
|
171
|
+
type: 'TextView',
|
|
172
|
+
}),
|
|
173
|
+
await findElement({
|
|
174
|
+
driver,
|
|
175
|
+
selector: 'UFG_Label_1',
|
|
176
|
+
type: 'TextView',
|
|
177
|
+
}),
|
|
178
|
+
await findElement({
|
|
179
|
+
driver,
|
|
180
|
+
selector: 'UFG_Label_2',
|
|
181
|
+
type: 'TextView',
|
|
182
|
+
}),
|
|
183
|
+
];
|
|
184
|
+
let vhs = '';
|
|
185
|
+
for (let chunk = 0; chunk < count / labels.length; ++chunk) {
|
|
186
|
+
for (let label = 0; label < Math.min(labels.length, count - chunk * labels.length); ++label) {
|
|
187
|
+
vhs += await labels[label].getText();
|
|
188
|
+
}
|
|
189
|
+
if (chunk * labels.length < count) {
|
|
190
|
+
await trigger.click();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return vhs;
|
|
194
|
+
}
|
|
195
|
+
function log(...msg) {
|
|
196
|
+
logger.log('[takeVHSes]', ...msg);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.takeVHSes = takeVHSes;
|
|
200
|
+
function transformProxy(proxy) {
|
|
201
|
+
const url = new URL(proxy.url);
|
|
202
|
+
const transformedProxy = {
|
|
203
|
+
protocol: url.protocol,
|
|
204
|
+
host: url.hostname,
|
|
205
|
+
port: url.port,
|
|
206
|
+
};
|
|
207
|
+
if (proxy.username) {
|
|
208
|
+
transformedProxy.auth = { username: proxy.username, password: proxy.password };
|
|
209
|
+
}
|
|
210
|
+
else if (url.username) {
|
|
211
|
+
transformedProxy.auth = { username: url.username, password: proxy.password };
|
|
212
|
+
}
|
|
213
|
+
return transformedProxy;
|
|
214
|
+
}
|
|
215
|
+
function throwError(msg) {
|
|
216
|
+
throw new Error(`Error while taking VHS - ${msg}`);
|
|
217
|
+
}
|
|
218
|
+
async function findElement(options) {
|
|
219
|
+
const { selector, timeout = 0, throwErr = true } = options;
|
|
220
|
+
let element = await findElementBySelector(options);
|
|
221
|
+
if (!element && timeout)
|
|
222
|
+
element = await waitFor(options);
|
|
223
|
+
if (!element && throwErr) {
|
|
224
|
+
const timeoutError = timeout ? `, (waited for ${timeout} ms)` : '.';
|
|
225
|
+
throwError(`${selector} element could not be found${timeoutError}`);
|
|
226
|
+
}
|
|
227
|
+
return element;
|
|
228
|
+
async function waitFor(options) {
|
|
229
|
+
const { timeout } = options;
|
|
230
|
+
const interval = 500;
|
|
231
|
+
let waiting = true;
|
|
232
|
+
const waitTime = setTimeout(() => (waiting = false), timeout);
|
|
233
|
+
while (waiting) {
|
|
234
|
+
const element = await findElementBySelector(options);
|
|
235
|
+
if (element) {
|
|
236
|
+
clearTimeout(waitTime);
|
|
237
|
+
return element;
|
|
238
|
+
}
|
|
239
|
+
await utils.general.sleep(interval);
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
async function findElementBySelector(options) {
|
|
244
|
+
const { driver, selector, type } = options;
|
|
245
|
+
const context = driver.currentContext;
|
|
246
|
+
let element;
|
|
247
|
+
if (driver.isAndroid) {
|
|
248
|
+
element = await driver.element({
|
|
249
|
+
type: 'xpath',
|
|
250
|
+
selector: `//android.widget.${type}[@content-desc="${selector}"]`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
element = await context.element({ type: 'accessibility id', selector });
|
|
255
|
+
}
|
|
256
|
+
return element;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.executePollScript = void 0;
|
|
27
|
+
const core_base_1 = require("@applitools/core-base");
|
|
28
|
+
const utils = __importStar(require("@applitools/utils"));
|
|
29
|
+
async function executePollScript({ context, scripts, settings, logger, }) {
|
|
30
|
+
logger.log('Executing poll script');
|
|
31
|
+
let isExecutionTimedOut = false;
|
|
32
|
+
const executionTimer = setTimeout(() => (isExecutionTimedOut = true), settings.executionTimeout);
|
|
33
|
+
try {
|
|
34
|
+
const { script, args = [] } = scripts.main;
|
|
35
|
+
let response = deserialize(await context.execute(script, ...args));
|
|
36
|
+
let chunks = '';
|
|
37
|
+
while (!isExecutionTimedOut) {
|
|
38
|
+
if (response.status === 'ERROR') {
|
|
39
|
+
throw new core_base_1.CoreError(`Error during execute poll script: '${response.error}'`, {
|
|
40
|
+
reason: 'poll script',
|
|
41
|
+
error: response.error,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else if (response.status === 'SUCCESS') {
|
|
45
|
+
return response.value;
|
|
46
|
+
}
|
|
47
|
+
else if (response.status === 'SUCCESS_CHUNKED') {
|
|
48
|
+
chunks += response.value;
|
|
49
|
+
if (response.done)
|
|
50
|
+
return deserialize(chunks);
|
|
51
|
+
}
|
|
52
|
+
else if (response.status === 'WIP') {
|
|
53
|
+
await utils.general.sleep(settings.pollTimeout);
|
|
54
|
+
}
|
|
55
|
+
logger.log('Polling...');
|
|
56
|
+
const { script, args = [] } = scripts.poll;
|
|
57
|
+
response = deserialize(await context.execute(script, ...args));
|
|
58
|
+
}
|
|
59
|
+
throw new core_base_1.CoreError('Poll script execution is timed out', { reason: 'timeout' });
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
clearTimeout(executionTimer);
|
|
63
|
+
}
|
|
64
|
+
function deserialize(json) {
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(json);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const firstChars = json.slice(0, 100);
|
|
70
|
+
const lastChars = json.slice(-100);
|
|
71
|
+
throw new Error(`Response is not a valid JSON string. length: ${json.length}, first 100 chars: "${firstChars}", last 100 chars: "${lastChars}". error: ${err}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.executePollScript = executePollScript;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractBrokerUrl = void 0;
|
|
4
|
+
async function extractBrokerUrl(driver) {
|
|
5
|
+
if (!driver.isIOS)
|
|
6
|
+
return null;
|
|
7
|
+
try {
|
|
8
|
+
const element = await driver.element({
|
|
9
|
+
type: 'xpath',
|
|
10
|
+
selector: '//XCUIElementTypeOther[@name="Applitools_View"]',
|
|
11
|
+
});
|
|
12
|
+
const result = JSON.parse(await element.getText());
|
|
13
|
+
return result.nextPath;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.extractBrokerUrl = extractBrokerUrl;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toJsonOutput = exports.toXmlOutput = exports.toFlattenedTAPString = exports.toHierarchicTAPString = exports.toFormatterString = void 0;
|
|
4
|
+
const OK = 'ok';
|
|
5
|
+
const NOT_OK = 'not ok';
|
|
6
|
+
function toFormatterString(results, { includeSubTests = true, markNewAsPassed = false } = {}) {
|
|
7
|
+
if (results.length === 0) {
|
|
8
|
+
return 'No results found.';
|
|
9
|
+
}
|
|
10
|
+
let formattedString = '[EYES: TEST RESULTS]:\n';
|
|
11
|
+
for (let i = 0; i < results.length; i += 1) {
|
|
12
|
+
/** @type {TestResults} */ const currentResult = results[i];
|
|
13
|
+
const testTitle = `${currentResult.name} [${currentResult.hostDisplaySize.width}x${currentResult.hostDisplaySize.height}]`;
|
|
14
|
+
let testResult = '';
|
|
15
|
+
if (currentResult.isNew) {
|
|
16
|
+
testResult = markNewAsPassed ? 'Passed' : 'New';
|
|
17
|
+
}
|
|
18
|
+
else if (!currentResult.isDifferent) {
|
|
19
|
+
testResult = 'Passed';
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const stepsFailed = currentResult.mismatches + currentResult.missing;
|
|
23
|
+
testResult = `Failed ${stepsFailed} of ${currentResult.steps}`;
|
|
24
|
+
}
|
|
25
|
+
formattedString += `${testTitle} - ${testResult}\n`;
|
|
26
|
+
if (includeSubTests) {
|
|
27
|
+
if (currentResult.stepsInfo.length > 0) {
|
|
28
|
+
for (let j = 0; j < currentResult.stepsInfo.length; j += 1) {
|
|
29
|
+
const currentStep = currentResult.stepsInfo[j];
|
|
30
|
+
const subTestTitle = currentStep.name;
|
|
31
|
+
const subTestResult = currentStep.isDifferent ? 'Passed' : 'Failed';
|
|
32
|
+
formattedString += `\t> ${subTestTitle} - ${subTestResult}\n`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
formattedString += '\tNo steps exist for this test.\n';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
formattedString += `See details at ${results[0].appUrls.batch}`;
|
|
41
|
+
return formattedString;
|
|
42
|
+
}
|
|
43
|
+
exports.toFormatterString = toFormatterString;
|
|
44
|
+
function toHierarchicTAPString(results, { includeSubTests = true, markNewAsPassed = false } = {}) {
|
|
45
|
+
if (results.length === 0) {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
let tapString = `1..${results.length}\n`;
|
|
49
|
+
for (let i = 0; i < results.length; i += 1) {
|
|
50
|
+
/** @type {TestResults} */ const currentResult = results[i];
|
|
51
|
+
const tapIndex = i + 1;
|
|
52
|
+
if (i > 0) {
|
|
53
|
+
tapString += '#\n';
|
|
54
|
+
}
|
|
55
|
+
const name = `Test: '${currentResult.name}', Application: '${currentResult.appName}'`;
|
|
56
|
+
if (!currentResult.isDifferent) {
|
|
57
|
+
tapString += `${OK} ${tapIndex} - [PASSED TEST] ${name}\n`;
|
|
58
|
+
}
|
|
59
|
+
else if (currentResult.isNew) {
|
|
60
|
+
// Test did not pass (might also be a new test).
|
|
61
|
+
// New test
|
|
62
|
+
const newResult = markNewAsPassed ? OK : NOT_OK;
|
|
63
|
+
tapString += `${newResult} ${tapIndex} - [NEW TEST] ${name}\n`;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Failed / Aborted test.
|
|
67
|
+
tapString += `${NOT_OK} ${tapIndex} - `;
|
|
68
|
+
if (currentResult.isAborted) {
|
|
69
|
+
tapString += `[ABORTED TEST] ${name}\n`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
tapString += `[FAILED TEST] ${name}\n`;
|
|
73
|
+
}
|
|
74
|
+
tapString += `#\tMismatches: ${currentResult.mismatches}\n`;
|
|
75
|
+
}
|
|
76
|
+
const url = currentResult.appUrls && currentResult.appUrls.session ? currentResult.appUrls.session : "No URL (session didn't start).";
|
|
77
|
+
tapString += `#\tTest url: ${url}\n`;
|
|
78
|
+
tapString += `#\tBrowser: ${currentResult.hostApp}, Viewport: ${currentResult.hostDisplaySize}\n`;
|
|
79
|
+
if (includeSubTests) {
|
|
80
|
+
if (currentResult.stepsInfo.length > 0) {
|
|
81
|
+
tapString += `\t1..${currentResult.stepsInfo.length}\n`;
|
|
82
|
+
for (let j = 0; j < currentResult.stepsInfo.length; j += 1) {
|
|
83
|
+
const currentStep = currentResult.stepsInfo[j];
|
|
84
|
+
tapString += '\t';
|
|
85
|
+
tapString += currentStep.isDifferent ? NOT_OK : OK;
|
|
86
|
+
tapString += ` '${currentStep.name}', URL: ${currentStep.appUrls.step}\n`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
tapString += '\tNo steps exist for this test.\n';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return tapString;
|
|
95
|
+
}
|
|
96
|
+
exports.toHierarchicTAPString = toHierarchicTAPString;
|
|
97
|
+
function toFlattenedTAPString(results, { markNewAsPassed = false } = {}) {
|
|
98
|
+
let tapString = '';
|
|
99
|
+
let stepsCounter = 0;
|
|
100
|
+
// We'll add the TAP plan at the beginning, after we calculate the total number of steps.
|
|
101
|
+
for (let i = 0; i < results.length; i += 1) {
|
|
102
|
+
tapString += '#\n';
|
|
103
|
+
/** @type {TestResults} */ const currentResult = results[i];
|
|
104
|
+
const tapIndex = i + 1;
|
|
105
|
+
const name = `Test: '${currentResult.name}', Application: '${currentResult.appName}'`;
|
|
106
|
+
if (!currentResult.isDifferent) {
|
|
107
|
+
tapString += `# ${OK} ${tapIndex} - [PASSED TEST] ${name}\n`;
|
|
108
|
+
}
|
|
109
|
+
else if (currentResult.isNew) {
|
|
110
|
+
// Test did not pass (might also be a new test).
|
|
111
|
+
// New test
|
|
112
|
+
const newResult = markNewAsPassed ? OK : NOT_OK;
|
|
113
|
+
tapString += `# ${newResult} ${tapIndex} - [NEW TEST] ${name}\n`;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Failed / Aborted test.
|
|
117
|
+
tapString += `# ${NOT_OK} ${tapIndex} - `;
|
|
118
|
+
if (currentResult.isAborted) {
|
|
119
|
+
tapString += `[ABORTED TEST] ${name}\n`;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
tapString += `[FAILED TEST] ${name}\n`;
|
|
123
|
+
}
|
|
124
|
+
tapString += `#\tMismatches: ${currentResult.mismatches}\n`;
|
|
125
|
+
}
|
|
126
|
+
const url = currentResult.appUrls && currentResult.appUrls.session ? currentResult.appUrls.session : "No URL (session didn't start).";
|
|
127
|
+
tapString += `#\tTest url: ${url}\n`;
|
|
128
|
+
if (currentResult.stepsInfo.length > 0) {
|
|
129
|
+
for (let j = 0; j < currentResult.stepsInfo.length; j += 1) {
|
|
130
|
+
stepsCounter += 1;
|
|
131
|
+
const currentStep = currentResult.stepsInfo[j];
|
|
132
|
+
tapString += currentStep.isDifferent ? NOT_OK : OK;
|
|
133
|
+
tapString += ` ${stepsCounter} '${currentStep.name}', URL: ${currentStep.appUrls.step}\n`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
tapString += '#\tNo steps exist for this test.\n';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (stepsCounter > 0) {
|
|
141
|
+
tapString = `1..${stepsCounter}\n${tapString}`;
|
|
142
|
+
}
|
|
143
|
+
return tapString;
|
|
144
|
+
}
|
|
145
|
+
exports.toFlattenedTAPString = toFlattenedTAPString;
|
|
146
|
+
function toXmlOutput(results, { totalTime } = {}) {
|
|
147
|
+
const suiteName = 'Eyes Test Suite';
|
|
148
|
+
let output = `<?xml version="1.0" encoding="UTF-8" ?>`;
|
|
149
|
+
output += `\n<testsuite name="${suiteName}" tests="${results.length}" time="${totalTime}">`;
|
|
150
|
+
results.forEach(result => {
|
|
151
|
+
output += `\n<testcase name="${result.name}"${result.duration ? ` time="${result.duration}"` : ''}>`;
|
|
152
|
+
const properties = {};
|
|
153
|
+
if (result.hostOS)
|
|
154
|
+
properties.hostOS = result.hostOS;
|
|
155
|
+
if (result.hostApp)
|
|
156
|
+
properties.hostApp = result.hostApp;
|
|
157
|
+
if (result.hostDisplaySize)
|
|
158
|
+
properties.viewportSize = `${result.hostDisplaySize.width}x${result.hostDisplaySize.height}`;
|
|
159
|
+
if (properties.hostOS || properties.hostApp || properties.viewportSize) {
|
|
160
|
+
output += `\n<properties>`;
|
|
161
|
+
for (const [name, value] of Object.entries(properties)) {
|
|
162
|
+
output += `\n<property name="${name}" value="${value}"/>`;
|
|
163
|
+
}
|
|
164
|
+
output += `\n</properties>`;
|
|
165
|
+
}
|
|
166
|
+
if (result.isDifferent) {
|
|
167
|
+
output += `\n<failure>`;
|
|
168
|
+
output += `\nDifference found. See ${result.appUrls.batch} for details.`;
|
|
169
|
+
output += `\n</failure>`;
|
|
170
|
+
}
|
|
171
|
+
else if (result.isAborted) {
|
|
172
|
+
output += `\n<failure>`;
|
|
173
|
+
output += `\nTest aborted.`;
|
|
174
|
+
output += `\n</failure>`;
|
|
175
|
+
}
|
|
176
|
+
output += `\n</testcase>`;
|
|
177
|
+
});
|
|
178
|
+
output += `\n</testsuite>`;
|
|
179
|
+
return output;
|
|
180
|
+
}
|
|
181
|
+
exports.toXmlOutput = toXmlOutput;
|
|
182
|
+
function toJsonOutput(results, space) {
|
|
183
|
+
return JSON.stringify(results, null, space);
|
|
184
|
+
}
|
|
185
|
+
exports.toJsonOutput = toJsonOutput;
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.toBaseCheckSettings = void 0;
|
|
27
|
+
const utils = __importStar(require("@applitools/utils"));
|
|
28
|
+
function toBaseCheckSettings({ settings, }) {
|
|
29
|
+
const regionTypes = ['ignore', 'layout', 'strict', 'content', 'floating', 'accessibility'];
|
|
30
|
+
const elementReferencesToCalculate = regionTypes.flatMap(regionType => {
|
|
31
|
+
var _a;
|
|
32
|
+
return ((_a = settings[`${regionType}Regions`]) !== null && _a !== void 0 ? _a : []).reduce((regions, reference) => {
|
|
33
|
+
const { region } = utils.types.has(reference, 'region') ? reference : { region: reference };
|
|
34
|
+
return !isRegion(region) ? regions.concat(region) : regions;
|
|
35
|
+
}, []);
|
|
36
|
+
});
|
|
37
|
+
const elementReferenceToTarget = !isRegion(settings.region) ? settings.region : undefined;
|
|
38
|
+
return { elementReferencesToCalculate, elementReferenceToTarget, getBaseCheckSettings };
|
|
39
|
+
function getBaseCheckSettings({ calculatedRegions, preserveTransformation, }) {
|
|
40
|
+
const transformedSettings = { ...settings };
|
|
41
|
+
if (!preserveTransformation) {
|
|
42
|
+
delete transformedSettings.region;
|
|
43
|
+
delete transformedSettings.normalization;
|
|
44
|
+
}
|
|
45
|
+
else if (elementReferenceToTarget) {
|
|
46
|
+
delete transformedSettings.region;
|
|
47
|
+
}
|
|
48
|
+
regionTypes.forEach(regionType => {
|
|
49
|
+
if (!transformedSettings[`${regionType}Regions`])
|
|
50
|
+
return;
|
|
51
|
+
transformedSettings[`${regionType}Regions`] = transformedSettings[`${regionType}Regions`].flatMap(reference => {
|
|
52
|
+
const { region, ...options } = utils.types.has(reference, 'region') ? reference : { region: reference };
|
|
53
|
+
if (isRegion(region))
|
|
54
|
+
return reference;
|
|
55
|
+
const { selector, regions } = calculatedRegions.shift();
|
|
56
|
+
return regions.map(region => ({
|
|
57
|
+
region,
|
|
58
|
+
regionId: utils.types.isString(selector) ? selector : selector === null || selector === void 0 ? void 0 : selector.selector,
|
|
59
|
+
...options,
|
|
60
|
+
}));
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
return transformedSettings;
|
|
64
|
+
}
|
|
65
|
+
function isRegion(region) {
|
|
66
|
+
return utils.types.has(region, ['x', 'y', 'width', 'height']);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.toBaseCheckSettings = toBaseCheckSettings;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForLazyLoad = void 0;
|
|
4
|
+
const snippets_1 = require("@applitools/snippets");
|
|
5
|
+
const execute_poll_script_1 = require("./execute-poll-script");
|
|
6
|
+
async function waitForLazyLoad({ driver, settings, logger, }) {
|
|
7
|
+
var _a, _b, _c, _d, _e;
|
|
8
|
+
logger.log('lazy loading the page before capturing a screenshot');
|
|
9
|
+
const arg = {
|
|
10
|
+
scrollLength: (_a = settings.scrollLength) !== null && _a !== void 0 ? _a : 300,
|
|
11
|
+
waitingTime: (_b = settings.waitingTime) !== null && _b !== void 0 ? _b : 2000,
|
|
12
|
+
maxAmountToScroll: (_c = settings.maxAmountToScroll) !== null && _c !== void 0 ? _c : 15000,
|
|
13
|
+
};
|
|
14
|
+
const scripts = {
|
|
15
|
+
main: { script: snippets_1.lazyLoad, args: [[arg]] },
|
|
16
|
+
poll: { script: snippets_1.lazyLoad, args: [[]] },
|
|
17
|
+
};
|
|
18
|
+
await (0, execute_poll_script_1.executePollScript)({
|
|
19
|
+
context: driver.currentContext,
|
|
20
|
+
scripts,
|
|
21
|
+
settings: { executionTimeout: 5 * 60 * 1000, pollTimeout: (_e = (_d = settings.pollTimeout) !== null && _d !== void 0 ? _d : settings.waitingTime) !== null && _e !== void 0 ? _e : 2000 },
|
|
22
|
+
logger,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
exports.waitForLazyLoad = waitForLazyLoad;
|