mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: remove operations for now (#9899)
This commit is contained in:
parent
5f72c8bac1
commit
976684e4e1
@ -1,153 +0,0 @@
|
||||
/**
|
||||
* Copyright 2023 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type {Awaitable} from './types.js';
|
||||
|
||||
/**
|
||||
* Operations are promises that can have <a
|
||||
* href="https://en.wikipedia.org/wiki/Side_effect_(computer_science)">effects</a>
|
||||
* added on them (through {@link Operation.effect}).
|
||||
*
|
||||
* Semantically-speaking, adding an effect equates to guaranteeing the operation
|
||||
* causes the added effect.
|
||||
*
|
||||
* The first effect that errors will propogate its error back to the operation.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* await input.click().effect(async () => {
|
||||
* await page.waitForNavigation();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Adding effects to a completed operation will result in an error. This occurs
|
||||
* when either
|
||||
*
|
||||
* 1. the effects are added asynchronously or
|
||||
* 2. the operation was awaited before effects were added.
|
||||
*
|
||||
* For example for (1),
|
||||
*
|
||||
* ```ts
|
||||
* const operation = input.click();
|
||||
* await new Promise(resolve => setTimeout(resolve, 100));
|
||||
* await operation.effect(() => console.log('Works!')); // This will throw because of (1).
|
||||
* ```
|
||||
*
|
||||
* For example for (2),
|
||||
*
|
||||
* ```ts
|
||||
* const operation = await input.click();
|
||||
* await operation.effect(() => console.log('Works!')); // This will throw because of (2).
|
||||
* ```
|
||||
*
|
||||
* Tl;dr, effects **must** be added synchronously (no `await` statements between
|
||||
* the time the operation is created and the effect is added).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class Operation<T> extends Promise<T> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
static create<T>(fn: () => Awaitable<T>, delay = 0): Operation<T> {
|
||||
return new Operation((resolve, reject) => {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
resolve(await fn());
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}, delay);
|
||||
});
|
||||
}
|
||||
|
||||
#settled = false;
|
||||
#effects: Array<Awaitable<unknown>> = [];
|
||||
#error?: unknown;
|
||||
|
||||
/**
|
||||
* Adds the given effect.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* await input.click().effect(async () => {
|
||||
* await page.waitForNavigation();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param effect - The effect to add.
|
||||
* @returns `this` for chaining.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
effect(effect: () => Awaitable<void>): this {
|
||||
if (this.#settled) {
|
||||
throw new Error(
|
||||
'Attempted to add effect to a completed operation. Make sure effects are added synchronously after the operation is created.'
|
||||
);
|
||||
}
|
||||
this.#effects.push(
|
||||
(async () => {
|
||||
try {
|
||||
return await effect();
|
||||
} catch (error) {
|
||||
// Note we can't just push a rejected promise to #effects. This is because
|
||||
// all rejections must be handled somewhere up in the call stack and since
|
||||
// this function is synchronous, it is not handled anywhere in the call
|
||||
// stack.
|
||||
this.#error = error;
|
||||
}
|
||||
})()
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
get #effectsPromise(): Promise<unknown> {
|
||||
if (this.#error) {
|
||||
return Promise.reject(this.#error);
|
||||
}
|
||||
return Promise.all(this.#effects);
|
||||
}
|
||||
|
||||
override then<TResult1 = T, TResult2 = never>(
|
||||
onfulfilled?: (value: T) => TResult1 | PromiseLike<TResult1>,
|
||||
onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>
|
||||
): Operation<TResult1 | TResult2> {
|
||||
return super.then(
|
||||
value => {
|
||||
this.#settled = true;
|
||||
return this.#effectsPromise.then(() => {
|
||||
if (!onfulfilled) {
|
||||
return value;
|
||||
}
|
||||
return onfulfilled(value);
|
||||
}, onrejected);
|
||||
},
|
||||
reason => {
|
||||
this.#settled = true;
|
||||
if (!onrejected) {
|
||||
throw reason;
|
||||
}
|
||||
return onrejected(reason);
|
||||
}
|
||||
) as Operation<TResult1 | TResult2>;
|
||||
}
|
||||
}
|
@ -55,7 +55,6 @@ export * from './PredefinedNetworkConditions.js';
|
||||
export * from './Product.js';
|
||||
export * from './Puppeteer.js';
|
||||
export * from './PuppeteerViewport.js';
|
||||
export * from './Operation.js';
|
||||
export * from './SecurityDetails.js';
|
||||
export * from './Target.js';
|
||||
export * from './TargetManager.js';
|
||||
|
@ -1,234 +0,0 @@
|
||||
/**
|
||||
* Copyright 2023 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import {Operation} from 'puppeteer-core/internal/common/Operation.js';
|
||||
|
||||
describe('Operation', () => {
|
||||
it('should work', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
await Operation.create(() => {
|
||||
values.push(1);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(2);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(3);
|
||||
});
|
||||
|
||||
expect(values).toEqual([2, 3, 1]);
|
||||
});
|
||||
|
||||
it('should work with error on operation', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
let errored: string | undefined;
|
||||
try {
|
||||
await Operation.create(() => {
|
||||
throw new Error('test');
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(1);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(2);
|
||||
});
|
||||
} catch (error) {
|
||||
errored = (error as Error).message;
|
||||
}
|
||||
|
||||
expect(errored).toBe('test');
|
||||
expect(values).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('should work with error on effect', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
let errored: string | undefined;
|
||||
try {
|
||||
await Operation.create(() => {
|
||||
values.push(1);
|
||||
})
|
||||
.effect(() => {
|
||||
throw new Error('test');
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(2);
|
||||
});
|
||||
} catch (error) {
|
||||
errored = (error as Error).message;
|
||||
}
|
||||
|
||||
expect(errored).toBe('test');
|
||||
expect(values).toEqual([2, 1]);
|
||||
});
|
||||
|
||||
it('should work with error on both operation and effect', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
let errored: string | undefined;
|
||||
try {
|
||||
await Operation.create(() => {
|
||||
throw new Error('test1');
|
||||
})
|
||||
.effect(() => {
|
||||
throw new Error('test2');
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(1);
|
||||
});
|
||||
} catch (error) {
|
||||
errored = (error as Error).message;
|
||||
}
|
||||
|
||||
expect(errored).toBe('test1');
|
||||
expect(values).toEqual([1]);
|
||||
});
|
||||
|
||||
it('should work with delayed error on operation', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
let errored: string | undefined;
|
||||
try {
|
||||
await Operation.create(() => {
|
||||
return new Promise((_, reject) => {
|
||||
return setTimeout(() => {
|
||||
reject(new Error('test1'));
|
||||
}, 10);
|
||||
});
|
||||
})
|
||||
.effect(() => {
|
||||
throw new Error('test2');
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(1);
|
||||
});
|
||||
} catch (error) {
|
||||
errored = (error as Error).message;
|
||||
}
|
||||
|
||||
expect(errored).toBe('test1');
|
||||
expect(values).toEqual([1]);
|
||||
});
|
||||
|
||||
it('should work with async error on effects', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
let errored: string | undefined;
|
||||
try {
|
||||
await Operation.create(() => {
|
||||
values.push(1);
|
||||
})
|
||||
.effect(async () => {
|
||||
throw new Error('test');
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(2);
|
||||
});
|
||||
} catch (error) {
|
||||
errored = (error as Error).message;
|
||||
}
|
||||
|
||||
expect(errored).toBe('test');
|
||||
expect(values).toEqual([2, 1]);
|
||||
});
|
||||
|
||||
it('should work with then', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
const operation = Operation.create(() => {
|
||||
values.push(1);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(2);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(3);
|
||||
})
|
||||
.then(() => {
|
||||
values.push(4);
|
||||
});
|
||||
await operation;
|
||||
|
||||
expect(operation).toBeInstanceOf(Operation);
|
||||
expect(values).toEqual([2, 3, 1, 4]);
|
||||
});
|
||||
|
||||
it('should work with catch', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
const operation = Operation.create(() => {
|
||||
throw new Error('test');
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(1);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(2);
|
||||
})
|
||||
.catch(() => {
|
||||
values.push(3);
|
||||
});
|
||||
await operation;
|
||||
|
||||
expect(operation).toBeInstanceOf(Operation);
|
||||
expect(values).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should work with finally', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
const operation = Operation.create(() => {
|
||||
values.push(1);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(2);
|
||||
})
|
||||
.effect(() => {
|
||||
values.push(3);
|
||||
})
|
||||
.finally(() => {
|
||||
values.push(4);
|
||||
});
|
||||
await operation;
|
||||
|
||||
expect(operation).toBeInstanceOf(Operation);
|
||||
expect(values).toEqual([2, 3, 1, 4]);
|
||||
});
|
||||
|
||||
it('should throw when adding effects on on awaited operation', async () => {
|
||||
const values: number[] = [];
|
||||
|
||||
const operation = Operation.create(() => {
|
||||
values.push(1);
|
||||
});
|
||||
await operation;
|
||||
|
||||
expect(() => {
|
||||
operation.effect(() => {
|
||||
values.push(2);
|
||||
});
|
||||
}).toThrowError(
|
||||
new Error(
|
||||
'Attempted to add effect to a completed operation. Make sure effects are added synchronously after the operation is created.'
|
||||
)
|
||||
);
|
||||
expect(operation).toBeInstanceOf(Operation);
|
||||
expect(values).toEqual([1]);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user