🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more →

poll-until-promise

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

poll-until-promise - npm Package Compare versions

Comparing version

to
4.2.0

@@ -1,2 +0,4 @@

import { IWaitForOptions, PollUntil, waitFor } from '../src';
import {
AbortError, IWaitForOptions, PollUntil, waitFor,
} from '../src';

@@ -181,20 +183,66 @@ describe('Unit: Wait Until Factory', () => {

it('should fail wait after timeout', (done) => {
it('should fail wait after timeout when stopOnFailure is false', async () => {
const pollUntil = new PollUntil();
shouldHaltPromiseResolve = true;
shouldRejectAfterHalt = true;
const errorContent = 'error abcdefg';
const specificFailedError = new Error(errorContent);
pollUntil
.tryEvery(1)
.stopAfter(3000)
.stopOnFailure(false)
.execute(() => Promise.reject(specificFailedError))
.catch((error) => {
expect(error.message).toContain('Failed to wait');
expect(error.message).toContain(errorContent);
done();
});
const mockPromise = jest.fn(() => Promise.reject(specificFailedError));
expect.assertions(3);
try {
await pollUntil
.tryEvery(1)
.stopAfter(50)
.stopOnFailure(false)
.execute(mockPromise);
} catch (err) {
const error = err as Error;
expect(error.message).toContain('Failed to wait');
expect(error.message).toContain(errorContent);
expect(mockPromise.mock.calls.length).toBeGreaterThan(1);
}
});
it('should fail immediately for AbortError when stopOnFailure is false -- passing Error arg', async () => {
const pollUntil = new PollUntil();
const errorContent = 'error abcdefg';
const specificFailedError = new Error(errorContent);
const abortError = new AbortError(specificFailedError);
const mockPromise = jest.fn(() => Promise.reject(abortError));
expect.assertions(3);
try {
await pollUntil
.tryEvery(1)
.stopAfter(50)
.stopOnFailure(false)
.execute(mockPromise);
} catch (err) {
const error = err as Error;
expect(error.message).not.toContain('Failed to wait');
expect(error.message).toContain(errorContent);
expect(mockPromise.mock.calls.length).toBe(1);
}
});
it('should fail immediately for AbortError when stopOnFailure is false -- passing string arg', async () => {
const pollUntil = new PollUntil();
const errorContent = 'error abcdefg';
const abortError = new AbortError(errorContent);
const mockPromise = jest.fn(() => Promise.reject(abortError));
expect.assertions(3);
try {
await pollUntil
.tryEvery(1)
.stopAfter(50)
.stopOnFailure(false)
.execute(mockPromise);
} catch (err) {
const error = err as Error;
expect(error.message).not.toContain('Failed to wait');
expect(error.message).toContain(errorContent);
expect(mockPromise.mock.calls.length).toBe(1);
}
});
it('should execute a second waiting when waiting is done (exceeded timeout) but not resolved', (done) => {

@@ -283,3 +331,3 @@ const pollUntil = new PollUntil();

} catch (e: Error | any) {
expect(e.message).toMatch(/Failed to wait after \d+ms: waiting for something\nFailed to wait after \d+ms: waiting for another thing/);
expect(e.message).toMatch(/Failed to wait after \d+ms \(total of \d+ attempts\): waiting for something\nFailed to wait after \d+ms \(total of \d+ attempts\): waiting for another thing/);
expect(e.stack).toMatch(/alon/);

@@ -299,3 +347,3 @@ }

}
expect(error?.message).toMatch(/^Failed to wait after \d+ms: waiting for something\nsome error message$/);
expect(error?.message).toMatch(/^Failed to wait after \d+ms \(total of \d+ attempts\): waiting for something\nsome error message$/);
});

@@ -314,3 +362,3 @@

}
expect(error?.message).toMatch(/^Failed to wait after \d+ms: waiting for something$/);
expect(error?.message).toMatch(/^Failed to wait after \d+ms \(total of \d+ attempts\): waiting for something$/);
expect(error?.stack).toMatch(/customFunction/);

@@ -396,2 +444,23 @@ });

});
it('should fail if max attempts exceeded', async () => {
const pollUntil = new PollUntil({ maxAttempts: 3, interval: 1 });
const error = new Error('whoops');
const mockPromise = jest.fn().mockRejectedValue(error);
await expect(pollUntil.execute(mockPromise)).rejects.toThrow(/Operation unsuccessful after 3 attempts \(total of \d+ms\)\nwhoops/);
});
it('should not fail if max attempts not exceeded', async () => {
const pollUntil = new PollUntil({ maxAttempts: 3, interval: 1 });
const error = new Error('whoops');
const mockPromise = jest.fn()
.mockRejectedValueOnce(error)
.mockRejectedValueOnce(error)
.mockResolvedValue({ fake: 'result' });
expect(await pollUntil.execute(mockPromise)).toEqual({ fake: 'result' });
});
});

