Ad
  • Custom User Avatar

    I made a typo in the description; the error thrown should be of the form "duplicate [query]" not "duplicated [query]". Otherwise, everything is good to go. While at it, I've added clause order tests to the fixed tests so the users don't have to wait till random tests to discover problems with their implementation. Fork here

  • Custom User Avatar

    These edge cases should also be tested since they are present in example tests:

    • [1..9,12..15] -- invalid since one single range is allowed

    • [1,2..20,25] -- invalid since a range has to be the final item

    • [1,2,3..20] -- invalid since at most one inidivual element can be provided before a range

  • Custom User Avatar

    These edge cases should also be tested since they are present in example tests:

    • [1..9,12..15] -- invalid since one single range is allowed

    • [1,2..20,25] -- invalid since a range has to be the final item

    • [1,2,3..20] -- invalid since at most one inidivual element can be provided before a range

  • Custom User Avatar

    It seems a bit odd to me that vowelSet.includes(A) is required to throw an error when such problems are decidable (determining if a finite set is a subset of any other set). Should it really be required that the implementation fails when it doesn't have to?

  • Custom User Avatar

    I'm failing exclusively on four "anti cheating memoization test"s (without cheating or using memoization), so either that test is misconfigured, or my solution is bad and some other "normal" tests (not related to cheating) are missing.

  • Custom User Avatar

    First off, thanks for putting this together, it's been a fun challenge. However, I seem to be running into an issue with my attempt. The first attempt, I passed all tests but the performance test. My second attempt, after some minor code cleanup, is now failing all of the "anti cheating" "edge case" tests, but I haven't functionally changed anything.

    Any idea what the issue could be? I put a good amount of effort into my solution, so it's a bit frustrating to fail in a way that I cannot identify.

  • Default User Avatar

    The JavaScript in the description and initial code is quite ancient. Decorator should be a class, and spread parameters should be used instead of using Function.prototype.call() on the arguments object.

  • Custom User Avatar

    The if outer observable never completes, then concatMap either and if inner observable never completes, then concatMap either tests are completely undecipherable.

    Please actually explain what are the expected behaviours of the 3 operators required to be implemented.

  • Custom User Avatar

    Starting from this part the kata stops resembling any kind of observable pattern. Observable pattern is push-based, not pull-based: downstream do not, and cannot control whether upstream starts pushing values; similarly, upstream do not have the concept of "knowing whether downstream want a values". That'd be a pull-based design like Promise.

    of and interval does not make any sense in a push-based system. It would be acceptable if the tests are using it responsibly, but then there are increasingly more gratuitous misuse of this in later parts (#4, then #5) which requires pull-based behaviour in a supposedly push-based system, requiring completely insane programming.

  • Custom User Avatar
        it("if first operator never ends, the second does not emit", cbk => {
          const numbers: number[] = [];
          let hasCompleted = false;
          const sourceOne = interval(10);
          const sourceTwo = of(4, 5, 6).pipe(tap(v => numbers.push(v)));
          const example: Observable<number> = sourceOne.pipe(concat(sourceTwo));
          const subscription = example.subscribe({
            onComplete: () => (hasCompleted = true)
          });
          setTimeout(() => {
            subscription.unsubscribe();
            expect(numbers).to.deep.equal([]);
            expect(hasCompleted).to.equal(false);
            cbk();
          }, 100);
        });
    
        it("when one operator is larger than the other its values are discarded", cbk => {
          const numbers: Array<[number, number]> = [];
          const sourceOneNumbers: number[] = [];
          let hasCompleted = false;
          const sourceOne = interval(5).pipe(tap(v => sourceOneNumbers.push(v)));
          const sourceTwo = of(4, 5, 6);
          const example: Observable<[number, number]> = sourceOne.pipe(
            zipWith((x, y) => [x, y])(sourceTwo)
          );
          example.subscribe({
            onNext: val => numbers.push(val),
            onComplete: () => (hasCompleted = true)
          });
          setTimeout(() => {
            expect(numbers).to.deep.equal([[0, 4], [1, 5], [2, 6]]);
            // Ideally the first observable should not emit more values than the second but this can be difficult to achieve so a small margin is given.
            console.log(sourceOneNumbers);
            expect(sourceOneNumbers.length).to.be.below(6);
            expect(hasCompleted).to.equal(true);
            cbk();
          }, 200);
        });
    

    These tests violate the basic principle of observable pattern: observable pattern is push-based, not pull-based (like Promise), so whether the upstream emits any value should not be the concern of downstream, and definitely not controllable by downstream.

    However, these tests are instead requiring pull-based behaviour from a push-based design. tap is not supposed to be used this way.

    (Though really, this problem has started since #2, when RxJS APIs are blindly copied without consideration...)

  • Custom User Avatar

    early take version of tap accidentally tests that onCompleted should be called right after take receives the last value. This should be explicitly tested in take tests.

  • Custom User Avatar

    Some tests in the kata forgets to unsubscribe the subscription after it is done:

      it("should work with interval", cbk => {
        const numbers: number[] = [];
        let hasCompleted = false;
        const source: Observable<number> = interval(5).pipe(take(5));
    
        source.subscribe({
          onNext: v => numbers.push(v),
          onComplete: () => (hasCompleted = true)
        });
    
        setTimeout(() => {
          expect(numbers).to.deep.equal([0, 1, 2, 3, 4]);
          expect(hasCompleted).to.equal(true);
          cbk();
        }, 250);
      });
    
      it("should see values after and before map", () => {
        const numbers: number[] = [];
        of(1, 2, 3, 4, 5)
          .pipe(
            tap((n: number) => numbers.push(n)),
            map(n => n * 2),
            tap((n: number) => numbers.push(n))
          )
          .subscribe({});
    
        expect(numbers).to.deep.equal([1, 2, 2, 4, 3, 6, 4, 8, 5, 10]);
      });
    
      it("if tap allows to verify that after complete unsubscriptions are handled correctly", cbk => {
        const beforeNumbers: number[] = [];
        const afterNumbers: number[] = [];
        interval(5)
          .pipe(
            tap((n: number) => beforeNumbers.push(n)),
            map(n => n * 2),
            tap((n: number) => afterNumbers.push(n)),
            take(6)
          )
          .subscribe({});
    
        setTimeout(() => {
          expect(beforeNumbers).to.deep.equal([0, 1, 2, 3, 4, 5]);
          expect(afterNumbers).to.deep.equal([0, 2, 4, 6, 8, 10]);
          cbk();
        }, 250);
      });
    
      it("if tap allows to verify that after complete unsubscriptions are handled correctly (early take version)", cbk => {
        const beforeNumbers: number[] = [];
        const afterNumbers: number[] = [];
        let hasCompleted = false;
        interval(5)
          .pipe(
            tap((n: number) => beforeNumbers.push(n)),
            take(6),
            map(n => n * 2),
            tap((n: number) => afterNumbers.push(n))
          )
          .subscribe({ onComplete: () => (hasCompleted = true) });
    
        setTimeout(() => {
          expect(beforeNumbers).to.deep.equal([0, 1, 2, 3, 4, 5]);
          expect(afterNumbers).to.deep.equal([0, 2, 4, 6, 8, 10]);
          expect(hasCompleted).to.equal(true);
          cbk();
        }, 250);
      });
    

    This violates how interval should work as stated in #2 (it will never complete, and only unsubscribe stops the timer), so a correct implementation of interval will never pass these tests.

  • Custom User Avatar

    The tests in this kata do not check that the operators handle post-complete/unsubscribe calls correctly.

  • Custom User Avatar

    interval does not specify how it should be started: does it start emitting 0 immediately, or after period time? This is not tested at all (both ways pass the tests), and it hits right at the boundary (500 / 20 = 25), so it is a lot of off-by-one shenanigans waiting to happen.

    Also, when the interval tests fail, it displays expected [ Array(50) ] to deeply equal [ Array(50) ]. chai.config.truncateThreshold is missing.

  • Custom User Avatar

    Seems to be a duplicate of Replicate `new`

  • Loading more items...