GithubHelp home page GithubHelp logo

cartant / rxjs-marbles Goto Github PK

View Code? Open in Web Editor NEW
300.0 4.0 18.0 3.99 MB

An RxJS marble testing library for any test framework

Home Page: https://cartant.github.io/rxjs-marbles/

License: MIT License

TypeScript 96.62% JavaScript 3.31% Shell 0.07%
rxjs marble-testing ava jasmine jest mocha tape

rxjs-marbles's Introduction

rxjs-marbles

GitHub License NPM version Downloads Build status dependency status devDependency Status peerDependency Status Greenkeeper badge

What is it?

rxjs-marbles is an RxJS marble testing library that should be compatible with any test framework. It wraps the RxJS TestScheduler and provides methods similar to the helper methods used the TestScheduler API.

It can be used with AVA, Jasmine, Jest, Mocha or Tape in the browser or in Node and it supports CommonJS and ES module bundlers.

Why might you need it?

I created this package because I wanted to use RxJS marble tests in a number of projects and those projects used different test frameworks.

There are a number of marble testing packages available - including the Mocha-based implementation in RxJS itself - but I wanted something that was simple, didn't involve messing with globals and beforeEach/afterEach functions and was consistent across test frameworks.

If you are looking for something similar, this might suit.

Install

Install the package using NPM:

npm install rxjs-marbles --save-dev

Getting started

If you're just getting started with marble testing, you might be interested in how I wasted some of my time by not carefully reading the manual: RxJS Marble Testing: RTFM.

In particular, you should read the RxJS documentation on marble syntax and synchronous assertion.

Usage

rxjs-marbles contains framework-specific import locations. If there is a location for the test framework that you are using, you should use the specific import. Doing so will ensure that you receive the best possible integration with your test framework.

For example, importing from "rxjs-marbles/jest" will ensure that Jest's matcher is used and the output for test failures will be much prettier.

With Mocha

Instead of passing your test function directly to it, pass it to the library's marbles function, like this:

import { marbles } from "rxjs-marbles/mocha";
import { map } from "rxjs/operators";

describe("rxjs-marbles", () => {

    it("should support marble tests", marbles(m => {

        const source =  m.hot("--^-a-b-c-|");
        const subs =            "^-------!";
        const expected =        "--b-c-d-|";

        const destination = source.pipe(
            map(value => String.fromCharCode(value.charCodeAt(0) + 1))
        );
        m.expect(destination).toBeObservable(expected);
        m.expect(source).toHaveSubscriptions(subs);
    }));
});

With other test frameworks

To see how rxjs-marbles can be used with other test frameworks, see the examples within the repository.

With AVA and Tape, the callback passed to the marbles function will receive an addional argument - the AVA ExecutionContext or the Tape Test - which can be used to specify the number of assertions in the test plan. See the framework-specific examples for details.

Using cases for test variations

In addition to the marbles function, the library exports a cases function that can be used to reduce test boilerplate by specifying multiple cases for variations of a single test. The API is based on that of jest-in-case, but also includes the marbles context.

The cases implementation is framework-specific, so the import must specify the framework. For example, with Mocha, you would import cases and use it instead of the it function, like this:

import { cases } from "rxjs-marbles/mocha";
import { map } from "rxjs/operators";

describe("rxjs-marbles", () => {

    cases("should support cases", (m, c) => {

        const source =  m.hot(c.s);
        const destination = source.pipe(
            map(value => String.fromCharCode(value.charCodeAt(0) + 1))
        );
        m.expect(destination).toBeObservable(c.e);

    }, {
        "non-empty": {
            s: "-a-b-c-|",
            e: "-b-c-d-|"
        },
        "empty": {
            s: "-|",
            e: "-|"
        }
    });
});

TestScheduler behaviour changes in RxJS version 6

In RxJS version 6, a run method was added to the TestScheduler and when it's used, the scheduler's behaviour is significantly changed.

rxjs-marbles now defaults to using the scheduler's run method. To use the scheduler's old behaviour, you can call the configure function, passing { run: false }, like this:

import { configure } from "rxjs-marbles/mocha";
const { cases, marbles } = configure({ run: false });

Dealing with deeply-nested schedulers

WARNING: bind is deprecated and can only be used with configure({ run: false }).

Sometimes, passing the TestScheduler instance to the code under test can be tedious. The context includes a bind method that can be used to bind a scheduler's now and schedule methods to those of the context's TestScheduler.

bind can be passed specific scheduler instances or can be called with no arguments to bind RxJS's animationFrame, asap, async and queue schedulers to the context's TestScheduler.

For example:

it("should support binding non-test schedulers", marbles(m => {

    m.bind();

    const source =  m.hot("--^-a-b-c-|");
    const subs =            "^--------!";
    const expected =        "---a-b-c-|";

    // Note that delay is not passed a scheduler:
    const destination = source.delay(m.time("-|"));
    m.expect(destination).toBeObservable(expected);
    m.expect(source).toHaveSubscriptions(subs);
}));

Changing the time per frame

WARNING: reframe is deprecated and can only be used with configure({ run: false }).

The RxJS TestScheduler defaults to 10 virtual milliseconds per frame (each character in the diagram represents a frame) with a maximum of 750 virtual milliseconds for each test.

If the default is not suitable for your test, you can change it by calling the context's reframe method, specifying the time per frame and the (optional) maximum time. The reframe method must be called before any of the cold, flush, hot or time methods are called.

The examples include tests that use reframe.

Alternate assertion methods

If the BDD syntax is something you really don't like, there are some alternative methods on the Context that are more terse:

const source =  m.hot("--^-a-b-c-|", values);
const subs =            "^-------!";
const expected = m.cold("--b-c-d-|", values);

const destination = source.map((value) => value + 1);
m.equal(destination, expected);
m.has(source, subs);

API