@@ -17,2 +17,3 @@ module.exports = {

"@typescript-eslint/lines-between-class-members" : 0,
"import/prefer-default-export": 0,
"no-underscore-dangle": 0,

@@ -19,0 +20,0 @@ "max-len": 0

export { IWaitForOptions, PollUntil, waitFor, IExecuteFunction, } from './poll-until-promise';
export { AbortError } from './abort';
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.waitFor = exports.PollUntil = void 0;
exports.AbortError = exports.waitFor = exports.PollUntil = void 0;
var poll_until_promise_1 = require("./poll-until-promise");
Object.defineProperty(exports, "PollUntil", { enumerable: true, get: function () { return poll_until_promise_1.PollUntil; } });
Object.defineProperty(exports, "waitFor", { enumerable: true, get: function () { return poll_until_promise_1.waitFor; } });
var abort_1 = require("./abort");
Object.defineProperty(exports, "AbortError", { enumerable: true, get: function () { return abort_1.AbortError; } });

@@ -1,2 +0,2 @@

export declare type IExecuteFunction = any;
export type IExecuteFunction = any;
export interface IWaitForOptions {

@@ -10,2 +10,3 @@ timeout?: number;

verbose?: boolean;
maxAttempts?: number;
}

@@ -15,2 +16,3 @@ export declare class PollUntil {

_timeout: number;
_executedAttempts: number;
private _stopOnFailure;

@@ -23,2 +25,3 @@ private readonly _backoffFactor;

private readonly _verbose;
private readonly _maxAttempts;
private _isWaiting;

@@ -32,3 +35,3 @@ private _isResolved;

private _lastError;
constructor({ interval, timeout, stopOnFailure, verbose, backoffFactor, backoffMaxInterval, message, }?: IWaitForOptions);
constructor({ interval, timeout, stopOnFailure, verbose, backoffFactor, backoffMaxInterval, message, maxAttempts, }?: IWaitForOptions);
tryEvery(interval: number): PollUntil;

@@ -44,2 +47,3 @@ stopAfter(timeout: number): PollUntil;

_shouldStopTrying(): boolean;
_attemptsExhausted(): boolean;
_executeAgain(): void;

@@ -50,2 +54,2 @@ _failedToWait(): Error;

}
export declare const waitFor: (waitForFunction: IExecuteFunction, options?: IWaitForOptions | undefined) => Promise<any>;
export declare const waitFor: (waitForFunction: IExecuteFunction, options?: IWaitForOptions) => Promise<any>;

@@ -13,2 +13,3 @@ "use strict";

exports.waitFor = exports.PollUntil = void 0;
const abort_1 = require("./abort");
const ERRORS = {

@@ -30,5 +31,6 @@ NOT_FUNCTION: 'Your executor is not a function. functions and promises are valid.',

class PollUntil {
constructor({ interval = 100, timeout = 1000, stopOnFailure = false, verbose = false, backoffFactor = 1, backoffMaxInterval, message = '', } = {}) {
constructor({ interval = 100, timeout = 1000, stopOnFailure = false, verbose = false, backoffFactor = 1, backoffMaxInterval, message = '', maxAttempts, } = {}) {
this._interval = interval;
this._timeout = timeout;
this._executedAttempts = 0;
this._stopOnFailure = stopOnFailure;

@@ -43,2 +45,3 @@ this._isWaiting = false;

this._backoffMaxInterval = backoffMaxInterval || timeout;
this._maxAttempts = maxAttempts;
this.start = +Date.now();

@@ -87,4 +90,7 @@ }

_shouldStopTrying() {
return this._timeFromStart() > this._timeout;
return this._timeFromStart() > this._timeout || this._attemptsExhausted();
}
_attemptsExhausted() {
return this._maxAttempts !== undefined && this._executedAttempts >= this._maxAttempts;
}
_executeAgain() {

@@ -95,6 +101,10 @@ this._log('executing again');

this._interval = (nextInterval > this._backoffMaxInterval) ? this._backoffMaxInterval : nextInterval;
this._executedAttempts += 1;
setTimeout(this._runFunction.bind(this), currentInterval);
}
_failedToWait() {
let waitErrorText = `${ERRORS.FAILED_TO_WAIT} after ${this._timeFromStart()}ms`;
const timeFromStartStr = `${this._timeFromStart()}ms`;
let waitErrorText = this._attemptsExhausted()
? `Operation unsuccessful after ${this._executedAttempts} attempts (total of ${timeFromStartStr})`
: `${ERRORS.FAILED_TO_WAIT} after ${timeFromStartStr} (total of ${this._executedAttempts} attempts)`;
if (this._userMessage)

@@ -137,6 +147,10 @@ waitErrorText = `${waitErrorText}: ${this._userMessage}`;

.catch((err) => {
var _a;
var _a, _b;
if (err instanceof abort_1.AbortError) {
this._log(`aborted with err: ${err.cause}`);
return (_a = this.reject) === null || _a === void 0 ? void 0 : _a.call(this, err.cause);
}
if (this._stopOnFailure) {
this._log(`stopped on failure with err: ${err}`);
return (_a = this.reject) === null || _a === void 0 ? void 0 : _a.call(this, err);
return (_b = this.reject) === null || _b === void 0 ? void 0 : _b.call(this, err);
}

@@ -143,0 +157,0 @@ this._lastError = err;

{
"name": "poll-until-promise",
"version": "4.1.0",
"version": "4.2.0",
"description": "Try repeatedly for a promise to be resolved",

@@ -22,6 +22,5 @@ "main": "lib/index.js",

"test": "jest",
"test:coverage": "jest --coverage && cat ./coverage/lcov.info | coveralls",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run lint",
"release": "npm run lint && npm run test:coverage && npm run build",
"release": "npm run lint && npm run && npm run build",
"preversion": "npm run lint",

@@ -56,3 +55,2 @@ "version": "npm run lint && git add -A src",

"@typescript-eslint/parser": "^5.8.0",
"coveralls": "^3.1.1",
"eslint": "^8.5.0",

@@ -64,3 +62,2 @@ "eslint-config-airbnb-base": "^15.0.0",

"jest": "^27.4.5",
"nyc": "^15.1.0",
"ts-jest": "^27.1.2",

@@ -71,19 +68,3 @@ "typescript": "^4.5.4",

"ts-loader": "^9.2.6"
},
"nyc": {
"reporter": [
"text",
"text-summary",
"lcov",
"html"
],
"include": [
"src/**/*.js"
],
"require": [
"babel-register"
],
"sourceMap": false,
"instrument": true
}
}
}

@@ -1,4 +0,3 @@

[![Build Status][travis-image]][travis-url]
[![Coverage Status][coveralls-image]][coveralls-url]
[![NPM Version][npm-image]][npm-url]
![Build](https://github.com/AlonMiz/poll-until-promise/actions/workflows/nodejs.yml/badge.svg)

@@ -29,3 +28,3 @@

```js
import { waitFor } from 'poll-until-promise';
import { waitFor, AbortError } from 'poll-until-promise';

@@ -39,3 +38,3 @@ async function waitForDomElement(cssSelector = 'div') {

}, { timeout: 60_000 });
return element;

@@ -52,2 +51,8 @@ } catch (e) {

const res = await fetch(path);
// Stop immediately if the resource doesn't exist
if (res.status === 404) {
throw new AbortError(res.statusText);
}
return res.json();

@@ -112,2 +117,3 @@ }, { timeout: 60_000, interval: 1000 });

message: 'Waiting for time to pass :)', // custom message to display on failure
maxAttempts: 5, // maximum attempts to make (default: no limit). Will still fail to wait if reaching timeout before attempts exhausted
};

@@ -156,9 +162,3 @@ let pollUntilPromise = new PollUntil(options);

[travis-url]: https://travis-ci.org/AlonMiz/poll-until-promise
[travis-image]: https://travis-ci.org/AlonMiz/poll-until-promise.svg?branch=master
[npm-url]: https://npmjs.org/package/poll-until-promise
[npm-image]: https://img.shields.io/npm/v/poll-until-promise.svg
[coveralls-url]: https://coveralls.io/github/AlonMiz/poll-until-promise
[coveralls-image]: https://img.shields.io/coveralls/AlonMiz/poll-until-promise.svg
export {
IWaitForOptions, PollUntil, waitFor, IExecuteFunction,
} from './poll-until-promise';
export { AbortError } from './abort';

@@ -0,1 +1,3 @@

import { AbortError } from './abort';
const ERRORS = {

@@ -29,2 +31,3 @@ NOT_FUNCTION: 'Your executor is not a function. functions and promises are valid.',

verbose?: boolean
maxAttempts?: number
}

@@ -35,2 +38,3 @@

_timeout: number;
_executedAttempts: number;
private _stopOnFailure: boolean;

@@ -43,2 +47,3 @@ private readonly _backoffFactor: number;

private readonly _verbose: boolean;
private readonly _maxAttempts: number | undefined;
private _isWaiting: boolean;

@@ -61,5 +66,7 @@ private _isResolved: boolean;

message = '',
maxAttempts,
}:IWaitForOptions = {}) {
this._interval = interval;
this._timeout = timeout;
this._executedAttempts = 0;
this._stopOnFailure = stopOnFailure;

@@ -74,2 +81,3 @@ this._isWaiting = false;

this._backoffMaxInterval = backoffMaxInterval || timeout;
this._maxAttempts = maxAttempts;
this.start = +Date.now();

@@ -131,5 +139,9 @@ }

_shouldStopTrying() {
return this._timeFromStart() > this._timeout;
return this._timeFromStart() > this._timeout || this._attemptsExhausted();
}
_attemptsExhausted() {
return this._maxAttempts !== undefined && this._executedAttempts >= this._maxAttempts;
}
_executeAgain() {

@@ -140,2 +152,3 @@ this._log('executing again');

this._interval = (nextInterval > this._backoffMaxInterval) ? this._backoffMaxInterval : nextInterval;
this._executedAttempts += 1;
setTimeout(this._runFunction.bind(this), currentInterval);

@@ -145,3 +158,6 @@ }

_failedToWait() {
let waitErrorText = `${ERRORS.FAILED_TO_WAIT} after ${this._timeFromStart()}ms`;
const timeFromStartStr = `${this._timeFromStart()}ms`;
let waitErrorText = this._attemptsExhausted()
? `Operation unsuccessful after ${this._executedAttempts} attempts (total of ${timeFromStartStr})`
: `${ERRORS.FAILED_TO_WAIT} after ${timeFromStartStr} (total of ${this._executedAttempts} attempts)`;
if (this._userMessage) waitErrorText = `${waitErrorText}: ${this._userMessage}`;

@@ -182,2 +198,6 @@ if (this._lastError) {

.catch((err: Error) => {
if (err instanceof AbortError) {
this._log(`aborted with err: ${err.cause}`);
return this.reject?.(err.cause);
}
if (this._stopOnFailure) {

@@ -184,0 +204,0 @@ this._log(`stopped on failure with err: ${err}`);