Skip to content

Commit

Permalink
feat(testing): Create helpers for testing of ErrorResult union types
Browse files Browse the repository at this point in the history
Relates to #437
  • Loading branch information
michaelbromley committed Sep 21, 2020
1 parent 0c0a7b2 commit 6ef6045
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
81 changes: 81 additions & 0 deletions packages/testing/src/error-result-guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
declare function fail(error?: any): never;

/**
* Convenience method for creating an {@link ErrorResultGuard}. Takes a predicate function which
* tests whether the input is considered successful (true) or an error result (false).
*
* Note that the resulting variable must _still_ be type annotated in order for the TypeScript
* type inference to work as expected:
*
* @example
* ```TypeScript
* const orderResultGuard: ErrorResultGuard<AddItemToOrderResult>
* = createErrorResultGuard<AddItemToOrderResult>(order => !!order.lines);
* ```
* @docsCategory testing
*/
export function createErrorResultGuard<T>(testFn: (input: T) => boolean): ErrorResultGuard<T> {
return new ErrorResultGuard<T>(testFn);
}

/**
* @description
* A utility class which is used to assert the success of an operation
* which returns a union type of `SuccessType | ErrorResponse [ | ErrorResponse ]`.
* The methods of this class are used to:
* 1. assert that the result is a success or error case
* 2. narrow the type so that TypeScript can correctly infer the properties of the result.
*
* @example
* ```TypeScript
* const orderResultGuard: ErrorResultGuard<AddItemToOrderResult>
* = createErrorResultGuard<AddItemToOrderResult>(order => !!order.lines);
*
* it('errors when quantity is negative', async () => {
* const { addItemToOrder } = await shopClient.query<AddItemToOrder.Query, AddItemToOrder.Mutation>(ADD_ITEM_TO_ORDER, {
* productVariantId: 42, quantity: -1,
* });
*
* // The test will fail
* orderResultGuard.assertErrorResult(addItemToOrder);
*
* // the type of `addItemToOrder` has now been
* // narrowed to only include the ErrorResult types.
* expect(addItemToOrder.errorCode).toBe(ErrorCode.NegativeQuantityError);
* }
* ```
* @docsCategory testing
*/
export class ErrorResultGuard<T> {
constructor(private testFn: (input: T) => boolean) {}

/**
* @description
* A type guard which returns `true` if the input passes the `testFn` predicate.
*/
isSuccess(input: T | any): input is T {
return this.testFn(input as T);
}

/**
* @description
* Asserts (using the testing library's `fail()` function) that the input is
* successful, i.e. it passes the `testFn`.
*/
assertSuccess<R>(input: T | R): asserts input is T {
if (!this.isSuccess(input)) {
fail(`Unexpected error: ${JSON.stringify(input)}`);
}
}

/**
* @description
* Asserts (using the testing library's `fail()` function) that the input is
* not successful, i.e. it does not pass the `testFn`.
*/
assertErrorResult<R>(input: T | R): asserts input is R {
if (this.isSuccess(input)) {
fail(`Should have errored`);
}
}
}
1 change: 1 addition & 0 deletions packages/testing/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './config/test-config';
export * from './create-test-environment';
export * from './data-population/clear-all-tables';
export * from './data-population/populate-customers';
export * from './error-result-guard';
export * from './initializers/initializers';
export * from './initializers/test-db-initializer';
export * from './initializers/mysql-initializer';
Expand Down

0 comments on commit 6ef6045

Please sign in to comment.