UNPKG

@rxjs-stuff/marbles

Version:

A set of plugins that provide a natural feeling integration with Mocha and Chai for RxJS "marbles" testing.

230 lines (171 loc) 6.16 kB
# @rxjs-stuff/marbles `@rxjs-stuff/marbles` provides a natural feeling integration with Mocha and Chai for RxJS "marbles" testing. See the [RxJS documentation on Marble Tests](https://rxjs-dev.firebaseapp.com/guide/testing/internal-marble-tests) for more information. ```typescript import { expect } from 'chai' describe.marbles('simple example', ({ cold }) => { it('emits and closes', () => { const example$ = cold('--a|') const expected = '--a|' expect(example$).to.equal(expected) }) }) ``` ## Configuration ### NodeJS Add the Mocha and Chai integrations from `@rxjs-stuff/marbles` in your `mocha.config.js` file: ```javascript const chai = require('chai') const { chaiMarbles, config } = require('@rxjs-stuff/marbles/chai') chai.use(chaiMarbles) require('@rxjs-stuff/marbles/mocha/node').mochaMarbles(config) ``` This file must be run before any specs, so it is easiest to include as a `require` entry in your mocha config. ### Karma (Browsers) Use with either the `webpack-karma` plugin or the Angular CLI, and load `@rxjs-stuff/marbles/karma` from your entry point. No other changes are required for `karma.conf.js`. #### Angular Include `@rxjs-stuff/marbles/karma` after Angular's initialization: ```typescript // First, initialize the Angular testing environment. getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()) import '@rxjs-stuff/marbles/karma' ``` #### Non-Angular For non-Angular uses, use the "bundle" approach for loading your test files: ```typescript import '@rxjs-stuff/marbles/karma' declare const require: { context( path: string, deep?: boolean, filter?: RegExp, ): { keys(): string[] <T>(id: string): T } } const context = require.context('./src', true, /\.spec\.ts$/) context.keys().map(context) ``` ### TypeScript (NodeJS or Browser) Add `node_modules/@rxjs-stuff/marbles/@types` to your `tsconfig.json`'s `typeRoots`: ```json { "compilerOptions": { ... "typeRoots": [ "node_modules/@rxjs-stuff/marbles/@types", "node_modules/@types" ] } } ``` `@rxjs-stuff/marbles` augments ambient types from Mocha and Chai in order to add the `.marbles` modifier, as well as additional Chai assertions. Since these do not require importing from `@rxjs-stuff/marbles` in the modules where those would be referenced, the additional `typeRoots` entry is required to allow TypeScript to pick up the augmented types. ## Usage `@rxjs-stuff/marbles` provides API extensions for Mocha and Chai that allow you to do marbles testing using the same syntax and patterns you're already used to. ### Mocha Use the `.marbles` chain when defining a suite to enable marbles testing for all tests and nested suites. When marbles testing is enabled, the framework automatically takes care of setting up a `TestScheduler` instance, as well as calling `TestScheduler.flush` for each test. The suite definition function will be passed an object with the same `RunHelpers` utilities provided when calling RxJS's `TestScheduler.run`. ```typescript describe.marbles('example', ({ cold, hot }) => { ... }) ``` The usual suite definition modifiers are also supported when using `describe.marbles`: ```typescript xdescribe.marbles(...) describe.marbles.only(...) describe.marbles.skip(...) ``` To disable marbles testing within a nested suite, use `describe.noMarbles`: ```typescript describe.marbles('marbles tests', () => { describe.noMarbles('non marbles tests', () => { }) }) ``` ### Chai `@rxjs-stuff/marbles/chai` adds several language chain functions and properties to assist in configuring your assertions, and also overrides `equal` to automatically handle comparisons between Observables and marbles strings. #### Basic Comparison You do not need to use `expectObservable` to compare marbles - Chai's existing `equal` assertion is overridden to use `expectObservable` internally. ```typescript import { expect } from 'chai' describe.marbles('simple example', ({ cold }) => { it('emits and completes', () => { const example$ = cold('--a|') const expected = '--a|' expect(example$).to.equal(expected) }) }) ``` #### Using marbleValues Use `.with.marbleValues(...)` to compare observables using a value map for marbles events. ```typescript import { expect } from 'chai' describe.marbles('simple example', ({ cold }) => { it('emits and completes', () => { const marbleValues = { a: { foo: 'bar' }} const example$ = cold('--a|', marbleValues) const expected = '--a|' expect(example$).with.marbleValues(marbleValues).to.equal(expected) }) }) ``` #### Comparing Subscriptions Use the `subscribedWith(...)` assertion to compare subscriptions ```typescript import { expect } from 'chai' describe.marbles('simple example', ({ cold }) => { it('emits and completes', () => { const example$ = cold('--a|') const expected = '--a|' const expectedSub = '^--!' expect(example$).to.equal(expected) expect(example$).to.have.been.subscribedWith(expectedSub) }) }) ``` #### Controlling Subscriptions Use the `subscription` language chain to provide finer control of subscription behavior during the test run. ```typescript import { expect } from 'chai' describe.marbles('simple example', ({ hot }) => { it('emits and completes', () => { const example$ = hot('--a--a--a|') const sub = '-----^---!' const expected = '-----a--a|' expect(example$).with.subscription(sub).to.equal(expected) }) }) ``` ### Utilities #### marbleValues Use the `marbleValues` helper function to generate typed marble values objects to use with the `cold` and `hot` observable generators and `with.marbleValues(...)` language chain. ```typescript // infers type to MarbleValues<string, 'a' | 'b'> // { a: string, b: string } const myMarbles = marbleValues({ a: 'foo', b: 'bar' }) ``` **boolean marble values** The `booleanMarbles` object includes marble values for working with boolean values. ```typescript export const booleanMarbles = marbleValues({ f: false, t: true, u: undefined, }) ```