@wdio/utils 9.0.0-alpha.9 → 9.0.4
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/build/envDetector.d.ts +3 -3
- package/build/envDetector.d.ts.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1902 -24
- package/build/initializeServices.d.ts +3 -3
- package/build/initializeServices.d.ts.map +1 -1
- package/build/monad.d.ts.map +1 -1
- package/build/node/manager.d.ts +2 -2
- package/build/node/manager.d.ts.map +1 -1
- package/build/node/startWebDriver.d.ts +2 -3
- package/build/node/startWebDriver.d.ts.map +1 -1
- package/build/node/utils.d.ts.map +1 -1
- package/build/node.js +473 -0
- package/build/pIteration.d.ts.map +1 -1
- package/build/shim.d.ts.map +1 -1
- package/build/startWebDriver.d.ts +2 -3
- package/build/startWebDriver.d.ts.map +1 -1
- package/build/test-framework/errorHandler.d.ts.map +1 -1
- package/build/test-framework/testFnWrapper.d.ts.map +1 -1
- package/build/test-framework/testInterfaceWrapper.d.ts.map +1 -1
- package/build/utils.d.ts +11 -2
- package/build/utils.d.ts.map +1 -1
- package/package.json +10 -11
- package/build/constants.js +0 -114
- package/build/envDetector.js +0 -251
- package/build/initializePlugin.js +0 -38
- package/build/initializeServices.js +0 -159
- package/build/monad.js +0 -196
- package/build/node/index.js +0 -3
- package/build/node/manager.js +0 -106
- package/build/node/startWebDriver.js +0 -140
- package/build/node/utils.js +0 -285
- package/build/pIteration.js +0 -347
- package/build/shim.js +0 -293
- package/build/startWebDriver.js +0 -20
- package/build/test-framework/errorHandler.js +0 -33
- package/build/test-framework/index.js +0 -4
- package/build/test-framework/testFnWrapper.js +0 -97
- package/build/test-framework/testInterfaceWrapper.js +0 -162
- package/build/test-framework/types.js +0 -1
- package/build/utils.js +0 -320
- /package/{LICENSE-MIT → LICENSE} +0 -0
package/build/node/utils.js
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import fsp from 'node:fs/promises';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import cp from 'node:child_process';
|
|
6
|
-
import decamelize from 'decamelize';
|
|
7
|
-
import logger from '@wdio/logger';
|
|
8
|
-
import { install, canDownload, resolveBuildId, detectBrowserPlatform, Browser, ChromeReleaseChannel, computeExecutablePath } from '@puppeteer/browsers';
|
|
9
|
-
import { download as downloadGeckodriver } from 'geckodriver';
|
|
10
|
-
import { download as downloadEdgedriver } from 'edgedriver';
|
|
11
|
-
import { locateChrome, locateFirefox, locateApp } from 'locate-app';
|
|
12
|
-
const log = logger('webdriver');
|
|
13
|
-
const EXCLUDED_PARAMS = ['version', 'help'];
|
|
14
|
-
/**
|
|
15
|
-
* Helper utility to check file access
|
|
16
|
-
* @param {string} file file to check access for
|
|
17
|
-
* @return true if file can be accessed
|
|
18
|
-
*/
|
|
19
|
-
export const canAccess = (file) => {
|
|
20
|
-
if (!file) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
fs.accessSync(file);
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
catch (err) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
export function parseParams(params) {
|
|
32
|
-
return Object.entries(params)
|
|
33
|
-
.filter(([key,]) => !EXCLUDED_PARAMS.includes(key))
|
|
34
|
-
.map(([key, val]) => {
|
|
35
|
-
if (typeof val === 'boolean' && !val) {
|
|
36
|
-
return '';
|
|
37
|
-
}
|
|
38
|
-
const vals = Array.isArray(val) ? val : [val];
|
|
39
|
-
return vals.map((v) => `--${decamelize(key, { separator: '-' })}${typeof v === 'boolean' ? '' : `=${v}`}`);
|
|
40
|
-
})
|
|
41
|
-
.flat()
|
|
42
|
-
.filter(Boolean);
|
|
43
|
-
}
|
|
44
|
-
export function getBuildIdByChromePath(chromePath) {
|
|
45
|
-
if (!chromePath) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (os.platform() === 'win32') {
|
|
49
|
-
const versionPath = path.dirname(chromePath);
|
|
50
|
-
const contents = fs.readdirSync(versionPath);
|
|
51
|
-
const versions = contents.filter(a => /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/g.test(a));
|
|
52
|
-
// returning oldest in case there is an updated version and chrome still hasn't relaunched
|
|
53
|
-
const oldest = versions.sort((a, b) => a > b ? -1 : 1)[0];
|
|
54
|
-
return oldest;
|
|
55
|
-
}
|
|
56
|
-
const versionString = cp.execSync(`"${chromePath}" --version --no-sandbox`).toString();
|
|
57
|
-
const versionSanitized = versionString.trim().split(' ').find((s) => s.split('.').length === 4);
|
|
58
|
-
if (!versionSanitized) {
|
|
59
|
-
throw new Error(`Couldn't find valid Chrome version from "${versionString}", please raise an issue in the WebdriverIO project (https://github.com/webdriverio/webdriverio/issues/new/choose)`);
|
|
60
|
-
}
|
|
61
|
-
return versionSanitized;
|
|
62
|
-
}
|
|
63
|
-
export async function getBuildIdByFirefoxPath(firefoxPath) {
|
|
64
|
-
if (!firefoxPath) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (os.platform() === 'win32') {
|
|
68
|
-
const appPath = path.dirname(firefoxPath);
|
|
69
|
-
const contents = (await fsp.readFile(path.join(appPath, 'application.ini'))).toString('utf-8');
|
|
70
|
-
return contents
|
|
71
|
-
.split('\n')
|
|
72
|
-
.filter((line) => line.startsWith('Version='))
|
|
73
|
-
.map((line) => line.replace('Version=', '').replace(/\r/, ''))
|
|
74
|
-
.pop();
|
|
75
|
-
}
|
|
76
|
-
const versionString = cp.execSync(`"${firefoxPath}" --version`).toString();
|
|
77
|
-
return versionString.trim().split(' ').pop()?.trim();
|
|
78
|
-
}
|
|
79
|
-
let lastTimeCalled = Date.now();
|
|
80
|
-
export const downloadProgressCallback = (artifact, downloadedBytes, totalBytes) => {
|
|
81
|
-
if (Date.now() - lastTimeCalled < 1000) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const percentage = ((downloadedBytes / totalBytes) * 100).toFixed(2);
|
|
85
|
-
log.progress(`Downloading ${artifact} ${percentage}%`);
|
|
86
|
-
lastTimeCalled = Date.now();
|
|
87
|
-
};
|
|
88
|
-
/**
|
|
89
|
-
* Installs a package using the provided installation options and clears the progress log afterward.
|
|
90
|
-
*
|
|
91
|
-
* @description
|
|
92
|
-
* When installing a package, progress updates are logged using `log.progress`.
|
|
93
|
-
* To ensure the formatting of subsequent logs is not disrupted, it's essential to clear the progress log after the installation is complete.
|
|
94
|
-
* This method combines the installation step and the clearing of the progress log.
|
|
95
|
-
*
|
|
96
|
-
* @see {@link https://github.com/webdriverio/webdriverio/blob/main/packages/wdio-logger/README.md#custom-log-levels} for more information.
|
|
97
|
-
*
|
|
98
|
-
* @param {InstallOptions & { unpack?: true | undefined }} args - An object containing installation options and an optional `unpack` flag.
|
|
99
|
-
* @returns {Promise<void>} A Promise that resolves once the package is installed and clear the progress log.
|
|
100
|
-
*/
|
|
101
|
-
const _install = async (args, retry = false) => {
|
|
102
|
-
await install(args).catch((err) => {
|
|
103
|
-
const error = `Failed downloading ${args.browser} v${args.buildId}: ${err.message}, retrying ...`;
|
|
104
|
-
if (retry) {
|
|
105
|
-
throw new Error(error);
|
|
106
|
-
}
|
|
107
|
-
log.error(error);
|
|
108
|
-
return _install(args, true);
|
|
109
|
-
});
|
|
110
|
-
log.progress('');
|
|
111
|
-
};
|
|
112
|
-
function locateChromeSafely() {
|
|
113
|
-
return locateChrome().catch(() => undefined);
|
|
114
|
-
}
|
|
115
|
-
export async function setupPuppeteerBrowser(cacheDir, caps) {
|
|
116
|
-
caps.browserName = caps.browserName?.toLowerCase();
|
|
117
|
-
const browserName = caps.browserName === Browser.FIREFOX
|
|
118
|
-
? Browser.FIREFOX
|
|
119
|
-
: caps.browserName === Browser.CHROMIUM
|
|
120
|
-
? Browser.CHROMIUM
|
|
121
|
-
: Browser.CHROME;
|
|
122
|
-
const exist = await fsp.access(cacheDir).then(() => true, () => false);
|
|
123
|
-
const isChromeOrChromium = browserName === Browser.CHROME || caps.browserName === Browser.CHROMIUM;
|
|
124
|
-
if (!exist) {
|
|
125
|
-
await fsp.mkdir(cacheDir, { recursive: true });
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* in case we run Chromium tests we have to switch back to browserName: 'chrome'
|
|
129
|
-
* as 'chromium' is not recognised as a valid browser name by Chromedriver
|
|
130
|
-
*/
|
|
131
|
-
if (browserName === Browser.CHROMIUM) {
|
|
132
|
-
caps.browserName = Browser.CHROME;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* don't set up Chrome/Firefox if a binary was defined in caps
|
|
136
|
-
*/
|
|
137
|
-
const browserOptions = (isChromeOrChromium
|
|
138
|
-
? caps['goog:chromeOptions']
|
|
139
|
-
: caps['moz:firefoxOptions']) || {};
|
|
140
|
-
if (typeof browserOptions.binary === 'string') {
|
|
141
|
-
return {
|
|
142
|
-
executablePath: browserOptions.binary,
|
|
143
|
-
browserVersion: (caps.browserVersion ||
|
|
144
|
-
(isChromeOrChromium
|
|
145
|
-
? getBuildIdByChromePath(browserOptions.binary)
|
|
146
|
-
: await getBuildIdByFirefoxPath(browserOptions.binary)))
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
const platform = detectBrowserPlatform();
|
|
150
|
-
if (!platform) {
|
|
151
|
-
throw new Error('The current platform is not supported.');
|
|
152
|
-
}
|
|
153
|
-
if (!caps.browserVersion) {
|
|
154
|
-
const executablePath = browserName === Browser.CHROME
|
|
155
|
-
? await locateChromeSafely()
|
|
156
|
-
: browserName === Browser.CHROMIUM
|
|
157
|
-
? await locateApp({
|
|
158
|
-
appName: Browser.CHROMIUM,
|
|
159
|
-
macOsName: Browser.CHROMIUM,
|
|
160
|
-
linuxWhich: 'chromium-browser'
|
|
161
|
-
}).catch(() => undefined)
|
|
162
|
-
: await locateFirefox().catch(() => undefined);
|
|
163
|
-
const tag = isChromeOrChromium
|
|
164
|
-
? getBuildIdByChromePath(executablePath)
|
|
165
|
-
: await getBuildIdByFirefoxPath(executablePath);
|
|
166
|
-
/**
|
|
167
|
-
* verify that we have a valid Chrome/Firefox browser installed
|
|
168
|
-
*/
|
|
169
|
-
if (tag) {
|
|
170
|
-
return {
|
|
171
|
-
executablePath,
|
|
172
|
-
browserVersion: await resolveBuildId(browserName, platform, tag)
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* otherwise download provided Chrome/Firefox browser version or "stable"
|
|
178
|
-
*/
|
|
179
|
-
const tag = browserName === Browser.CHROME
|
|
180
|
-
? caps.browserVersion || ChromeReleaseChannel.STABLE
|
|
181
|
-
: caps.browserVersion || 'latest';
|
|
182
|
-
const buildId = await resolveBuildId(browserName, platform, tag);
|
|
183
|
-
const installOptions = {
|
|
184
|
-
unpack: true,
|
|
185
|
-
cacheDir,
|
|
186
|
-
platform,
|
|
187
|
-
buildId,
|
|
188
|
-
browser: browserName,
|
|
189
|
-
downloadProgressCallback: (downloadedBytes, totalBytes) => downloadProgressCallback(`${browserName} (${buildId})`, downloadedBytes, totalBytes)
|
|
190
|
-
};
|
|
191
|
-
const isCombinationAvailable = await canDownload(installOptions);
|
|
192
|
-
if (!isCombinationAvailable) {
|
|
193
|
-
throw new Error(`Couldn't find a matching ${browserName} browser for tag "${buildId}" on platform "${platform}"`);
|
|
194
|
-
}
|
|
195
|
-
log.info(`Setting up ${browserName} v${buildId}`);
|
|
196
|
-
await _install(installOptions);
|
|
197
|
-
const executablePath = computeExecutablePath(installOptions);
|
|
198
|
-
/**
|
|
199
|
-
* for Chromium browser `resolveBuildId` returns with a useless build id
|
|
200
|
-
* which will not find a Chromedriver, therefor we need to resolve the
|
|
201
|
-
* id using Chrome as browser name
|
|
202
|
-
*/
|
|
203
|
-
let browserVersion = buildId;
|
|
204
|
-
if (browserName === Browser.CHROMIUM) {
|
|
205
|
-
browserVersion = await resolveBuildId(Browser.CHROME, platform, tag);
|
|
206
|
-
}
|
|
207
|
-
return { executablePath, browserVersion };
|
|
208
|
-
}
|
|
209
|
-
export function getDriverOptions(caps) {
|
|
210
|
-
return (caps['wdio:chromedriverOptions'] ||
|
|
211
|
-
caps['wdio:geckodriverOptions'] ||
|
|
212
|
-
caps['wdio:edgedriverOptions'] ||
|
|
213
|
-
// Safaridriver does not have any options as it already
|
|
214
|
-
// is installed on macOS
|
|
215
|
-
{});
|
|
216
|
-
}
|
|
217
|
-
export function getCacheDir(options, caps) {
|
|
218
|
-
const driverOptions = getDriverOptions(caps);
|
|
219
|
-
return driverOptions.cacheDir || options.cacheDir || os.tmpdir();
|
|
220
|
-
}
|
|
221
|
-
export function getMajorVersionFromString(fullVersion) {
|
|
222
|
-
let prefix;
|
|
223
|
-
if (fullVersion) {
|
|
224
|
-
prefix = fullVersion.match(/^[+-]?([0-9]+)/);
|
|
225
|
-
}
|
|
226
|
-
return prefix && prefix.length > 0 ? prefix[0] : '';
|
|
227
|
-
}
|
|
228
|
-
export async function setupChromedriver(cacheDir, driverVersion) {
|
|
229
|
-
const platform = detectBrowserPlatform();
|
|
230
|
-
if (!platform) {
|
|
231
|
-
throw new Error('The current platform is not supported.');
|
|
232
|
-
}
|
|
233
|
-
const version = driverVersion || getBuildIdByChromePath(await locateChromeSafely()) || ChromeReleaseChannel.STABLE;
|
|
234
|
-
const buildId = await resolveBuildId(Browser.CHROMEDRIVER, platform, version);
|
|
235
|
-
let executablePath = computeExecutablePath({
|
|
236
|
-
browser: Browser.CHROMEDRIVER,
|
|
237
|
-
buildId,
|
|
238
|
-
platform,
|
|
239
|
-
cacheDir
|
|
240
|
-
});
|
|
241
|
-
const hasChromedriverInstalled = await fsp.access(executablePath).then(() => true, () => false);
|
|
242
|
-
if (!hasChromedriverInstalled) {
|
|
243
|
-
log.info(`Downloading Chromedriver v${buildId}`);
|
|
244
|
-
const chromedriverInstallOpts = {
|
|
245
|
-
cacheDir,
|
|
246
|
-
buildId,
|
|
247
|
-
platform,
|
|
248
|
-
browser: Browser.CHROMEDRIVER,
|
|
249
|
-
unpack: true,
|
|
250
|
-
downloadProgressCallback: (downloadedBytes, totalBytes) => downloadProgressCallback('Chromedriver', downloadedBytes, totalBytes)
|
|
251
|
-
};
|
|
252
|
-
let knownBuild = buildId;
|
|
253
|
-
if (await canDownload(chromedriverInstallOpts)) {
|
|
254
|
-
await _install({ ...chromedriverInstallOpts, buildId });
|
|
255
|
-
log.info(`Download of Chromedriver v${buildId} was successful`);
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
log.warn(`Chromedriver v${buildId} don't exist, trying to find known good version...`);
|
|
259
|
-
knownBuild = await resolveBuildId(Browser.CHROMEDRIVER, platform, getMajorVersionFromString(version));
|
|
260
|
-
if (knownBuild) {
|
|
261
|
-
await _install({ ...chromedriverInstallOpts, buildId: knownBuild });
|
|
262
|
-
log.info(`Download of Chromedriver v${knownBuild} was successful`);
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
throw new Error(`Couldn't download any known good version from Chromedriver major v${getMajorVersionFromString(version)}, requested full version - v${version}`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
executablePath = computeExecutablePath({
|
|
269
|
-
browser: Browser.CHROMEDRIVER,
|
|
270
|
-
buildId: knownBuild,
|
|
271
|
-
platform,
|
|
272
|
-
cacheDir
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
log.info(`Using Chromedriver v${buildId} from cache directory ${cacheDir}`);
|
|
277
|
-
}
|
|
278
|
-
return { executablePath };
|
|
279
|
-
}
|
|
280
|
-
export function setupGeckodriver(cacheDir, driverVersion) {
|
|
281
|
-
return downloadGeckodriver(driverVersion, cacheDir);
|
|
282
|
-
}
|
|
283
|
-
export function setupEdgedriver(cacheDir, driverVersion) {
|
|
284
|
-
return downloadEdgedriver(driverVersion, cacheDir);
|
|
285
|
-
}
|
package/build/pIteration.js
DELETED
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Implements ES5 [`Array#forEach()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) method.<br><br>
|
|
3
|
-
* Executes the provided callback once for each element.<br>
|
|
4
|
-
* Callbacks are run concurrently,
|
|
5
|
-
* and are only invoked for properties of the array that have been initialized (including those initialized with *undefined*), for unassigned ones `callback` is not run.<br>
|
|
6
|
-
* @param {Array} array - Array to iterate over.
|
|
7
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
8
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
9
|
-
* @return {Promise} - Returns a Promise with undefined value.
|
|
10
|
-
*/
|
|
11
|
-
export const forEach = async (array, callback, thisArg) => {
|
|
12
|
-
const promiseArray = [];
|
|
13
|
-
for (let i = 0; i < array.length; i++) {
|
|
14
|
-
if (i in array) {
|
|
15
|
-
const p = Promise.resolve(array[i]).then((currentValue) => {
|
|
16
|
-
return callback.call(thisArg || this, currentValue, i, array);
|
|
17
|
-
});
|
|
18
|
-
promiseArray.push(p);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
await Promise.all(promiseArray);
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Same functionality as [`forEach()`](global.html#forEach), but runs only one callback at a time.
|
|
25
|
-
* @param {Array} array - Array to iterate over.
|
|
26
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
27
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
28
|
-
* @return {Promise} - Returns a Promise with undefined value.
|
|
29
|
-
*/
|
|
30
|
-
export const forEachSeries = async (array, callback, thisArg) => {
|
|
31
|
-
for (let i = 0; i < array.length; i++) {
|
|
32
|
-
if (i in array) {
|
|
33
|
-
await callback.call(thisArg || this, await array[i], i, array);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Implements ES5 [`Array#map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) method.<br><br>
|
|
39
|
-
* Creates a new array with the results of calling the provided callback once for each element.<br>
|
|
40
|
-
* Callbacks are run concurrently,
|
|
41
|
-
* and are only invoked for properties of the array that have been initialized (including those initialized with *undefined*), for unassigned ones`callback` is not run.<br>
|
|
42
|
-
* Resultant *Array* is always the same *length* as the original one.
|
|
43
|
-
* @param {Array} array - Array to iterate over.
|
|
44
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
45
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
46
|
-
* @return {Promise} - Returns a Promise with the resultant *Array* as value.
|
|
47
|
-
*/
|
|
48
|
-
export const map = async (array, callback, thisArg) => {
|
|
49
|
-
const promiseArray = [];
|
|
50
|
-
for (let i = 0; i < array.length; i++) {
|
|
51
|
-
if (i in array) {
|
|
52
|
-
promiseArray[i] = Promise.resolve(array[i]).then((currentValue) => {
|
|
53
|
-
return callback.call(thisArg || this, currentValue, i, array);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return Promise.all(promiseArray);
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Same functionality as [`map()`](global.html#map), but runs only one callback at a time.
|
|
61
|
-
* @param {Array} array - Array to iterate over.
|
|
62
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
63
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
64
|
-
* @return {Promise} - Returns a Promise with the resultant *Array* as value.
|
|
65
|
-
*/
|
|
66
|
-
export const mapSeries = async (array, callback, thisArg) => {
|
|
67
|
-
const result = [];
|
|
68
|
-
for (let i = 0; i < array.length; i++) {
|
|
69
|
-
if (i in array) {
|
|
70
|
-
result[i] = await callback.call(thisArg || this, await array[i], i, array);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return result;
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* Implements ES5 [`Array#find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) method.<br><br>
|
|
77
|
-
* Returns the value of the element that satisfies the provided `callback`. The value returned is the one found first.<br>
|
|
78
|
-
* Callbacks are run concurrently, meaning that all the callbacks are going to run even if the returned value is found in one of the first elements of `array`,
|
|
79
|
-
* depending on the async calls you are going to use, consider using instead [`findSeries()`](global.html#findSeries).<br>
|
|
80
|
-
* @param {Array} array - Array to iterate over.
|
|
81
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
82
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
83
|
-
* @return {Promise} - Returns a Promise with the element that passed the test as value, otherwise *undefined*.
|
|
84
|
-
*/
|
|
85
|
-
export const find = (array, callback, thisArg) => {
|
|
86
|
-
return new Promise((resolve, reject) => {
|
|
87
|
-
if (array.length === 0) {
|
|
88
|
-
return resolve(undefined);
|
|
89
|
-
}
|
|
90
|
-
let counter = 1;
|
|
91
|
-
for (let i = 0; i < array.length; i++) {
|
|
92
|
-
const check = (found) => {
|
|
93
|
-
if (found) {
|
|
94
|
-
resolve(array[i]);
|
|
95
|
-
}
|
|
96
|
-
else if (counter === array.length) {
|
|
97
|
-
resolve(undefined);
|
|
98
|
-
}
|
|
99
|
-
counter++;
|
|
100
|
-
};
|
|
101
|
-
Promise.resolve(array[i])
|
|
102
|
-
.then((elem) => callback.call(thisArg || this, elem, i, array))
|
|
103
|
-
.then(check)
|
|
104
|
-
.catch(reject);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
};
|
|
108
|
-
/**
|
|
109
|
-
* Same functionality as [`find()`](global.html#find), but runs only one callback at a time.
|
|
110
|
-
* @param {Array} array - Array to iterate over.
|
|
111
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
112
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
113
|
-
* @return {Promise} - Returns a Promise with the element that passed the test as value, otherwise *undefined*.
|
|
114
|
-
*/
|
|
115
|
-
export const findSeries = async (array, callback, thisArg) => {
|
|
116
|
-
for (let i = 0; i < array.length; i++) {
|
|
117
|
-
if (await callback.call(thisArg || this, await array[i], i, array)) {
|
|
118
|
-
return array[i];
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
/**
|
|
123
|
-
* Implements ES5 [`Array#findIndex()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) method.<br><br>
|
|
124
|
-
* Returns the index of the element that satisfies the provided `callback`. The index returned is the one found first.<br>
|
|
125
|
-
* Callbacks are run concurrently, meaning that all the callbacks are going to run even if the returned index is found in one of the first elements of `array`,
|
|
126
|
-
* depending on the async calls you are going to use, consider using instead [`findSeries()`](global.html#findSeries).<br>
|
|
127
|
-
* @param {Array} array - Array to iterate over.
|
|
128
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
129
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
130
|
-
* @return {Promise} - Returns a Promise with the index that passed the test as value, otherwise *-1*.
|
|
131
|
-
*/
|
|
132
|
-
export const findIndex = (array, callback, thisArg) => {
|
|
133
|
-
return new Promise((resolve, reject) => {
|
|
134
|
-
if (array.length === 0) {
|
|
135
|
-
return resolve(-1);
|
|
136
|
-
}
|
|
137
|
-
let counter = 1;
|
|
138
|
-
for (let i = 0; i < array.length; i++) {
|
|
139
|
-
const check = (found) => {
|
|
140
|
-
if (found) {
|
|
141
|
-
resolve(i);
|
|
142
|
-
}
|
|
143
|
-
else if (counter === array.length) {
|
|
144
|
-
resolve(-1);
|
|
145
|
-
}
|
|
146
|
-
counter++;
|
|
147
|
-
};
|
|
148
|
-
Promise.resolve(array[i])
|
|
149
|
-
.then((elem) => callback.call(thisArg || this, elem, i, array))
|
|
150
|
-
.then(check)
|
|
151
|
-
.catch(reject);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* Same functionality as [`findIndex()`](global.html#findIndex), but runs only one callback at a time.
|
|
157
|
-
* @param {Array} array - Array to iterate over.
|
|
158
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
159
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
160
|
-
* @return {Promise} - Returns a Promise with the index that passed the test, otherwise *-1*.
|
|
161
|
-
*/
|
|
162
|
-
export const findIndexSeries = async (array, callback, thisArg) => {
|
|
163
|
-
for (let i = 0; i < array.length; i++) {
|
|
164
|
-
if (await callback.call(thisArg || this, await array[i], i, array)) {
|
|
165
|
-
return i;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
/**
|
|
170
|
-
* Implements ES5 [`Array#some()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) method.<br><br>
|
|
171
|
-
* Test if some element in `array` passes the test implemented in `callback`.<br>
|
|
172
|
-
* Callbacks are run concurrently, meaning that all the callbacks are going to run even if some of the first elements pass the test,
|
|
173
|
-
* depending on the async calls you are going to use, consider using instead [`someSeries()`](global.html#someSeries).<br>
|
|
174
|
-
* @param {Array} array - Array to iterate over.
|
|
175
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
176
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
177
|
-
* @return {Promise} - Returns a Promise with *true* as value if some element passed the test, otherwise *false*.
|
|
178
|
-
*/
|
|
179
|
-
export const some = (array, callback, thisArg) => {
|
|
180
|
-
return new Promise((resolve, reject) => {
|
|
181
|
-
if (array.length === 0) {
|
|
182
|
-
return resolve(false);
|
|
183
|
-
}
|
|
184
|
-
let counter = 1;
|
|
185
|
-
for (let i = 0; i < array.length; i++) {
|
|
186
|
-
if (!(i in array)) {
|
|
187
|
-
counter++;
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
const check = (found) => {
|
|
191
|
-
if (found) {
|
|
192
|
-
resolve(true);
|
|
193
|
-
}
|
|
194
|
-
else if (counter === array.length) {
|
|
195
|
-
resolve(false);
|
|
196
|
-
}
|
|
197
|
-
counter++;
|
|
198
|
-
};
|
|
199
|
-
Promise.resolve(array[i])
|
|
200
|
-
.then((elem) => callback.call(thisArg || this, elem, i, array))
|
|
201
|
-
.then(check)
|
|
202
|
-
.catch(reject);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
};
|
|
206
|
-
/**
|
|
207
|
-
* Same functionality as [`some()`](global.html#some), but runs only one callback at a time.
|
|
208
|
-
* @param {Array} array - Array to iterate over.
|
|
209
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
210
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
211
|
-
* @return {Promise} - Returns a Promise with *true* as value if some element passed the test, otherwise *false*.
|
|
212
|
-
*/
|
|
213
|
-
export const someSeries = async (array, callback, thisArg) => {
|
|
214
|
-
for (let i = 0; i < array.length; i++) {
|
|
215
|
-
if (i in array && await callback.call(thisArg || this, await array[i], i, array)) {
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return false;
|
|
220
|
-
};
|
|
221
|
-
/**
|
|
222
|
-
* Implements ES5 [`Array#every()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every) method.<br><br>
|
|
223
|
-
* Test if all elements in `array` pass the test implemented in `callback`.<br>
|
|
224
|
-
* Callbacks are run concurrently, meaning that all the callbacks are going to run even if any of the first elements do not pass the test,
|
|
225
|
-
* depending on the async calls you are going to use, consider using instead [`everySeries()`](global.html#everySeries).<br>
|
|
226
|
-
* @param {Array} array - Array to iterate over.
|
|
227
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
228
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
229
|
-
* @return {Promise} - Returns a Promise with *true* as value if all elements passed the test, otherwise *false*.
|
|
230
|
-
*/
|
|
231
|
-
export const every = (array, callback, thisArg) => {
|
|
232
|
-
return new Promise((resolve, reject) => {
|
|
233
|
-
if (array.length === 0) {
|
|
234
|
-
return resolve(true);
|
|
235
|
-
}
|
|
236
|
-
let counter = 1;
|
|
237
|
-
for (let i = 0; i < array.length; i++) {
|
|
238
|
-
if (!(i in array)) {
|
|
239
|
-
counter++;
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
const check = (found) => {
|
|
243
|
-
if (!found) {
|
|
244
|
-
resolve(false);
|
|
245
|
-
}
|
|
246
|
-
else if (counter === array.length) {
|
|
247
|
-
resolve(true);
|
|
248
|
-
}
|
|
249
|
-
counter++;
|
|
250
|
-
};
|
|
251
|
-
Promise.resolve(array[i])
|
|
252
|
-
.then((elem) => callback.call(thisArg || this, elem, i, array))
|
|
253
|
-
.then(check)
|
|
254
|
-
.catch(reject);
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
};
|
|
258
|
-
/**
|
|
259
|
-
* Same functionality as [`every()`](global.html#every), but runs only one callback at a time.<br><br>
|
|
260
|
-
* @param {Array} array - Array to iterate over.
|
|
261
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
262
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
263
|
-
* @return {Promise} - Returns a Promise with *true* as value if all elements passed the test, otherwise *false*.
|
|
264
|
-
*/
|
|
265
|
-
export const everySeries = async (array, callback, thisArg) => {
|
|
266
|
-
for (let i = 0; i < array.length; i++) {
|
|
267
|
-
if (i in array && !await callback.call(thisArg || this, await array[i], i, array)) {
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return true;
|
|
272
|
-
};
|
|
273
|
-
/**
|
|
274
|
-
* Implements ES5 [`Array#filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) method.<br><br>
|
|
275
|
-
* Creates a new array with the elements that passed the test implemented in `callback`.<br>
|
|
276
|
-
* Callbacks are run concurrently.<br>
|
|
277
|
-
* @param {Array} array - Array to iterate over.
|
|
278
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
279
|
-
* @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
|
280
|
-
* @return {Promise} - Returns a Promise with the resultant filtered *Array* as value.
|
|
281
|
-
*/
|
|
282
|
-
export const filter = (array, callback, thisArg) => {
|
|
283
|
-
/* two loops are necessary in order to do the filtering concurrently
|
|
284
|
-
* while keeping the order of the elements
|
|
285
|
-
* (if you find a better way to do it please send a PR!)
|
|
286
|
-
*/
|
|
287
|
-
return new Promise((resolve, reject) => {
|
|
288
|
-
const promiseArray = [];
|
|
289
|
-
for (let i = 0; i < array.length; i++) {
|
|
290
|
-
if (i in array) {
|
|
291
|
-
promiseArray[i] = Promise.resolve(array[i]).then((currentValue) => {
|
|
292
|
-
return callback.call(thisArg || this, currentValue, i, array);
|
|
293
|
-
}).catch(reject);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return Promise.all(promiseArray.map(async (p, i) => {
|
|
297
|
-
if (await p) {
|
|
298
|
-
return await array[i];
|
|
299
|
-
}
|
|
300
|
-
return undefined;
|
|
301
|
-
})).then((result) => result.filter((val) => typeof val !== 'undefined')).then(resolve, reject);
|
|
302
|
-
});
|
|
303
|
-
};
|
|
304
|
-
/**
|
|
305
|
-
* Same functionality as [`filter()`](global.html#filter), but runs only one callback at a time.
|
|
306
|
-
* @param {Array} array - Array to iterate over.
|
|
307
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
|
308
|
-
* @return {Promise} - Returns a Promise with the resultant filtered *Array* as value.
|
|
309
|
-
*/
|
|
310
|
-
export const filterSeries = async (array, callback, thisArg) => {
|
|
311
|
-
const result = [];
|
|
312
|
-
for (let i = 0; i < array.length; i++) {
|
|
313
|
-
if (i in array && await callback.call(thisArg || this, await array[i], i, array)) {
|
|
314
|
-
result.push(await array[i]);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return result;
|
|
318
|
-
};
|
|
319
|
-
/**
|
|
320
|
-
* Implements ES5 [`Array#reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) method.<br><br>
|
|
321
|
-
* Applies a `callback` against an accumulator and each element in `array`.
|
|
322
|
-
* @param {Array} array - Array to iterate over.
|
|
323
|
-
* @param {Function} callback - Function to apply each item in `array`. Accepts four arguments: `accumulator`, `currentValue`, `currentIndex` and `array`.
|
|
324
|
-
* @param {Object} [initialValue] - Used as first argument to the first call of `callback`.
|
|
325
|
-
* @return {Promise} - Returns a Promise with the resultant value from the reduction.
|
|
326
|
-
*/
|
|
327
|
-
export const reduce = async (array, callback, initialValue) => {
|
|
328
|
-
if (array.length === 0 && initialValue === undefined) {
|
|
329
|
-
throw TypeError('Reduce of empty array with no initial value');
|
|
330
|
-
}
|
|
331
|
-
let i;
|
|
332
|
-
let previousValue;
|
|
333
|
-
if (initialValue !== undefined) {
|
|
334
|
-
previousValue = initialValue;
|
|
335
|
-
i = 0;
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
previousValue = array[0];
|
|
339
|
-
i = 1;
|
|
340
|
-
}
|
|
341
|
-
for (i; i < array.length; i++) {
|
|
342
|
-
if (i in array) {
|
|
343
|
-
previousValue = await callback(await previousValue, await array[i], i, array);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return previousValue;
|
|
347
|
-
};
|