-
Notifications
You must be signed in to change notification settings - Fork 451
Description
Currently the implementation for StepType.CustomStep
is missing.
Since it is a custom step, the implementation must be provided by the user.
While I can think of some approaches to this, I think the best solution is a callback property on PuppeteerRunnerExtension
. We could name it executeCustomStep
.
Someone who executes the runner can just provide their implementation by calling: puppeteerRunnerExtension.executeCustomStep = async (opts) => {/* do stuff... */};
Now the interesting question is: "What should opts
look like?"
When I provide my implementation of executeCustomStep
I should be able to do everything that all existing steps can do. The opts object should contain all parameters used by the other steps.
This leads to this API:
executeCustomStep: (opts: {
step: CustomStep;
mainPage: Page;
targetPageOrFrame: Page | Frame;
localFrame: Frame;
timeout: number;
startWaitingForEvents: () => void;
querySelectorsAll: (
selectors: Selector[]
) => Promise<ElementHandle<Element>[]>;
}) => Promise<void> = async () => {};
The first parameters are exactly the parameters used in runStepInFrame()
.
startWaitingForEvents
is used in the other steps, so I should be able to call it, when I see fit.
querySelectorsAll
is a wrapper around the querySelectorsAll
function in the PuppeteerRunnerExtension.ts
. It collects all returned Element handles. When the users implementation has finished, the Element handles are disposed.
This is what the changed switch/case in runStepInFrame()
would look like.
case StepType.CustomStep: {
const { dispose, querySelectorsAll } =
createDisposableQuerySelectorsAll(localFrame);
try {
await this.executeCustomStep({
step,
mainPage,
targetPageOrFrame,
localFrame,
timeout,
startWaitingForEvents,
querySelectorsAll,
});
} finally {
await dispose();
}
break;
}
And this is how the wrapper around querySelectorsAll
is implemented:
function createDisposableQuerySelectorsAll(localFrame: Frame): {
querySelectorsAll: (
selectors: Selector[]
) => Promise<ElementHandle<Element>[]>;
dispose: () => Promise<void>;
} {
const toDispose: ElementHandle<Element>[] = [];
async function _querySelectorsAll(
selectors: Selector[]
): Promise<ElementHandle<Element>[]> {
const elements = await querySelectorsAll(selectors, localFrame);
toDispose.push(...elements);
return elements;
}
async function dispose(): Promise<void> {
await Promise.all(toDispose.map((element) => element.dispose()));
}
return { querySelectorsAll: _querySelectorsAll, dispose };
}
What do you think?