Skip to content

Instantly share code, notes, and snippets.

@savagematt
Created August 7, 2019 13:59
Show Gist options
  • Save savagematt/1ce198dd0f3f836276d20cc1f194111c to your computer and use it in GitHub Desktop.
Save savagematt/1ce198dd0f3f836276d20cc1f194111c to your computer and use it in GitHub Desktop.
scenario vars
export interface Vars<T> {
/**
* Returns a step that assigns the result of step to key k.
*
* Always returns void, so if the step returned a promise set() won't block.
*
* You can:
*
* // will NOT block on stepThatCompletesEventually return value
* set('value',stepThatCompletesEventually),
* doSomeOtherStuffThatCausesThePromiseToResolve(),
* // will block on stepThatCompletesEventually return value
* get('value'),
*
* Values of keys are immutable.
*/
set<C, K extends keyof T>(k: K, step: Step<C, T[K]>): Step<C, void>;
/**
* Returns a step that gets the value of key k, waiting for it to resolve
* if it is a promise.
*
* Throws exception if k has not been set.
*/
get<C, K extends keyof T>(k: K): Step<C, Promise<T[K]>>;
}
export function vars<T, C = any>(): Vars<T> {
const values: Partial<T> = {};
return {
set: <C, K extends keyof T>(k: K, step: Step<C, T[K]>): Step<C, void> => {
return (c: C): void => {
if (k in values)
throw new Error(`'${k} already set to ${JSON.stringify(values[k])}'`);
values[k] = execute(c, step);
return
}
},
get: <C, K extends keyof T>(k: K): Step<C, Promise<T[K]>> => {
return async (c: C): Promise<T[K]> => {
if (!(k in values))
throw new Error(`'${k} has not been set`);
return await values[k] as T[K];
}
}
}
}
@savagematt
Copy link
Author

const {get,set} = vars<{login:LoginStep}>();

scenario(context,
  set('login', login("bob","bobspassword")), // login blocks waiting for confirmation in some app
  confirmTwoFactorRequest(),
  get('login') // wait for login to complete
)

@savagematt
Copy link
Author

Also good for storing values that aren't known at runtime:

interface UserArgs {
  name: string;
}
/**
* user that's been created in the database
**/
interface User extends UserArgs {
  id: number; // auto-increment
}
function createUser(args:UserArgs) : Step<Promise<User>> {
   return (context:ScenarioContext) : Promise<User> {
     return context.createUser(args);
  }
}
function createNote(text:string, user:Promise<User>) : Step<Promise<Note>> {
   return async (context:ScenarioContext) : Promise<Note> {
     return context.createNote({text, userId: (await user).id});
  }
}

const {get,set} = vars<{user:Promise<User>}>();
scenario(context,
  set('user', createUser({name:"bob"})), // we don't know User.id until the step is complete
  createNote("buy some milk", get('user')) // retrieves the User created in the previous step
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment