Skip to content

Add function to generate and compare marble diagrams #96

@daerogami

Description

@daerogami

This might be out-of-scope for this package, but I find myself wanting some debugging tooling to inspect what character(s) I am missing in my marble diagrams. The provided snippet is basically the reverse of the notification mappings and it seems to generate the marble diagrams I am expecting. There are still scenarios I am not accounting for such as emissions within the same frame and using parenthesis, but this is massively helpful when trying to reason about more complex observables that may have multiple pipe operators. I can't find any existing utility packages that already do this.

import { getTestScheduler } from 'jasmine-marbles';
import { Observable } from 'rxjs';

export function createMarbleDiagram<T>(
  source: Observable<T>,
  values: Record<string, T>
): Array<string> {
  const scheduler = getTestScheduler();
  let diagram: Array<string> = [];
  let lastFrame = scheduler.frame;

  source.subscribe({
    next: (value: T) => {
      appendFrames(scheduler.frame-lastFrame, diagram);
      lastFrame = scheduler.frame;
      diagram.push(
        Object.keys(values).find((key) => values[key] === value) || '?'
      );
    },
    error: (error) => {
      appendFrames(scheduler.frame-lastFrame, diagram);
      lastFrame = scheduler.frame;
      diagram.push('#');
    },
    complete: () => {
      appendFrames(scheduler.frame-lastFrame, diagram);
      lastFrame = scheduler.frame;
      diagram.push('|');
    },
  });

  return diagram;
}

function appendFrames(frameDuration: number, diagram: Array<string>) {
  const lastEmissionDeduction = diagram.length ? 10 : 0;
  const frames = Math.floor((frameDuration - lastEmissionDeduction)/10);
  for(let i = 0; i < frames; i++) {
    diagram.push('-');
  }
}

Here is a really simple test from my angular project using this method to debug

  it('should combine data from DataService and an additional source in the correct order', () => {
    const values = { a: { name: 'Test Data 1' }, b: { name: 'Test Data 2' } };
    let source1 = cold('-a---|', values);
    let source2 = cold('---b-|', values);
    someHttpService.getData.and.returnValue(source1);
    someHttpService.getMoreData.and.returnValue(source2);

    const expectedObservable = cold('-a-b-|', values);

    const result = component.getSequentialDataFromHttp();

    const diagram = createMarbleDiagram(result, values); // Debug
    expect(result).toBeObservable(expectedObservable);

    getTestScheduler().flush();
    console.debug(`Expect marble: -a-b-|`); // Debug
    console.debug(`Actual marble: ${diagram.join('')}`); // Debug
  });

which results in the output

DEBUG: 'Expect marble: -a-b-|'
DEBUG: 'Actual marble: -a-b-|'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions