@mablhq/mabl-cli 1.61.8 → 2.0.3
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/Globals.js +11 -7
- package/api/basicApiClient.js +10 -10
- package/api/mablApiClient.js +39 -1
- package/browserLauncher/playwrightBrowserLauncher/playwrightBrowser.js +6 -6
- package/browserLauncher/playwrightBrowserLauncher/playwrightPage.js +8 -0
- package/cli.js +1 -0
- package/commands/app-files/app-files.js +5 -0
- package/commands/app-files/app-files_cmds/create.js +98 -0
- package/commands/app-files/app-files_cmds/delete.js +31 -0
- package/commands/app-files/app-files_cmds/download.js +50 -0
- package/commands/app-files/app-files_cmds/list.js +72 -0
- package/commands/config/config_cmds/install.js +92 -0
- package/commands/constants.js +10 -3
- package/commands/tests/testsUtil.js +45 -15
- package/commands/tests/tests_cmds/run-mobile.js +218 -0
- package/commands/tests/tests_cmds/run.js +11 -9
- package/core/util.js +36 -2
- package/env/defaultEnv.js +2 -1
- package/env/dev.js +2 -1
- package/env/env.js +3 -1
- package/env/local.js +2 -1
- package/env/prod.js +2 -1
- package/execution/index.js +13 -1
- package/execution/runAppiumServer.js +133 -0
- package/http/MablHttpAgent.js +4 -1
- package/http/RequestFilteringHttpAgent.js +3 -3
- package/http/axiosProxyConfig.js +27 -22
- package/http/httpUtil.js +6 -10
- package/mablApi/index.js +1 -1
- package/mablscript/MablAction.js +1 -1
- package/mablscript/MablStep.js +28 -2
- package/mablscript/MablStepV2.js +51 -0
- package/mablscript/MablSymbol.js +6 -2
- package/mablscript/actions/ExtractAction.js +11 -6
- package/mablscript/actions/FindAction.js +5 -5
- package/mablscript/actions/JavaScriptAction.js +19 -12
- package/mablscript/importer.js +97 -14
- package/mablscript/mobile/steps/CreateVariableMobileStep.js +53 -0
- package/mablscript/mobile/steps/EnterTextStep.js +41 -0
- package/mablscript/mobile/steps/NavigateBackStep.js +20 -0
- package/mablscript/mobile/steps/NavigateHomeStep.js +21 -0
- package/mablscript/mobile/steps/ScrollStep.js +37 -0
- package/mablscript/mobile/steps/SetOrientationStep.js +20 -0
- package/mablscript/mobile/steps/TapStep.js +37 -0
- package/mablscript/mobile/steps/actions/MobileFindAction.js +23 -0
- package/mablscript/mobile/steps/stepUtil.js +71 -0
- package/mablscript/mobile/tests/StepTestsUtil.js +20 -0
- package/mablscript/mobile/tests/TestMobileFindDescriptors.js +215 -0
- package/mablscript/mobile/tests/steps/CreateVariableMobileStep.mobiletest.js +287 -0
- package/mablscript/mobile/tests/steps/EnterTextStep.mobiletest.js +74 -0
- package/mablscript/mobile/tests/steps/GeneralHumanization.mobiletest.js +167 -0
- package/mablscript/mobile/tests/steps/NavigateBackStep.mobiletest.js +22 -0
- package/mablscript/mobile/tests/steps/NavigateHomeStep.mobiletest.js +22 -0
- package/mablscript/mobile/tests/steps/ScrollStep.mobiletest.js +112 -0
- package/mablscript/mobile/tests/steps/SetOrientationStep.mobiletest.js +27 -0
- package/mablscript/mobile/tests/steps/TapStep.mobiletest.js +53 -0
- package/mablscript/steps/AssertStep.js +48 -38
- package/mablscript/steps/AssertStepOld.js +30 -2
- package/mablscript/steps/CreateVariableStep.js +9 -2
- package/mablscript/steps/EchoStep.js +4 -3
- package/mablscript/steps/ElseIfConditionStep.js +8 -2
- package/mablscript/steps/ElseStep.js +2 -1
- package/mablscript/steps/EndStep.js +2 -1
- package/mablscript/steps/EvaluateJavaScriptStep.js +6 -1
- package/mablscript/steps/IfConditionStep.js +17 -10
- package/mablscript/steps/SendHttpRequestStep.js +4 -3
- package/mablscript/steps/WaitStep.js +4 -3
- package/mablscript/types/GetVariableDescriptor.js +8 -3
- package/mablscript/types/mobile/CreateVariableMobileStepDescriptor.js +9 -0
- package/mablscript/types/mobile/EnterTextStepDescriptor.js +2 -0
- package/mablscript/types/mobile/NavigateBackStepDescriptor.js +2 -0
- package/mablscript/types/mobile/NavigateHomeStepDescriptor.js +2 -0
- package/mablscript/types/mobile/ScrollStepDescriptor.js +2 -0
- package/mablscript/types/mobile/SetOrientationStepDescriptor.js +8 -0
- package/mablscript/types/mobile/StepWithMobileFindDescriptor.js +2 -0
- package/mablscript/types/mobile/TapStepDescriptor.js +8 -0
- package/mablscriptFind/index.js +1 -1
- package/package.json +13 -6
- package/resources/pdf-viewer/embeddedPdfDetection.js +1 -14
- package/resources/webdriver.js +21 -0
- package/upload/index.js +5 -0
- package/util/FileCache.js +180 -0
- package/util/Lazy.js +90 -0
- package/util/MobileAppFileCache.js +102 -0
- package/util/RichPromise.js +3 -1
- package/webdriver/index.js +41 -0
package/upload/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UploadClient = void 0;
|
|
4
|
+
var upload_service_client_nodejs_1 = require("@mablhq/upload-service-client-nodejs");
|
|
5
|
+
Object.defineProperty(exports, "UploadClient", { enumerable: true, get: function () { return upload_service_client_nodejs_1.UploadClient; } });
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FileCache = void 0;
|
|
7
|
+
const crc32c_1 = require("@aws-crypto/crc32c");
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const promises_1 = require("fs/promises");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const cachedir_1 = __importDefault(require("cachedir"));
|
|
12
|
+
const env_1 = require("../env/env");
|
|
13
|
+
const loggingProvider_1 = require("../providers/logging/loggingProvider");
|
|
14
|
+
const DEFAULT_READ_BUFFER_SIZE_BYTES = 8 * 1024 * 1024;
|
|
15
|
+
class File {
|
|
16
|
+
constructor(directory, name) {
|
|
17
|
+
this.path = (0, node_path_1.resolve)((0, node_path_1.join)(directory, name));
|
|
18
|
+
this.stats = (0, fs_1.statSync)(this.path);
|
|
19
|
+
}
|
|
20
|
+
get createdTimeMillis() {
|
|
21
|
+
return this.stats.ctimeMs;
|
|
22
|
+
}
|
|
23
|
+
delete() {
|
|
24
|
+
(0, fs_1.rmSync)(this.path, { force: true, recursive: this.isDirectory });
|
|
25
|
+
}
|
|
26
|
+
get isDirectory() {
|
|
27
|
+
return this.stats.isDirectory();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
class FileCache {
|
|
31
|
+
constructor({ cacheName, maximumCachedFiles, maximumCachedFileAgeMinutes, readBufferSizeBytes, }) {
|
|
32
|
+
this.cacheName = cacheName;
|
|
33
|
+
this.cacheDirectoryPath = (0, node_path_1.resolve)((0, node_path_1.join)((0, cachedir_1.default)(env_1.CONF_FILE_PROJECT_NAME), cacheName));
|
|
34
|
+
this.maximumCachedFiles = maximumCachedFiles;
|
|
35
|
+
this.maximumCachedFileAgeMinutes = maximumCachedFileAgeMinutes;
|
|
36
|
+
this.readBufferSizeBytes =
|
|
37
|
+
readBufferSizeBytes !== null && readBufferSizeBytes !== void 0 ? readBufferSizeBytes : DEFAULT_READ_BUFFER_SIZE_BYTES;
|
|
38
|
+
}
|
|
39
|
+
async performCacheCleanup() {
|
|
40
|
+
if (!(await this.directoryExists(this.cacheDirectoryPath))) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const nowMillis = Date.now();
|
|
44
|
+
try {
|
|
45
|
+
const files = (await (0, promises_1.readdir)(this.cacheDirectoryPath))
|
|
46
|
+
.map((name) => new File(this.cacheDirectoryPath, name))
|
|
47
|
+
.filter((file) => {
|
|
48
|
+
if (this.maximumCachedFileAgeMinutes &&
|
|
49
|
+
(nowMillis - file.createdTimeMillis) / 1000 / 60 >
|
|
50
|
+
this.maximumCachedFileAgeMinutes) {
|
|
51
|
+
loggingProvider_1.logger.info(`Deleting expired cache ${file.isDirectory ? 'directory' : 'file'} ${file.path}`);
|
|
52
|
+
file.delete();
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
})
|
|
57
|
+
.sort((left, right) => right.createdTimeMillis - left.createdTimeMillis);
|
|
58
|
+
if (this.maximumCachedFiles && files.length > this.maximumCachedFiles) {
|
|
59
|
+
loggingProvider_1.logger.info(`Deleting the oldest ${files.length - this.maximumCachedFiles} file(s) in ${this.cacheName} cache`);
|
|
60
|
+
files.slice(this.maximumCachedFiles).forEach((file) => {
|
|
61
|
+
loggingProvider_1.logger.info(`Deleting cache ${file.isDirectory ? 'directory' : 'file'} ${file.path}`);
|
|
62
|
+
file.delete();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
loggingProvider_1.logger.error(`Error performing cache cleanup on ${this.cacheDirectoryPath}`, error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async getOrCreateFile(name, getReader, metadata) {
|
|
71
|
+
const path = this.getCacheFilePath(name);
|
|
72
|
+
await this.performCacheCleanup();
|
|
73
|
+
if (await this.cachedFileIsValid(path, metadata)) {
|
|
74
|
+
return path;
|
|
75
|
+
}
|
|
76
|
+
const { crc32cHex: writtenCrc32cHex, sizeBytes: writtenSizeBytes } = await this.write(await getReader(), path, metadata === null || metadata === void 0 ? void 0 : metadata.sizeBytes);
|
|
77
|
+
if (metadata) {
|
|
78
|
+
if (writtenSizeBytes !== metadata.sizeBytes) {
|
|
79
|
+
throw new Error(`Expected ${metadata.sizeBytes}b but received ${writtenSizeBytes}b`);
|
|
80
|
+
}
|
|
81
|
+
if (writtenCrc32cHex !== metadata.crc32cHex) {
|
|
82
|
+
throw new Error(`Expected checksum ${metadata.crc32cHex} but computed ${writtenCrc32cHex}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!(await this.cachedFileIsValid(path, metadata))) {
|
|
86
|
+
throw new Error(`Downloaded cache file ${path} failed verification`);
|
|
87
|
+
}
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
async directoryExists(path) {
|
|
91
|
+
try {
|
|
92
|
+
return (await (0, promises_1.stat)(path)).isDirectory();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async fileExists(path) {
|
|
99
|
+
try {
|
|
100
|
+
return (await (0, promises_1.stat)(path)).isFile();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async cachedFileIsValid(path, metadata) {
|
|
107
|
+
if (!(await this.fileExists(path))) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (metadata) {
|
|
111
|
+
loggingProvider_1.logger.info(`Verifying integrity of cache file ${path}`);
|
|
112
|
+
const actualSizeBytes = (await (0, promises_1.stat)(path)).size;
|
|
113
|
+
if (actualSizeBytes !== metadata.sizeBytes) {
|
|
114
|
+
loggingProvider_1.logger.warn(`Expected cache file at ${path} to have size [${metadata.sizeBytes}b] but was [${actualSizeBytes}b]`);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const actualCrc32cHex = await this.computeCrc32cHex(path);
|
|
118
|
+
if (actualCrc32cHex !== metadata.crc32cHex) {
|
|
119
|
+
loggingProvider_1.logger.warn(`Expected cache file at ${path} to have checksum [${metadata.crc32cHex}] but was [${actualCrc32cHex}]}`);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
getCacheFilePath(relativePath) {
|
|
126
|
+
return (0, node_path_1.resolve)((0, node_path_1.join)(this.cacheDirectoryPath, relativePath));
|
|
127
|
+
}
|
|
128
|
+
async write(reader, destination, expectedSizeBytes) {
|
|
129
|
+
const destinationDirectory = (0, node_path_1.dirname)(destination);
|
|
130
|
+
if (!(await this.directoryExists(destinationDirectory))) {
|
|
131
|
+
await (0, promises_1.mkdir)(destinationDirectory, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
const fd = await (0, promises_1.open)(destination, 'w');
|
|
134
|
+
const writeStream = fd.createWriteStream({ autoClose: true });
|
|
135
|
+
const metadata = await new Promise((resolve, reject) => {
|
|
136
|
+
const crc32c = new crc32c_1.Crc32c();
|
|
137
|
+
let sizeBytes = 0;
|
|
138
|
+
let percentageComplete = 0;
|
|
139
|
+
writeStream.on('error', (error) => reject(error));
|
|
140
|
+
writeStream.on('close', () => resolve({ crc32cHex: crc32c.digest().toString(16), sizeBytes }));
|
|
141
|
+
reader.on('error', (error) => reject(error));
|
|
142
|
+
reader.on('end', () => writeStream.close());
|
|
143
|
+
reader.on('data', (chunk) => {
|
|
144
|
+
crc32c.update(chunk);
|
|
145
|
+
sizeBytes += chunk.byteLength;
|
|
146
|
+
if (expectedSizeBytes) {
|
|
147
|
+
const newPercentageComplete = Math.round((sizeBytes / expectedSizeBytes) * 100);
|
|
148
|
+
if (newPercentageComplete % 10 === 0 &&
|
|
149
|
+
newPercentageComplete > percentageComplete) {
|
|
150
|
+
percentageComplete = newPercentageComplete;
|
|
151
|
+
loggingProvider_1.logger.info(`Downloaded ${sizeBytes} of ${expectedSizeBytes} bytes (${percentageComplete}% complete)`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
reader.pipe(writeStream);
|
|
156
|
+
});
|
|
157
|
+
await this.performCacheCleanup();
|
|
158
|
+
return metadata;
|
|
159
|
+
}
|
|
160
|
+
async computeCrc32cHex(path) {
|
|
161
|
+
const fd = await (0, promises_1.open)(path, 'r');
|
|
162
|
+
const crc32c = new crc32c_1.Crc32c();
|
|
163
|
+
let totalBytesRead = 0;
|
|
164
|
+
while (true) {
|
|
165
|
+
const { buffer, bytesRead: chunkSizeBytes } = await fd.read({
|
|
166
|
+
buffer: Buffer.alloc(this.readBufferSizeBytes),
|
|
167
|
+
length: this.readBufferSizeBytes,
|
|
168
|
+
position: totalBytesRead,
|
|
169
|
+
});
|
|
170
|
+
if (chunkSizeBytes === 0) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
totalBytesRead += chunkSizeBytes;
|
|
174
|
+
const chunk = buffer.subarray(0, chunkSizeBytes);
|
|
175
|
+
crc32c.update(chunk);
|
|
176
|
+
}
|
|
177
|
+
return crc32c.digest().toString(16);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.FileCache = FileCache;
|
package/util/Lazy.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Lazy = void 0;
|
|
4
|
+
class Lazy {
|
|
5
|
+
constructor(initializer) {
|
|
6
|
+
this.initializer = initializer;
|
|
7
|
+
}
|
|
8
|
+
apply(action) {
|
|
9
|
+
return action(this.instance);
|
|
10
|
+
}
|
|
11
|
+
get instance() {
|
|
12
|
+
if (!this.value) {
|
|
13
|
+
this.value = this.initializer();
|
|
14
|
+
}
|
|
15
|
+
return this.value;
|
|
16
|
+
}
|
|
17
|
+
get isInitialized() {
|
|
18
|
+
return !!this.value;
|
|
19
|
+
}
|
|
20
|
+
map(mapper) {
|
|
21
|
+
return new Lazy(() => this.apply(mapper));
|
|
22
|
+
}
|
|
23
|
+
get maybe() {
|
|
24
|
+
return this.value;
|
|
25
|
+
}
|
|
26
|
+
reset() {
|
|
27
|
+
this.value = undefined;
|
|
28
|
+
}
|
|
29
|
+
withValue(consumer) {
|
|
30
|
+
consumer(this.instance);
|
|
31
|
+
}
|
|
32
|
+
withValueAsync(consumer) {
|
|
33
|
+
return this.apply(consumer);
|
|
34
|
+
}
|
|
35
|
+
static from(instance) {
|
|
36
|
+
return new Lazy(() => instance);
|
|
37
|
+
}
|
|
38
|
+
static of(initializer) {
|
|
39
|
+
return new Lazy(initializer);
|
|
40
|
+
}
|
|
41
|
+
static proxy(initializer) {
|
|
42
|
+
const lazy = Lazy.of(initializer);
|
|
43
|
+
return new Proxy(lazy, new LazyProxy(lazy));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.Lazy = Lazy;
|
|
47
|
+
class LazyProxy {
|
|
48
|
+
constructor(lazy) {
|
|
49
|
+
this.lazy = lazy;
|
|
50
|
+
}
|
|
51
|
+
apply(target, _thisArg, args) {
|
|
52
|
+
return Reflect.apply(target, this.lazy.instance, args);
|
|
53
|
+
}
|
|
54
|
+
construct(target, argArray, _newTarget) {
|
|
55
|
+
return Reflect.construct(target, argArray, this.lazy.instance.constructor);
|
|
56
|
+
}
|
|
57
|
+
defineProperty(_target, property, attributes) {
|
|
58
|
+
return Reflect.defineProperty(this.lazy.instance, property, attributes);
|
|
59
|
+
}
|
|
60
|
+
deleteProperty(_target, p) {
|
|
61
|
+
return Reflect.deleteProperty(this.lazy.instance, p);
|
|
62
|
+
}
|
|
63
|
+
get(_target, p, receiver) {
|
|
64
|
+
return Reflect.get(this.lazy.instance, p, receiver);
|
|
65
|
+
}
|
|
66
|
+
getOwnPropertyDescriptor(_target, p) {
|
|
67
|
+
return Reflect.getOwnPropertyDescriptor(this.lazy.instance, p);
|
|
68
|
+
}
|
|
69
|
+
getPrototypeOf(_target) {
|
|
70
|
+
return Reflect.getPrototypeOf(this.lazy.instance);
|
|
71
|
+
}
|
|
72
|
+
has(_target, p) {
|
|
73
|
+
return Reflect.has(this.lazy.instance, p);
|
|
74
|
+
}
|
|
75
|
+
isExtensible(_target) {
|
|
76
|
+
return Reflect.isExtensible(this.lazy.instance);
|
|
77
|
+
}
|
|
78
|
+
ownKeys(_target) {
|
|
79
|
+
return Reflect.ownKeys(this.lazy.instance);
|
|
80
|
+
}
|
|
81
|
+
preventExtensions(_target) {
|
|
82
|
+
return Reflect.preventExtensions(this.lazy.instance);
|
|
83
|
+
}
|
|
84
|
+
set(_target, p, newValue, receiver) {
|
|
85
|
+
return Reflect.set(this.lazy.instance, p, newValue, receiver);
|
|
86
|
+
}
|
|
87
|
+
setPrototypeOf(_target, v) {
|
|
88
|
+
return Reflect.setPrototypeOf(this.lazy.instance, v);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MobileAppFileCache = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
|
+
const promises_1 = require("fs/promises");
|
|
7
|
+
const mablApi_1 = require("../mablApi");
|
|
8
|
+
const util_1 = require("../core/util");
|
|
9
|
+
const FileCache_1 = require("./FileCache");
|
|
10
|
+
const loggingProvider_1 = require("../providers/logging/loggingProvider");
|
|
11
|
+
const MOBILE_APP_FILE_CACHE_DIRECTORY = 'mobile-app-files';
|
|
12
|
+
const DEFAULT_MAX_MOBILE_APP_FILES = 10;
|
|
13
|
+
const DEFAULT_MAX_MOBILE_APP_FILE_AGE_MINUTES = 60 * 24;
|
|
14
|
+
const MOBILE_APP_FILE_IMMUTABLE_FIELDS = [
|
|
15
|
+
'content_type',
|
|
16
|
+
'crc32c_hex',
|
|
17
|
+
'created_by_id',
|
|
18
|
+
'created_time',
|
|
19
|
+
'extension',
|
|
20
|
+
'id',
|
|
21
|
+
'name',
|
|
22
|
+
'platform',
|
|
23
|
+
'size',
|
|
24
|
+
'workspace_id',
|
|
25
|
+
];
|
|
26
|
+
const MOBILE_APP_FILE_IMMUTABLE_FIELD_SET = new Set(MOBILE_APP_FILE_IMMUTABLE_FIELDS);
|
|
27
|
+
class MobileAppFileCache extends FileCache_1.FileCache {
|
|
28
|
+
constructor(options) {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
super({
|
|
31
|
+
...options,
|
|
32
|
+
cacheName: MOBILE_APP_FILE_CACHE_DIRECTORY,
|
|
33
|
+
maximumCachedFiles: (_a = options === null || options === void 0 ? void 0 : options.maximumCachedFiles) !== null && _a !== void 0 ? _a : DEFAULT_MAX_MOBILE_APP_FILES,
|
|
34
|
+
maximumCachedFileAgeMinutes: (_b = options === null || options === void 0 ? void 0 : options.maximumCachedFileAgeMinutes) !== null && _b !== void 0 ? _b : DEFAULT_MAX_MOBILE_APP_FILE_AGE_MINUTES,
|
|
35
|
+
});
|
|
36
|
+
this.mablApiClientPromise = (options === null || options === void 0 ? void 0 : options.mablApiClient)
|
|
37
|
+
? Promise.resolve(options.mablApiClient)
|
|
38
|
+
: (0, util_1.getApiClient)();
|
|
39
|
+
}
|
|
40
|
+
async getMobileAppFileImmutableFields(id) {
|
|
41
|
+
const getReader = async () => {
|
|
42
|
+
const apiClient = await this.mablApiClientPromise;
|
|
43
|
+
const mobileAppFile = this.retainImmutableFields(await apiClient.getMobileAppFile(id));
|
|
44
|
+
return stream_1.Readable.from(Buffer.from(JSON.stringify(mobileAppFile, undefined, 0), 'utf-8'));
|
|
45
|
+
};
|
|
46
|
+
const path = await this.getOrCreateFile((0, path_1.join)(id, 'MobileAppFile.json'), getReader);
|
|
47
|
+
return JSON.parse((await (0, promises_1.readFile)(path)).toString());
|
|
48
|
+
}
|
|
49
|
+
async getMobileAppFile(id) {
|
|
50
|
+
const mobileAppFile = await this.getMobileAppFileImmutableFields(id);
|
|
51
|
+
const { crc32c_hex: crc32cHex, size: sizeBytes } = mobileAppFile;
|
|
52
|
+
const getReader = async () => {
|
|
53
|
+
loggingProvider_1.logger.info(`Downloading mobile app file ${id}`);
|
|
54
|
+
const apiClient = await this.mablApiClientPromise;
|
|
55
|
+
const { download_url: downloadUrl } = await apiClient.getMobileAppFile(id, true);
|
|
56
|
+
return (await apiClient.httpClient.get(downloadUrl, {
|
|
57
|
+
responseType: 'stream',
|
|
58
|
+
})).data;
|
|
59
|
+
};
|
|
60
|
+
const extension = this.getMobileAppFileExtension(mobileAppFile);
|
|
61
|
+
if (!extension) {
|
|
62
|
+
throw new Error(`Unable to determine file extension for mobile app file ${id}`);
|
|
63
|
+
}
|
|
64
|
+
const path = await this.getOrCreateFile((0, path_1.join)(id, `MobileAppFile.${extension}`), getReader, {
|
|
65
|
+
crc32cHex: crc32cHex,
|
|
66
|
+
sizeBytes: sizeBytes,
|
|
67
|
+
});
|
|
68
|
+
return { ...mobileAppFile, path };
|
|
69
|
+
}
|
|
70
|
+
retainImmutableFields(mobileAppFile) {
|
|
71
|
+
const immutableFields = { ...mobileAppFile };
|
|
72
|
+
Object.keys(immutableFields).forEach((field) => {
|
|
73
|
+
if (!MOBILE_APP_FILE_IMMUTABLE_FIELD_SET.has(field)) {
|
|
74
|
+
delete immutableFields[field];
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return immutableFields;
|
|
78
|
+
}
|
|
79
|
+
getMobileAppFileExtension({ extension, id, name, platform, }) {
|
|
80
|
+
let resolvedExtension = extension;
|
|
81
|
+
if (!resolvedExtension && name) {
|
|
82
|
+
resolvedExtension = (0, path_1.extname)(name);
|
|
83
|
+
}
|
|
84
|
+
if (!resolvedExtension && platform) {
|
|
85
|
+
switch (platform) {
|
|
86
|
+
case mablApi_1.MobilePlatformEnum.Android:
|
|
87
|
+
resolvedExtension = 'apk';
|
|
88
|
+
break;
|
|
89
|
+
case mablApi_1.MobilePlatformEnum.Ios:
|
|
90
|
+
resolvedExtension = 'app.zip';
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
throw new Error(`Mobile app file ${id} has unknown platform ${platform}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (resolvedExtension === null || resolvedExtension === void 0 ? void 0 : resolvedExtension.startsWith('.')) {
|
|
97
|
+
resolvedExtension = resolvedExtension.substring(1);
|
|
98
|
+
}
|
|
99
|
+
return resolvedExtension;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.MobileAppFileCache = MobileAppFileCache;
|
package/util/RichPromise.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var _a;
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
class RichPromise {
|
|
4
5
|
constructor(chained) {
|
|
@@ -6,6 +7,7 @@ class RichPromise {
|
|
|
6
7
|
this.resolved = false;
|
|
7
8
|
this.rejecter = () => { };
|
|
8
9
|
this.rejected = false;
|
|
10
|
+
this[_a] = 'promise';
|
|
9
11
|
this.promise = new Promise((resolve, reject) => {
|
|
10
12
|
this.resolver = resolve;
|
|
11
13
|
this.rejecter = reject;
|
|
@@ -48,4 +50,4 @@ class RichPromise {
|
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
exports.default = RichPromise;
|
|
51
|
-
Symbol.toStringTag;
|
|
53
|
+
_a = Symbol.toStringTag;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDriver = exports.getSessionInfo = exports.setSessionInfo = exports.initializeDriverInPage = void 0;
|
|
4
|
+
const web2driver_1 = require("web2driver");
|
|
5
|
+
let MABL_SESSION_CONFIG;
|
|
6
|
+
let MABL_APPIUM_DRIVER;
|
|
7
|
+
async function initializeDriverInPage(config) {
|
|
8
|
+
var _a, _b, _c, _d, _e;
|
|
9
|
+
const driver = await web2driver_1.Web2Driver.attachToSession(config.sessionId, {
|
|
10
|
+
protocol: (_a = config.protocol) !== null && _a !== void 0 ? _a : 'http',
|
|
11
|
+
hostname: (_b = config.hostname) !== null && _b !== void 0 ? _b : 'localhost',
|
|
12
|
+
port: (_c = config.port) !== null && _c !== void 0 ? _c : 4723,
|
|
13
|
+
path: (_d = config.path) !== null && _d !== void 0 ? _d : '/',
|
|
14
|
+
}, config.capabilities, (_e = config.isW3C) !== null && _e !== void 0 ? _e : true);
|
|
15
|
+
return driver;
|
|
16
|
+
}
|
|
17
|
+
exports.initializeDriverInPage = initializeDriverInPage;
|
|
18
|
+
function setSessionInfo(config) {
|
|
19
|
+
MABL_SESSION_CONFIG = config;
|
|
20
|
+
}
|
|
21
|
+
exports.setSessionInfo = setSessionInfo;
|
|
22
|
+
function getSessionInfo() {
|
|
23
|
+
return MABL_SESSION_CONFIG;
|
|
24
|
+
}
|
|
25
|
+
exports.getSessionInfo = getSessionInfo;
|
|
26
|
+
async function getDriver() {
|
|
27
|
+
if (!MABL_APPIUM_DRIVER) {
|
|
28
|
+
MABL_APPIUM_DRIVER = (await initializeDriverInPage(MABL_SESSION_CONFIG));
|
|
29
|
+
}
|
|
30
|
+
return MABL_APPIUM_DRIVER;
|
|
31
|
+
}
|
|
32
|
+
exports.getDriver = getDriver;
|
|
33
|
+
window.mabl = {
|
|
34
|
+
mobile: {
|
|
35
|
+
initializeDriverInPage,
|
|
36
|
+
setSessionInfo,
|
|
37
|
+
getSessionInfo,
|
|
38
|
+
getDriver,
|
|
39
|
+
},
|
|
40
|
+
...window._mabl,
|
|
41
|
+
};
|