The rxjs-marbles API includes the following functions:

configure

interface Configuration {
    assert?: (value: any, message: string) => void;
    assertDeepEqual?: (a: any, b: any) => void;
    frameworkMatcher?: boolean;
    run?: boolean;
}

function configure(options: Configuration): { marbles: MarblesFunction };

The configure method can be used to specify the assertion functions that are to be used. Calling it is optional; it's only necessary if particular assertion functions are to be used. It returns an object containing a marbles function that will use the specified configuration.

The default implementations simply perform the assertion and throw an error for failed assertions.

marbles

function marbles(test: (context: Context) => any): () => any;
function marbles<T>(test: (context: Context, t: T) => any): (t: T) => any;

marbles is passed the test function, which it wraps, passing the wrapper to the test framework. When the test function is called, it is passed the Context - which contains methods that correspond to the TestScheduler helper methods:

interface Context {
    autoFlush: boolean;
    bind(...schedulers: IScheduler[]): void;
    cold<T = any>(marbles: string, values?: any, error?: any): ColdObservable<T>;
    configure(options: Configuration): void;
    equal<T = any>(actual: Observable<T>, expected: Observable<T>): void;
    equal<T = any>(actual: Observable<T>, expected: string, values?: { [key: string]: T }, error?: any): void;
    equal<T = any>(actual: Observable<T>, subscription: string, expected: Observable<T>): void;
    equal<T = any>(actual: Observable<T>, subscription: string, expected: string, values?: { [key: string]: T }, error?: any): void;
    expect<T = any>(actual: Observable<T>, subscription?: string): Expect<T>;
    flush(): void;
    has<T = any>(actual: Observable<T>, expected: string | string[]): void;
    hot<T = any>(marbles: string, values?: any, error?: any): HotObservable<T>;
    reframe(timePerFrame: number, maxTime?: number): void;
    readonly scheduler: TestScheduler;
    teardown(): void;
    time(marbles: string): number;
}

interface Expect<T> {
    toBeObservable(expected: ColdObservable<T> | HotObservable<T>): void;
    toBeObservable(expected: string, values?: { [key: string]: T }, error?: any): void;
    toHaveSubscriptions(expected: string | string[]): void;
}

observe

In Jasmine, Jest and Mocha, the test framework recognises asynchronous tests by their taking a done callback or returning a promise.

The observe helper can be useful when an observable cannot be tested using a marble test. Instead, expectations can be added to the observable stream and the observable can be returned from the test.

See the examples for usage.

fakeSchedulers

With Jest and Jasmine, the test framework can be configured to use its own concept of fake time. AVA, Mocha and Tape don't have built-in support for fake time, but the functionality can be added via sinon.useFakeTimers().

In some situations, testing asynchronous observables with marble tests can be awkward. Where testing with marble tests is too difficult, it's possible to test observables using the test framework's concept of fake time, but the now method of the AsyncScheduler has to be patched. The fakeSchedulers helper can be used to do this.

See the examples for usage.

Also, I've written an article on the fakeSchedulers function: RxJS: Testing with Fake Time.

rxjs-marbles's People

Contributors

brentd avatar cartant avatar greenkeeper[bot] avatar huy-nguyen avatar karol-majewski avatar mozmorris avatar sgrishchenko avatar zvirja avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

rxjs-marbles's Issues

Docs around how many frames are required

Hi, I hope this issue is constructive as I don't want to clutter the issues with a help request. I am just trying to understand better why dashes added here or removed there will cause a test to fail or succeed. Take this example where I am testing an NgRx effect. This is the effect:

  @Effect()
  insert: Observable<Action> = this.actions$
    .ofType<InsertEntity>(EntityActionTypes.InsertEntity)
    .pipe(
      exhaustMap((action) =>
        this.service.create(action.payload.entity).pipe(
          map((entity: Entity) => new InsertEntitySuccess({ result: entity })),
          catchError(({ message }) =>
            of(new InsertEntityFail({ error: message }))
          )
        )
      )
    );

This test will pass ✅:

  it('should return InsertSuccess action with entity on success', () => {
        const entity = generateEntity();
        const insertAction = new InsertEntity({ entity: entity });
        const successAction = new InsertEntitySuccess({ result: entity });

        actions = hot('i-', { i: insertAction });
        service.create.and.returnValue(cold('-e|', { e: entity }));
        const expected = cold('-s', { s: successAction });

        expect(effects.insert).toBeObservable(expected);
      });

I can add another dash to actions and it will still pass ✅

actions = hot('i--', { i: insertAction }); 

However if I add a dash in front, it fails ❌Error: Expected $[0].frame = 10 to equal 20. (??)

actions = hot('-i--', { i: insertAction }); 

Unless I add an additional dash to the expected, now it passes ✅

actions = hot('-i--', { i: insertAction }); 
service.create.and.returnValue(cold('-e|', { e: entity }));
const expected = cold('--s', { s: successAction });

Therefore it's a process of trial-and-error, removing and adding dashes, and I have no real understanding why x number of dashes are required, or where.

In addition, I can change hot to cold and it still passes in most cases, I understand what hot and cold observables are, but I don't understand when I need to use hot and cold marbles in tests.

Any kind of info you can provide to make this less of a trial-and-error process is much appreciated :)

source.map is not a function

Was trying your jest example line for line from the readme:

import { marbles } from 'rxjs-marbles'

test('does something', marbles(m => {
  const values = { a: 1, b: 2, c: 3, d: 4 }

  const source = m.hot('--^-a-b-c-|', values)
  const subs = '^-------!'
  const expected = m.cold('--b-c-d-|', values)

  const destination = source.map(value => value + 1)
  m.expect(destination).toBeObservable(expected)
  m.expect(source).toHaveSubscriptions(subs)
}))

