Skip to content

Commit

Permalink
feat!: add abort function to Runner and remove stepIdx from `ru…
Browse files Browse the repository at this point in the history
…n` (#175)

Co-authored-by: Ergün Erdoğmuş <ergunsh@chromium.org>
  • Loading branch information
ergunsh and ergunsh committed Jun 9, 2022
1 parent adb3fd0 commit 087d9ce
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 60 deletions.
44 changes: 20 additions & 24 deletions src/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { UserFlow } from './Schema.js';
export class Runner {
#flow: UserFlow;
#extension: RunnerExtension;
#aborted: boolean = false;
#nextStep = 0;

/**
Expand All @@ -31,40 +32,35 @@ export class Runner {
this.#extension = extension;
}

abort(): void {
this.#aborted = true;
}

/**
* @param stepIdx - Run the flow up until the step with the `stepIdx` index.
* Run all the steps in the flow
* @returns whether all the steps are run or the execution is aborted
*/
async run(stepIdx?: number): Promise<boolean> {
if (stepIdx === undefined) {
stepIdx = this.#flow.steps.length;
}
if (this.#nextStep === 0) {
await this.#extension.beforeAllSteps?.(this.#flow);
}
while (
this.#nextStep < stepIdx &&
this.#nextStep < this.#flow.steps.length
) {
async run(): Promise<boolean> {
this.#aborted = false;
await this.#extension.beforeAllSteps?.(this.#flow);

let nextStep = 0;
while (nextStep < this.#flow.steps.length && !this.#aborted) {
await this.#extension.beforeEachStep?.(
this.#flow.steps[this.#nextStep],
this.#flow
);
await this.#extension.runStep(
this.#flow.steps[this.#nextStep],
this.#flow.steps[nextStep],
this.#flow
);
await this.#extension.runStep(this.#flow.steps[nextStep], this.#flow);
await this.#extension.afterEachStep?.(
this.#flow.steps[this.#nextStep],
this.#flow.steps[nextStep],
this.#flow
);
this.#nextStep++;
}
if (this.#nextStep >= this.#flow.steps.length) {
await this.#extension.afterAllSteps?.(this.#flow);
return true;
nextStep++;
}

return false;
await this.#extension.afterAllSteps?.(this.#flow);

return nextStep >= this.#flow.steps.length;
}
}

Expand Down
164 changes: 128 additions & 36 deletions test/runner_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ describe('Runner', () => {
await runner.run();
});

it('should run return true when all the steps are run', async () => {
const runner = await createRunner(
{
title: 'test',
steps: [],
},
new PuppeteerRunnerExtension(browser, page)
);

const isFinished = await runner.run();

assert.isTrue(isFinished);
});

it('should navigate to the right URL', async () => {
const runner = await createRunner(
{
Expand Down Expand Up @@ -704,42 +718,6 @@ describe('Runner', () => {
await runner.run();
});

it('should run steps partially', async () => {
class DummyExtension implements RunnerExtension {
#log: string[] = [];

getLog(): string {
return this.#log.join(',');
}

async runStep(step: Step, flow: UserFlow): Promise<void> {
this.#log.push(flow.steps.indexOf(step).toString(10));
}
}
const extension = new DummyExtension();
const runner = await createRunner(
{
title: 'test',
steps: [
{ type: 'customStep', name: 'step1', parameters: {} },
{ type: 'customStep', name: 'step2', parameters: {} },
{ type: 'customStep', name: 'step3', parameters: {} },
],
},
extension
);
assert.strictEqual(await runner.run(-1), false);
assert.strictEqual(extension.getLog(), '');
assert.strictEqual(await runner.run(0), false);
assert.strictEqual(extension.getLog(), '');
assert.strictEqual(await runner.run(1), false);
assert.strictEqual(extension.getLog(), '0');
assert.strictEqual(await runner.run(3), true);
assert.strictEqual(extension.getLog(), '0,1,2');
assert.strictEqual(await runner.run(), true);
assert.strictEqual(extension.getLog(), '0,1,2');
});

it('should run all extension hooks', async () => {
class DummyExtension implements RunnerExtension {
#log: string[] = [];
Expand Down Expand Up @@ -900,4 +878,118 @@ describe('Runner', () => {
'Hovered'
);
});

describe('abort', () => {
it('should abort execution of remaining steps', async () => {
class AbortAfterFirstStepExtension extends RunnerExtension {
ranSteps = 0;
#abortFn?: Function;

setAbortFn(abortFn: Function) {
this.#abortFn = abortFn;
}

async runStep(step: Step, flow: UserFlow) {
if (flow.steps.indexOf(step) === 0) {
this.#abortFn?.();
}

await super.runStep(step, flow);
this.ranSteps++;
}
}
const extension = new AbortAfterFirstStepExtension();
const runner = await createRunner(
{
title: 'test',
steps: [
{ type: 'customStep', name: 'step1', parameters: {} },
{ type: 'customStep', name: 'step2', parameters: {} },
{ type: 'customStep', name: 'step3', parameters: {} },
],
},
extension
);
extension.setAbortFn(() => runner.abort());

await runner.run();

assert.strictEqual(extension.ranSteps, 1);
});

it('should run afterAllSteps if the execution is aborted', async () => {
class AbortAfterFirstStepExtension extends RunnerExtension {
isAfterAllStepsRan = false;
#abortFn?: Function;

setAbortFn(abortFn: Function) {
this.#abortFn = abortFn;
}

async runStep(step: Step, flow: UserFlow) {
if (flow.steps.indexOf(step) === 0) {
this.#abortFn?.();
}

await super.runStep(step, flow);
}

async afterAllSteps() {
this.isAfterAllStepsRan = true;
}
}
const extension = new AbortAfterFirstStepExtension();
const runner = await createRunner(
{
title: 'test',
steps: [
{ type: 'customStep', name: 'step1', parameters: {} },
{ type: 'customStep', name: 'step2', parameters: {} },
{ type: 'customStep', name: 'step3', parameters: {} },
],
},
extension
);
extension.setAbortFn(() => runner.abort());

await runner.run();

assert.isTrue(extension.isAfterAllStepsRan);
});

it('should return false when the execution is aborted before all the steps are executed', async () => {
class AbortAfterFirstStepExtension extends RunnerExtension {
#abortFn?: Function;

setAbortFn(abortFn: Function) {
this.#abortFn = abortFn;
}

async runStep(step: Step, flow: UserFlow) {
if (flow.steps.indexOf(step) === 0) {
this.#abortFn?.();
}

await super.runStep(step, flow);
}
}
const extension = new AbortAfterFirstStepExtension();
const runner = await createRunner(
{
title: 'test',
steps: [
{ type: 'customStep', name: 'step1', parameters: {} },
{ type: 'customStep', name: 'step2', parameters: {} },
{ type: 'customStep', name: 'step3', parameters: {} },
],
},
extension
);
extension.setAbortFn(() => runner.abort());

const isFinished = await runner.run();

assert.isFalse(isFinished);
});
});
});

0 comments on commit 087d9ce

Please sign in to comment.