and I receive the error:

    TypeError: source.map is not a function

       8 |   const expected = m.cold('--b-c-d-|', values)
       9 |
    > 10 |   const destination = source.map(value => value + 1)
      11 |   m.expect(destination).toBeObservable(expected)
      12 |   m.expect(source).toHaveSubscriptions(subs)
      13 | }))

      at scan.test.js:10:28
      at Object.<anonymous> (node_modules/rxjs-marbles/bundles/rxjs-marbles.umd.js:2242:14)

looks to me like we expect the hot() function to return an observable but instead it's returning a SubscriptionLoggable type? I'm pretty new to rxjs so don't really understand!

any idea?

(My dependency versions:)

    "jest": "^22.0.4",
    "rxjs": "^5.5.6",
    "rxjs-marbles": "^2.3.0"

Dealing with Promises

First, thanks for the lib! I've successfully written several tests, and it's very effective.

I hit a wall recently though when attempting to test an observable that mergeMaps a Promise (in my case, one that waits on a network request, which I've stubbed in the test with nock).

Simplified example:

  it('promises', marbles(m => {
    const obs1 = Observable.from(['a','b','c']).mergeMap(x => Observable.of(x))
    m.equal(obs1, '(abc|)') // Works

    const obs2 = Observable.from(['a','b','c']).mergeMap(x => Promise.resolve(x))
    m.equal(obs2, '(abc|)') // Fails
  }))

The second assertion fails with:

     Error:
Expected

to deep equal
	{"frame":0,"notification":{"kind":"N","value":"a","hasValue":true}}
	{"frame":0,"notification":{"kind":"N","value":"b","hasValue":true}}
	{"frame":0,"notification":{"kind":"N","value":"c","hasValue":true}}
	{"frame":0,"notification":{"kind":"C","hasValue":false}}

I assume this is because Promises (the native implementation, anyway) always resolves on the next tick, while marble testing is intended to test the observable within one tick.

Is my only option to do something hacky like mock the function that returns a promise to return a Observable.of() stubbed value instead?

ES modules shouldn't use `require`

The problem:
I'd like to use rxjs-marbles for testing a library at work, but Rollup isn't able to bundle it.

The reason:
This is, as an example (it happens in many files), rxjs-marbles/esm5/cases.js:

var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var isArray = require("lodash/isArray");
export function _cases(adapter, cases) {
    if (!isArray(cases)) {
        cases = Object.keys(cases).map(function (key) { return (__assign({}, cases[key], { name: key })); });
    }
    cases.forEach(adapter);
}

This looks like a ES module, and is recognized as such, because of the export statements. But in the middle it also uses require() to pull in the lodash dependency. This mixture isn't any module format at all. It would require a bundler or runtime to apply both ESM and commonjs semantics to the same module. Rollup (sensibly, I think) does not do that.

The solution:
Unfortunately I don't know Webpack well enough to point to the source of that mixed module output.

Prefer strict equality comparison over the regular one

Hello and thank you for your wonderful tool.

The problem

The Observable I'm testing returns an object. That object is not allowed to have values explicitly set to undefined — in case the value does not exist, I want to skip the field instead.

My Marble tests reflect that. I expect only one property.

const expected$ = m.cold(expectedM, {
  a: {
    name: 'Bob',
  },
});

m.expect(actual$).toBeObservable(expected$);

However, the output emitted by actual$ looks like that:

  a: {
    name: 'Bob',
    age: undefined,
  },

This change was not detected by Jest, but it did break my code.

The solution

When performing deep equality comparison. prefer toStrictEqual over toEqual.

If we replace toEqual with toStrictEqual, my excess properties are detected, which makes the tests fail as expected. This change would need to be made here:

assertDeepEqual: (a, e) => expect(a).toEqual(e),

If you agree with that change, I can open a PR and get it done — just say the word.

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Document usage of the marbles object for deep comparisons of observables

@cartant To begin with, congratulations on this nice framework !

It would be nice to document usage of the marbles object for deep comparisons of observables.

     // Within my spec:

      function deepEqual(actual, expected) {
        fail();
      }

      m.configure({assertDeepEqual: deepEqual, assert});

      m.expect(effects$.myEffect$).toBeObservable(expected);

The above test passes (under Jasmine), indicating the deepEqual is not taken into account.

What are your thoughts?

Cheers,

typing issue

I used typesafe-actions

const values = {
  a: fetchPlansAsync.request(),
};

const action$ = m.cold('-a--', values);
fetchPlansEpic(action$);

but action$: TestObservableLike<EmptyAction<"PLANS:FETCH_PLANS_REQUEST">>
is not the same with ActionsObservable<EmptyAction<"PLANS:FETCH_PLANS_REQUEST">>

any suggestion about this warning~

Time-dependent testings and scheduling

Though the library does make user of rxjs' scheduler internally, it isn't directly expose nor documented.

I needed marble testing for some time-dependent observable (namely, I am using debounceTime). I had to dig in the code source of the library, the code source of RxJs and read some stuff to understand how was time management dealt with and finally realize that because rxjs-marbles is really just a wrapper around the stuffs in rxjs/testing, I could have access to the scheduler being used through the scheduler property of one of my observables.

It solved my issue but by looking at the source code of context, it is not super clear how is rxjs-marbles manages schedulers: it seems it could recreate a new at some point though I am not really sure when, and it seems Context deliberately keeps its scheduler private.

If directly accessing an observable's scheduler member is safe, I believe it is probably useful to document so that other users do not have to go through the same trouble I had too (I believe time-dependent testing is not rare).
If that is possible, it would also be nice if the scheduler could be exposed by context. Accessing it through the observables is clearly hacky.

An in-range update of rxjs-tslint-rules is breaking the build 🚨

The devDependency rxjs-tslint-rules was updated from 4.10.0 to 4.10.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

rxjs-tslint-rules is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Support promise return values

I am currently using Jest and need to perform an async/await inside my test wrapped with marbles. However, it looks like the marbles wrapper is not respecting the fact that I am returning a promise to it.

Example:

describe('UsernameController', () => {
  let server: express.Application;
  let auth0Service: Auth0Service;
  let messagingService: MessagingService;

  beforeAll(async () => {
    server = express();
    server.use(bodyParser.json());

    const module = await Test.createTestingModule({
      imports: [UsernameModule]
    })
      // Auth0Module Overrides
      .overrideComponent(Auth0Service)
      .useValue({})
      // CQRSModule Overrides
      .overrideComponent(MessagingService)
      .useValue({})
      .compile();

    const app = module.createNestApplication(server);
    app.setGlobalPrefix('/api');
    app.useGlobalFilters(...globalFilters);
    await app.init();

    auth0Service = module.get<Auth0Service>(Auth0Service);
    messagingService = module.get<MessagingService>(MessagingService);
  });

  afterEach(() => {
    jest.resetAllMocks();
  });

  describe('forgotUsername', () => {
    it('should respond with 201 and nothing in the body', marbles(async m => {
      const app = rand.string({ length: 10 });
      const emailAddress = rand.email();
      const user = {
        user_id: rand.string({ length: 10 }),
        username: rand.string({ length: 10 })
      };
      auth0Service.findUser = jest.fn(() => Promise.resolve(user));
      messagingService.email = jest.fn(() => m.cold('(a|)'));

      await new Promise(resolve => {
        setTimeout(() => {
          console.log('test');
          resolve();
        }, 1000);
        console.log('test2');
      });

      console.log('test3');

      await request(server)
        .post('/api/profile/username/forgot')
        .set('app', app)
        .send({ emailAddress })
        .expect(201)
        .then(response => {
          console.log(response.body);
        });

      console.log('test4');

      await new Promise(resolve => {
        setTimeout(() => {
          resolve();
        }, 1000);
      });
    }));
  });
});

This produces the below output. Notice the missing console logs.

 PASS  e2e\username.controller.e2e-spec.ts
  UsernameController
    forgotUsername
      √ should respond with 201 and nothing in the body (5ms)

  console.log e2e\username.controller.e2e-spec.ts:63
    test2

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.655s, estimated 2s
Ran all test suites matching /e2e\\username/i.

Watch Usage: Press w to show more.

If I add a call to done, everything seems to work.

describe('UsernameController', () => {
  let server: express.Application;
  let auth0Service: Auth0Service;
  let messagingService: MessagingService;

  beforeAll(async () => {
    server = express();
    server.use(bodyParser.json());

    const module = await Test.createTestingModule({
      imports: [UsernameModule]
    })
      // Auth0Module Overrides
      .overrideComponent(Auth0Service)
      .useValue({})
      // CQRSModule Overrides
      .overrideComponent(MessagingService)
      .useValue({})
      .compile();

    const app = module.createNestApplication(server);
    app.setGlobalPrefix('/api');
    app.useGlobalFilters(...globalFilters);
    await app.init();

    auth0Service = module.get<Auth0Service>(Auth0Service);
    messagingService = module.get<MessagingService>(MessagingService);
  });

  afterEach(() => {
    jest.resetAllMocks();
  });

  describe('forgotUsername', () => {
    it('should respond with 201 and nothing in the body', marbles(async (m, done) => {
      const app = rand.string({ length: 10 });
      const emailAddress = rand.email();
      const user = {
        user_id: rand.string({ length: 10 }),
        username: rand.string({ length: 10 })
      };
      auth0Service.findUser = jest.fn(() => Promise.resolve(user));
      messagingService.email = jest.fn(() => m.cold('(a|)'));

      await new Promise(resolve => {
        setTimeout(() => {
          console.log('test');
          resolve();
        }, 1000);
        console.log('test2');
      });

      console.log('test3');

      await request(server)
        .post('/api/profile/username/forgot')
        .set('app', app)
        .send({ emailAddress })
        .expect(201)
        .then(response => {
          console.log(response.body);
        });

      console.log('test4');

      await new Promise(resolve => {
        setTimeout(() => {
          resolve();
        }, 1000);
      });
      done();
    }));
  });
});

Result:

  console.log e2e\username.controller.e2e-spec.ts:63
    test2

  console.log e2e\username.controller.e2e-spec.ts:60
    test

  console.log e2e\username.controller.e2e-spec.ts:66
    test3

  console.log e2e\username.controller.e2e-spec.ts:74
    {}

  console.log e2e\username.controller.e2e-spec.ts:95
    test4

 PASS  e2e\username.controller.e2e-spec.ts (5.494s)
  UsernameController
    forgotUsername
      √ should respond with 201 and nothing in the body (2146ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.336s
Ran all test suites matching /e2e\\username/i.

Should await be added here?

Behavior wrt white space in marble diagrams not conforming to rxjs's documentation

rxjs's documentation on marble syntax says that

' ' whitespace: horizontal whitespace is ignored, and can be used to help vertically align multiple marble diagrams.

These are the unit tests inside RxJS itself that show in details how white spaces should be treated (tests named “should ignore whitespace when runMode=true”).

However, it looks like the wayrxjs-marbles parses white spaces in marble diagrams doesn't obey that rule.

I made a fork with a few small changes to demonstrate the problem. On the whitespace-bug-demo branch, I first created a test for white space behavior without actually using those whitespace (commit 6828f98) . The tests for jest pass at this point. The test command I used is the same the one you used to test jest:

  • yarn run test:build
  • cross-env FAILING=0 yarn run test:jest (with cross-env scoped to the package's directory, not globally).

Then I add white spaces into a marble diagram (commit 1a36cc4). The white space test failed, indicating that the added white spaces changed how the marble diagrams are parsed when they really shouldn't. This is the diff between those two commits for your convenience.

Am I misunderstanding the rxjs's marble syntax explanation or is there a bug in your library?

Cannot read property 'helpers_' of undefined

Hello,

When destructuring parameters from the marbles function like so

it('should work', marbles(({ hot, cold, expect }) => {
// ...
}))

I get the following error:

TypeError: Cannot read property 'helpers_' of undefined
            at <Jasmine>
            at RunContext.cold (./node_modules/rxjs-marbles/esm5/context-run.js?:34:29)
            at UserContext.eval (./src/store/some/effect.effects.spec.ts?:12:26)
            at eval (./node_modules/rxjs-marbles/esm5/marbles.js?:45:78)
            at TestScheduler.run (./node_modules/rxjs/_esm5/internal/testing/TestScheduler.js?:355:23)
            at UserContext.wrapper (./node_modules/rxjs-marbles/esm5/marbles.js?:45:36)
            at ZoneDelegate.invoke (./node_modules/zone.js/dist/zone.js?:387:26)
            at ProxyZoneSpec.onInvoke (./node_modules/zone.js/dist/zone-testing.js?:287:39)
            at ZoneDelegate.invoke (./node_modules/zone.js/dist/zone.js?:386:32)
            at Zone.run (./node_modules/zone.js/dist/zone.js?:137:43)

When I don't destructure the parameters, everything seems to work fine.

Would you be able to provide some guidance? I'd be happy to help if needed.

Testing async operators such as `delay()`

// decrementAsync.js
import { of } from 'rxjs';
import { delay, filter } from 'rxjs/operators';

export const decrementAsync = ({ dispatch }) => {
  of(null)
    .pipe(delay(1000))
    .subscribe(() => dispatch('decrement'));  // It can not be tested
};
// decrementAsync.spec.js
import { marbles } from 'rxjs-marbles/jest';

import { decrementAsync } from './decrementAsync';

describe('decrementAsync', () => {
  it('should handle decrementAsync', marbles((m) => {
    const dispatch = (type) => {
      const source =  m.hot('--^-a-----|', { a: null });
      const subs =            '^-------!';
      const expected =        '---a----|';

      const destination = source.pipe(delay(m.time('-|')));

      m.expect(destination).toBeObservable(expected);
      m.expect(source).toHaveSubscriptions(subs);

      m.expect(type).toBe('decrement');
    };

    decrementAsync({ dispatch });
  }));
});

Maintenance question

Is this library still supported? Is it safe to pull it into the project in 2024 and write unit tests? Are there any alternatives that are maintained actively?

[jest] "toEqual" timeouts with observe

rxjs-marbles revision: a7ae5dc
node: v9.5.0
rxjs: 6.2.0

copy&paste snippet below into ./examples/jest/observe-spec.ts

    it(
        "should property reduce `localState`",
        observe(() => {
            // would be passed as param
            const userState$ = of({
                userName: "jdoe",
                lastLogin: "1231241241"
            });

            const logout$ = never();
            const login$ = never();
            const actionsWithUser$ = merge(
                merge(logout$, login$),
                userState$.pipe(
                    map(userState => localState => ({
                        ...localState,
                        user: userState
                    }))
                )
            );

            const result$ = actionsWithUser$.pipe(
                startWith({ user: undefined, someOtherProp: "foo" }),
                scan((state, reduce) => reduce(state))
            );
            return result$.pipe(
                skip(1),
                tap(val =>
                    expect(val).toEqual({
                        someOtherProp: "foo",
                        user: { lastLogin: "1231241241", userName: "jdoe" }
                    })
                )
            );
        })
    );

when used with toBe there is an proper failure because Object.is equality check

when I use expect(val).toEqual the test timeouts... Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

EDIT: it also might be related to the skip() call but with scalar state - only number this test works

An in-range update of mocha is breaking the build 🚨

The devDependency mocha was updated from 6.0.2 to 6.1.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

mocha is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Release Notes for v6.1.0

6.1.0 / 2019-04-07

🔒 Security Fixes

  • #3845: Update dependency "js-yaml" to v3.13.0 per npm security advisory (@plroebuck)

🎉 Enhancements

  • #3766: Make reporter constructor support optional options parameter (@plroebuck)
  • #3760: Add support for config files with .jsonc extension (@sstephant)

📠 Deprecations

These are soft-deprecated, and will emit a warning upon use. Support will be removed in (likely) the next major version of Mocha:

🐛 Fixes

  • #3829: Use cwd-relative pathname to load config file (@plroebuck)
  • #3745: Fix async calls of this.skip() in "before each" hooks (@juergba)
  • #3669: Enable --allow-uncaught for uncaught exceptions thrown inside hooks (@givanse)

and some regressions:

📖 Documentation

🔩 Other

  • #3830: Replace dependency "findup-sync" with "find-up" for faster startup (@cspotcode)
  • #3799: Update devDependencies to fix many npm vulnerabilities (@XhmikosR)
Commits

The new version differs by 28 commits.

  • f4fc95a Release v6.1.0
  • bd29dbd update CHANGELOG for v6.1.0 [ci skip]
  • aaf2b72 Use cwd-relative pathname to load config file (#3829)
  • b079d24 upgrade deps as per npm audit fix; closes #3854
  • e87c689 Deprecate this.skip() for "after all" hooks (#3719)
  • 81cfa90 Copy Suite property "root" when cloning; closes #3847 (#3848)
  • 8aa2fc4 Fix issue 3714, hide pound icon showing on hover header on docs page (#3850)
  • 586bf78 Update JS-YAML to address security issue (#3845)
  • d1024a3 Update doc examples "tests.html" (#3811)
  • 1d570e0 Delete "/docs/example/chai.js"
  • ade8b90 runner.js: "self.test" undefined in Browser (#3835)
  • 0098147 Replace findup-sync with find-up for faster startup (#3830)
  • d5ba121 Remove "package" flag from sample config file because it can only be passes as CLI arg (#3793)
  • a3089ad update package-lock
  • 75430ec Upgrade yargs-parser dependency to avoid loading 2 copies of yargs

There are 28 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

What is the difference berween of() and m.cold()?

Hello,

I have the following test (example repo with it https://github.com/olessavluk/rxmarbles-issue/blob/master/src/marble.test.js):

it(
  "renders without crashing",
  marbles(m => {
    const expected$ = m.cold("(r|)", { r: [1, 2] });

    const two$ = m.cold("t", { t: 2 }); // of(2);
    const res$ = of(1).pipe(withLatestFrom(two$));

    m.expect(res$).toBeObservable(expected$);
  })
);

When I use of(2) it works fine, but when I use m.cold result is never returned.

Why is that, aren't they supposed to behave in the same way?

Testing expectations after flush

If I would like to test a number of expectations after the flush has occurred, what would be the advised approach? Right now I'm disabling autoFlush and call flush manually, but that adds a bit of plumbing to every test that needs it.

The timing is not correct when using parentheses

I have the following minimal example that I think should work:

it("should succeed", marbles((m) => {
        const a$ = m.cold("-a|");
        const b$ = m.cold("-1|");

        const o$ = m.cold("-(a1)|");

        const result = merge(a$, b$);
        m.expect(result).toBeObservable(o$);
    }));

The result ends as expected in frame 2, but o$ ends only in frame 5.

Are synchronous notifications possible?

How to actually deal with promises?

This is probably a duplicate of #11, but the original issue never contained a valid example of a workaround for the issue with Promises.

And it would be really helpful if the library supported Promises out of the box.

Because right now I'm in a situation where after a code refactoring I cannot continue using rxjs-marbles in testing my Angular ngrx effect, because it consumes a service that returns a promise.

Any help appreciated.

Thanks

Examples or reference materials.

Firstly, many thanks for some awesome work here.

I have just spent the morning getting everything working with our Jest setup. Struggled a bit TBH. I spent a lot of time hopping between blogs and docs trying to piece together a decent real world test example.

My output is here: https://github.com/hally9k/rxjs-marbles-jest-example

This issue is really 2 questions:

  1. Should we add some examples like this to the source or links to the readme to help future noobs like myself?

  2. Does my output referenced serve as good example or have I missed a better way to do this? (It doesn't feel super clean the way that I am mocking delay...) 🤔

Use native matchers

This library is great, thanks! I've never been able to satisfactorily test observables before but this seems to work really well.

I'm using Jest, and it has some really great native matchers built-in for pretty-printing deep object comparisons. Is it possible to use these instead of the default matcher?

RxJs BufferTime operator not supported to mock elapsed time

Hello

I can't mock time elapsed when I use bufferTime operator with fakeSchedulers

I created this environment to demonstrate you the issue :
https://stackblitz.com/edit/rxjs-marbles-buffer-time-issue?file=src%2Fbuffer-time.spec.ts

FakeSchedulers works well with delay / debounceTime, but not with bufferTime.

For now I can use a workaround in using buffer(source.pipe(debounceTime(timeInMs)) because I don't need the "interval" behavior of bufferTime

Do you have an idea ?

Thank you !

Passing a done callback

I am using rxjs-marbles with Jest (and very thankful for it). However I would like to use the done() callback, as I can do with test that don't use the marbles. Is there a way to do that? Or another recommended way to test if the subscribe has fired? Thanks in advance!

fakeSchedulers stopped working with rxjs 7

I recently upgraded rxjs and rxjs-marbles to v7 in a stencil project.
This caused some jest tests using fakeSchedulers to test debounceTime to stop working.

I don't know if this is a bug, or an error on my side, but any help would be really appreciated.

I created a repo that reproduces this https://github.com/thccorni/rxjs-marbles-fake-schedulers-with-rxjs-7

To summarize:
I have some component emitting user input after some debounce time.

this.countDebouncer$
  .pipe(debounceTime(this.dueTime), takeUntil(this.destroy$))
  .subscribe((count) => {
    this.countChange.emit(count);
    console.log('emitting', { count });
  });

// some click handler
this.countDebouncer$.next(count)

I usually tested this using advance with the appropriate amount of time, which worked great until now

fakeSchedulers(async (advance) => {
  // omitted…
  button.click();
  expect(emit).not.toBeCalled();
  advance(300);
  expect(emit).toBeCalledTimes(1);
}),

Thanks for your help in advance!

doing a custom non-deep object comparison

it('bar',marbles(m =>{
    const vals = {
        a: {
            x: 10,
            y: 20,
            z: 30
        }
    }
    const stream = new Observable((sub)=>{
        sub.next({
            x: 11,
            h: false
        })
    })
    m.expect(stream).toBeObservable("a", vals)
}))

I don't want to a deep object compare between the emitted value and my test. I just want to test parts of the emitted value. In this case if x>=10. Is that possible with .toBeObservable? i.e.

m.expect(stream).toBeObservable("a", (vals)=>{
    if(vals['a']['x']<10) throw 'bad x' 
})

Visual marble failure

Hi there,

Is there a visual failure report with a marble view possible with this lib ? Like for example :

Error :
Source: "---a--b--c"
Expected: "a--b--c"

Instead of the array diff view :

Error :
Expected 
        {"frame":6,"notification":{"kind":"N","value":1,"hasValue":true}}
        {"frame":12,"notification":{"kind":"N","value":1,"hasValue":true}}
        {"frame":18,"notification":{"kind":"N","value":1,"hasValue":true}}
        {"frame":24,"notification":{"kind":"N","value":1,"hasValue":true}}
        {"frame":30,"notification":{"kind":"N","value":1,"hasValue":true}}
        {"frame":36,"notification":{"kind":"N","value":1,"hasValue":true}}
        {"frame":36,"notification":{"kind":"C","hasValue":false}}
      
      to deep equal 
        {"frame":6,"notification":{"kind":"N","value":1,"hasValue":true}}
        {"frame":6,"notification":{"kind":"C","hasValue":false}}
      : Expected false to be truthy.

Thanks

Object property order and deep equals: expected behavior?

Fantastic library. Thank you!!!!

I have a question about object comparison. I'm using @ngrx/effects in an Angular project. I'm using Jest as my testing framework. I have a class called UserEffects that listens for login attempts and deals with the side effects:

@Injectable()
export class UserEffects {
  // Listen for and handle login events
  @Effect() login$: Observable<Action> = this.action$.pipe(
    ofType('[User] Start Login'),
    mergeMap(() => this.auth.login().pipe(
      map((res: AuthResult) => ({ type: '[User] Successful Login', payload: res })),
      catchError(() => of({ type: '[User] Failed Login' }))
    ))
  );
  constructor(private action$: Actions, private auth: AuthService) {}
}

I've also got a bunch of helper classes that inherit from Action that can be used to generate actions, e.g.

export class LoginSuccess implements Action {
  readonly type = UserActionTypes.LoginSuccess;
  constructor(public payload: AuthResult) {}
}

My DESIRED test looks like this:

it("should catch login attempts", marbles((m) => {
  const authRes = { accessToken: '12345', idToken: '67890', refreshToken: 'abcde' } as AuthResult;
  const loginStart = new UserActions.LoginStart();
  const loginSuccess = new UserActions.LoginSuccess(authRes); // <-- THIS DOES NOT WORK
  action$        = m.cold("a---", { a: loginStart } );
  const response = m.cold("--r-", { r: authRes } );
  const expected = m.cold("--b-", { b: loginSuccess } );
  auth.login = jest.fn(() => response);

  m.expect(fx.login$).toBeObservable(expected);
}));

However, when I run this test, I get output that looks like this:

    Expected
    	{"frame":20,"notification":{"kind":"N","value":{"type":"[User] Successful Login","payload":{"accessToken":"12345","idToken":"67890","refreshToken":"abcde"}},"hasValue":true}}

    to deep equal
    	{"frame":20,"notification":{"kind":"N","value":{"payload":{"accessToken":"12345","idToken":"67890","refreshToken":"abcde"},"type":"[User] Successful Login"},"hasValue":true}}

If you look at these closely, you can see that the ONLY difference between the actual and expected values is the order of the properties. If I re-write my test like this...

it("should catch login attempts", marbles((m) => {
  const authRes = { accessToken: '12345', idToken: '67890', refreshToken: 'abcde' } as AuthResult;
  const loginStart = new UserActions.LoginStart();
  const loginSuccess = { type: '[User] Successful Login', payload: authRes }; // <-- THIS WORKS!!
  action$        = m.cold("a---", { a: loginStart } );
  const response = m.cold("--r-", { r: authRes } );
  const expected = m.cold("--b-", { b: loginSuccess } );
  auth.login = jest.fn(() => response);

  m.expect(fx.login$).toBeObservable(expected);
}));

...my tests pass.

Is there any way to specify a different function for performing object comparison? I expect that there's some way I could use configure() or marbles() to specify this, but I couldn't quite figure it out.

fakeSchedulers - Testing throttleTime pipes?

Hello,

I am trying to test a NestJS Saga (not wholly relevant -- just a bit of context here) with the following implementation:

// active-user.saga.ts
  @Saga()
  readonly userWasActive = (
    events$: Observable<any>
  ): Observable<ICommand> => {
    return events$.pipe(
      ofType(UserActiveEvent),
      mergeMap((event: UserActiveEvent) => {
        return of(event.user)
      }),
      filter((user) =>
        this.cooldownService.hasElapsed('activity.cooldown', user.lastLogin)
      ),
      groupBy((user) => user.id),
      mergeMap((group$) =>
        group$.pipe(
          throttleTime(
            10000,
          ),
          mergeMap((user: User) => {
            return of(
              new UpdateUserCommand(user.id, { lastLogin: currentTime() })
            )
          })
        )
      )
    )
  }

As you can see, I am grouping the events by user id and then throttling those individual groups.

The test I have is:

// active-user.saga.spec.ts

    it(
      'prevents multiple command from being output from the same user over 10 seconds',
      fakeSchedulers((advance) => {
        const result: UpdateUserCommand[] = []
        const subject = new Subject()
        effects.userWasActive(subject).subscribe((value: UpdateUserCommand) => {
          console.log('saga was updated with:', value)
          result.push(value)
        })
        subject.next(mockEvent)
        advance(4000)
        subject.next(mockEvent)
        advance(4000)
        subject.next(mockEvent)
        advance(4000)
        subject.next(mockEvent)
        advance(4000)

        expect(result).toHaveLength(2)
      })
    )

Unfortunately this test fails, as result is empty.
If I remove throttleTime, then result is no longer empty, and contains 4 items.

Is it possible to test an observable that uses throttleTime using fakeScheduler?

Problem example jest test

I have an issue when running the jest example.

In new node project, install rxjs, rxjs-marbles and jest. Run the example jest test from the readme, I get:

TypeError: source.map is not a function

  at m (index.test.js:15:32)
  at Object.<anonymous> (node_modules/rxjs-marbles/bundles/rxjs-marbles.umd.js:2248:27)
      at new Promise (<anonymous>)
      at <anonymous>
  at process._tickCallback (internal/process/next_tick.js:160:7)

package file:

"devDependencies": {
    "jest": "^22.4.3",
    "rxjs-marbles": "^2.3.2"
  },
  "dependencies": {
    "rxjs": "^5.5.7"
  }

m.expect not recognized as jasmine expectation (SPEC HAS NO EXPECTATIONS)

In my Angular7 I made very simple test:

it('should see expectations', marbles(m => {
  const source: Observable<Message> = m.hot('^a--b--c');
  m.expect(source).toBeObservable('-a--b--c');
}));

But I get in console error:

ERROR: 'Spec 'MyService should see expectations' has no expectations.

And my test is prefixed with SPEC HAS NO EXPECTATIONS.

Adding expect(true).toBe(true); helps but I guess jasmine should recognize m.expect as expectation, right?

I posted also stackoverflow question: https://stackoverflow.com/questions/53239173/rxjs-marbles-testing-has-no-expectations but I feel here I have better chance to understand the problem.

Also I looked on tests in this repository and there is no such message and I couldn't find any settings related to this problem. Thank you for help.

Dependencies:

"rxjs-marbles": "4.3.2"
"jasmine-core": "3.2.1"

Make rxjs 6 ready

When I try to run my marble tests after upgrading to rxjs 6.1, I get the follwing errors:

ERROR in node_modules/rxjs-marbles/context.d.ts(3,32): error TS2307: Cannot find module 'rxjs/testing/ColdObservable'.
node_modules/rxjs-marbles/context.d.ts(4,31): error TS2307: Cannot find module 'rxjs/testing/HotObservable'.
node_modules/rxjs-marbles/context.d.ts(5,31): error TS2307: Cannot find module 'rxjs/testing/TestScheduler'.
node_modules/rxjs-marbles/expect.d.ts(1,32): error TS2307: Cannot find module 'rxjs/testing/ColdObservable'.
node_modules/rxjs-marbles/expect.d.ts(2,31): error TS2307: Cannot find module 'rxjs/testing/HotObservable'.
node_modules/rxjs-marbles/expect.d.ts(3,31): error TS2307: Cannot find module 'rxjs/testing/TestScheduler'.

Use marbles and fakeSchedulers together

I'd love to use marbles and fakeSchedulers together.

Why can't testSchedulers just be a configuration option for marbles? I have the expectation that if observable virtual time is being faked that JavaScript timers should also be faked simultaneously.

There must be a lot of code in the wild with both JavaScript based timers and observable based timers that needs to be tested.

Related: #62

Issue with emitted values containg undefined properties

I have a test like the following:

it(`should fail with a useful error message`, marbles(m => {
    const actual = of([undefined]);
    m.expect(actual).toBeObservable('--|');
}));

I expect the test to fail with a message that the observables don't match, but instead I get an error like:

TypeError: Unable to get property 'replace' of undefined or null reference
   at stringify ...

(I've cut out the stack trace because I'm using webpack). The problem seems to be that stringify in matcher.js does not check if its parameter x is undefined before attempting to format it. Simply checking for undefined and returning 'undefined' makes the test fail correctly.

This does not happen if the emitted value is actually undefined, only if it has an undefined property (or presumably if any of its inner objects do). It also does not happen for tests that pass, regardless of the emitted values.

How to use marbles and fakeSchedulers together

Hi,

I would like to be able to create an observable with a marble diagram and then use the fakeSchedulers feature to advance the time manually in my test. I'm using Jasmine with Angular but I haven't been able to get it to work, the observable created with marbles.cold doesn't emit anything when I advance the time using Angular's tick function or the function passed as the first (and sole) argument of the fakeSchedulers function (which btw, I see is deprecated). Also, nesting fakeSchedulers() and marbles() doesn't seem to work. Any idea?

Test Observable that depends on Subject

I have the following test

  it.only('should test subject', marbles((m) => {
    const subject = new Subject<void>();
    component.validate.next();
  
    const obs = subject.pipe(
      mapTo('test')
    );
    m.expect(obs).toBeObservable(m.cold('a', { a: 'test' }));
  }));

Sadly it looks like .next does not have any affect in this context.

GroupFormComponent › should test subject

    expect(received).toStrictEqual(expected) // deep equality

    - Expected  - 11
    + Received  +  1

    - Array [
    -   Object {
    -     "frame": 0,
    -     "notification": Notification {
    -       "error": undefined,
    -       "hasValue": true,
    -       "kind": "N",
    -       "value": "test",
    -     },
    -   },
    - ]
    + Array []

      at assertDeepEqual (node_modules/rxjs-marbles/jest/index.js:41:153)
      at Object.observableMatcher (node_modules/rxjs-marbles/matcher.js:38:13)
      at TestScheduler.assertDeepEqual (node_modules/rxjs-marbles/marbles.js:47:38)
      at node_modules/rxjs/src/internal/testing/TestScheduler.ts:159:14
          at Array.filter (<anonymous>)
      at TestScheduler.Object.<anonymous>.TestScheduler.flush (node_modules/rxjs/src/internal/testing/TestScheduler.ts:157:39)
      at TestScheduler.Object.<anonymous>.TestScheduler.run (node_modules/rxjs/src/internal/testing/TestScheduler.ts:392:12)
      at wrapper (node_modules/rxjs-marbles/marbles.js:49:36)
      at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:386:30)
      at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
      at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:385:36)
      at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/dist/zone.js:143:47)

Can someone drop me a hint to what I am doing wrong here?

Thanks a lot!

How to do custom checks after an observable finished?

I'm using Jasmine and I need to do some additional checks after the observable checks. Ie,

m.expect(effects.openSequence).toBeObservable(m.cold('(1234)', expectedActions));
expect(activityService.trackFinalization).toHaveBeenCalled();

How do I do that?

Jest output prettiness is gone

Though it works fine, the Jest output compared to a test using jasmine-marbles is missing a lot of flavor (see attached images). I especially miss the coloured diff (frame). Will this be improved?

screen shot 2018-03-29 at 13 56 31

screen shot 2018-03-29 at 13 56 40

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.