test(docs-infra): add unit tests for rxjs examples (#34190)

This commit adds missing unit tests for all rxjs examples from the docs.

Closes #28017

PR Close #34190
This commit is contained in:
Sonu Kapoor 2019-12-02 13:15:58 -05:00 committed by Andrew Kushnir
parent a85109fd72
commit ba54671993
11 changed files with 341 additions and 199 deletions

View File

@ -1,3 +1,4 @@
// TODO: Add unit tests for this file.
// tslint:disable: no-output-native // tslint:disable: no-output-native
// #docregion // #docregion
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core'; import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';

View File

@ -2,7 +2,11 @@
"tests": [ "tests": [
{ {
"cmd": "yarn", "cmd": "yarn",
"args": [ "tsc", "--project", "./tsconfig.app.json" ] "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
},
{
"cmd": "yarn",
"args": ["jasmine", "out-tsc/**/*.spec.js"]
} }
] ]
} }

View File

@ -0,0 +1,55 @@
import { docRegionFromEvent, docRegionSubscriber } from './creating';
describe('observables', () => {
it('should create an observable using the constructor', () => {
const console = {log: jasmine.createSpy('log')};
docRegionSubscriber(console);
expect(console.log).toHaveBeenCalledTimes(4);
expect(console.log.calls.allArgs()).toEqual([
[1],
[2],
[3],
['Finished sequence'],
]);
});
it('should listen to input changes', () => {
let triggerInputChange;
const input = {
value: 'Test',
addEventListener: jasmine
.createSpy('addEvent')
.and.callFake((eventName: string, cb: (e) => void) => {
if (eventName === 'keydown') {
triggerInputChange = cb;
}
}),
removeEventListener: jasmine.createSpy('removeEventListener'),
};
const document = { getElementById: () => input };
docRegionFromEvent(document);
triggerInputChange({keyCode: 65});
expect(input.value).toBe('Test');
triggerInputChange({keyCode: 27});
expect(input.value).toBe('');
});
it('should call removeEventListener when unsubscribing', (doneFn: DoneFn) => {
const input = {
addEventListener: jasmine.createSpy('addEvent'),
removeEventListener: jasmine
.createSpy('removeEvent')
.and.callFake((eventName: string, cb: (e) => void) => {
if (eventName === 'keydown') {
doneFn();
}
})
};
const document = { getElementById: () => input };
const subscription = docRegionFromEvent(document);
subscription.unsubscribe();
});
});

View File

@ -1,38 +1,39 @@
// #docplaster
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
// #docregion subscriber export function docRegionSubscriber(console) {
// #docregion subscriber
// This function runs when subscribe() is called
function sequenceSubscriber(observer) {
// synchronously deliver 1, 2, and 3, then complete
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
// This function runs when subscribe() is called // unsubscribe function doesn't need to do anything in this
function sequenceSubscriber(observer) { // because values are delivered synchronously
// synchronously deliver 1, 2, and 3, then complete return {unsubscribe() {}};
observer.next(1); }
observer.next(2);
observer.next(3);
observer.complete();
// unsubscribe function doesn't need to do anything in this // Create a new Observable that will deliver the above sequence
// because values are delivered synchronously const sequence = new Observable(sequenceSubscriber);
return {unsubscribe() {}};
// execute the Observable and print the result of each notification
sequence.subscribe({
next(num) { console.log(num); },
complete() { console.log('Finished sequence'); }
});
// Logs:
// 1
// 2
// 3
// Finished sequence
// #enddocregion subscriber
} }
// Create a new Observable that will deliver the above sequence
const sequence = new Observable(sequenceSubscriber);
// execute the Observable and print the result of each notification
sequence.subscribe({
next(num) { console.log(num); },
complete() { console.log('Finished sequence'); }
});
// Logs:
// 1
// 2
// 3
// Finished sequence
// #enddocregion subscriber
// #docregion fromevent // #docregion fromevent
function fromEvent(target, eventName) { function fromEvent(target, eventName) {
@ -51,16 +52,18 @@ function fromEvent(target, eventName) {
// #enddocregion fromevent // #enddocregion fromevent
// #docregion fromevent_use export function docRegionFromEvent(document) {
// #docregion fromevent_use
const ESC_KEY = 27; const ESC_KEY = 27;
const nameInput = document.getElementById('name') as HTMLInputElement; const nameInput = document.getElementById('name') as HTMLInputElement;
const subscription = fromEvent(nameInput, 'keydown') const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => {
.subscribe((e: KeyboardEvent) => {
if (e.keyCode === ESC_KEY) { if (e.keyCode === ESC_KEY) {
nameInput.value = ''; nameInput.value = '';
} }
}); });
// #enddocregion fromevent_use
return subscription;
}
// #enddocregion fromevent_use

View File

@ -1,5 +1,5 @@
// TODO: Add unit tests for this file.
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
// #docregion // #docregion
// Create an Observable that will start listening to geolocation updates // Create an Observable that will start listening to geolocation updates

View File

@ -0,0 +1,48 @@
import { docRegionDelaySequence, docRegionMulticastSequence } from './multicasting';
describe('multicasting', () => {
let console;
beforeEach(() => {
jasmine.clock().install();
console = {log: jasmine.createSpy('log')};
});
afterEach(() => {
jasmine.clock().uninstall();
});
it('should create an observable and emit in sequence', () => {
docRegionDelaySequence(console);
jasmine.clock().tick(10000);
expect(console.log).toHaveBeenCalledTimes(12);
expect(console.log.calls.allArgs()).toEqual([
[1],
['1st subscribe: 1'],
['2nd subscribe: 1'],
[2],
['1st subscribe: 2'],
['2nd subscribe: 2'],
[3],
['Finished sequence'],
['1st subscribe: 3'],
['1st sequence finished.'],
['2nd subscribe: 3'],
['2nd sequence finished.']
]);
});
it('should create an observable and multicast the emissions', () => {
docRegionMulticastSequence(console);
jasmine.clock().tick(10000);
expect(console.log).toHaveBeenCalledTimes(7);
expect(console.log.calls.allArgs()).toEqual([
['1st subscribe: 1'],
['1st subscribe: 2'],
['2nd subscribe: 2'],
['1st subscribe: 3'],
['2nd subscribe: 3'],
['1st sequence finished.'],
['2nd sequence finished.']
]);
});
});

View File

@ -1,155 +1,160 @@
// #docplaster
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
// #docregion delay_sequence export function docRegionDelaySequence(console) {
// #docregion delay_sequence
function sequenceSubscriber(observer) {
const seq = [1, 2, 3];
let timeoutId;
function sequenceSubscriber(observer) { // Will run through an array of numbers, emitting one value
const seq = [1, 2, 3]; // per second until it gets to the end of the array.
let timeoutId; function doInSequence(arr, idx) {
timeoutId = setTimeout(() => {
observer.next(arr[idx]);
if (idx === arr.length - 1) {
observer.complete();
} else {
doInSequence(arr, ++idx);
}
}, 1000);
}
// Will run through an array of numbers, emitting one value doInSequence(seq, 0);
// Unsubscribe should clear the timeout to stop execution
return {
unsubscribe() {
clearTimeout(timeoutId);
}
};
}
// Create a new Observable that will deliver the above sequence
const sequence = new Observable(sequenceSubscriber);
sequence.subscribe({
next(num) { console.log(num); },
complete() { console.log('Finished sequence'); }
});
// Logs:
// (at 1 second): 1
// (at 2 seconds): 2
// (at 3 seconds): 3
// (at 3 seconds): Finished sequence
// #enddocregion delay_sequence
// #docregion subscribe_twice
// Subscribe starts the clock, and will emit after 1 second
sequence.subscribe({
next(num) { console.log('1st subscribe: ' + num); },
complete() { console.log('1st sequence finished.'); }
});
// After 1/2 second, subscribe again.
setTimeout(() => {
sequence.subscribe({
next(num) { console.log('2nd subscribe: ' + num); },
complete() { console.log('2nd sequence finished.'); }
});
}, 500);
// Logs:
// (at 1 second): 1st subscribe: 1
// (at 1.5 seconds): 2nd subscribe: 1
// (at 2 seconds): 1st subscribe: 2
// (at 2.5 seconds): 2nd subscribe: 2
// (at 3 seconds): 1st subscribe: 3
// (at 3 seconds): 1st sequence finished
// (at 3.5 seconds): 2nd subscribe: 3
// (at 3.5 seconds): 2nd sequence finished
// #enddocregion subscribe_twice
}
export function docRegionMulticastSequence(console) {
// #docregion multicast_sequence
function multicastSequenceSubscriber() {
const seq = [1, 2, 3];
// Keep track of each observer (one for every active subscription)
const observers = [];
// Still a single timeoutId because there will only ever be one
// set of values being generated, multicasted to each subscriber
let timeoutId;
// Return the subscriber function (runs when subscribe()
// function is invoked)
return observer => {
observers.push(observer);
// When this is the first subscription, start the sequence
if (observers.length === 1) {
timeoutId = doSequence({
next(val) {
// Iterate through observers and notify all subscriptions
observers.forEach(obs => obs.next(val));
},
complete() {
// Notify all complete callbacks
observers.slice(0).forEach(obs => obs.complete());
}
}, seq, 0);
}
return {
unsubscribe() {
// Remove from the observers array so it's no longer notified
observers.splice(observers.indexOf(observer), 1);
// If there's no more listeners, do cleanup
if (observers.length === 0) {
clearTimeout(timeoutId);
}
}
};
};
}
// Run through an array of numbers, emitting one value
// per second until it gets to the end of the array. // per second until it gets to the end of the array.
function doInSequence(arr, idx) { function doSequence(observer, arr, idx) {
timeoutId = setTimeout(() => { return setTimeout(() => {
observer.next(arr[idx]); observer.next(arr[idx]);
if (idx === arr.length - 1) { if (idx === arr.length - 1) {
observer.complete(); observer.complete();
} else { } else {
doInSequence(arr, ++idx); doSequence(observer, arr, ++idx);
} }
}, 1000); }, 1000);
} }
doInSequence(seq, 0); // Create a new Observable that will deliver the above sequence
const multicastSequence = new Observable(multicastSequenceSubscriber());
// Unsubscribe should clear the timeout to stop execution // Subscribe starts the clock, and begins to emit after 1 second
return {unsubscribe() {
clearTimeout(timeoutId);
}};
}
// Create a new Observable that will deliver the above sequence
const sequence = new Observable(sequenceSubscriber);
sequence.subscribe({
next(num) { console.log(num); },
complete() { console.log('Finished sequence'); }
});
// Logs:
// (at 1 second): 1
// (at 2 seconds): 2
// (at 3 seconds): 3
// (at 3 seconds): Finished sequence
// #enddocregion delay_sequence
// #docregion subscribe_twice
// Subscribe starts the clock, and will emit after 1 second
sequence.subscribe({
next(num) { console.log('1st subscribe: ' + num); },
complete() { console.log('1st sequence finished.'); }
});
// After 1/2 second, subscribe again.
setTimeout(() => {
sequence.subscribe({
next(num) { console.log('2nd subscribe: ' + num); },
complete() { console.log('2nd sequence finished.'); }
});
}, 500);
// Logs:
// (at 1 second): 1st subscribe: 1
// (at 1.5 seconds): 2nd subscribe: 1
// (at 2 seconds): 1st subscribe: 2
// (at 2.5 seconds): 2nd subscribe: 2
// (at 3 seconds): 1st subscribe: 3
// (at 3 seconds): 1st sequence finished
// (at 3.5 seconds): 2nd subscribe: 3
// (at 3.5 seconds): 2nd sequence finished
// #enddocregion subscribe_twice
// #docregion multicast_sequence
function multicastSequenceSubscriber() {
const seq = [1, 2, 3];
// Keep track of each observer (one for every active subscription)
const observers = [];
// Still a single timeoutId because there will only ever be one
// set of values being generated, multicasted to each subscriber
let timeoutId;
// Return the subscriber function (runs when subscribe()
// function is invoked)
return (observer) => {
observers.push(observer);
// When this is the first subscription, start the sequence
if (observers.length === 1) {
timeoutId = doSequence({
next(val) {
// Iterate through observers and notify all subscriptions
observers.forEach(obs => obs.next(val));
},
complete() {
// Notify all complete callbacks
observers.slice(0).forEach(obs => obs.complete());
}
}, seq, 0);
}
return {
unsubscribe() {
// Remove from the observers array so it's no longer notified
observers.splice(observers.indexOf(observer), 1);
// If there's no more listeners, do cleanup
if (observers.length === 0) {
clearTimeout(timeoutId);
}
}
};
};
}
// Run through an array of numbers, emitting one value
// per second until it gets to the end of the array.
function doSequence(observer, arr, idx) {
return setTimeout(() => {
observer.next(arr[idx]);
if (idx === arr.length - 1) {
observer.complete();
} else {
doSequence(observer, arr, ++idx);
}
}, 1000);
}
// Create a new Observable that will deliver the above sequence
const multicastSequence = new Observable(multicastSequenceSubscriber());
// Subscribe starts the clock, and begins to emit after 1 second
multicastSequence.subscribe({
next(num) { console.log('1st subscribe: ' + num); },
complete() { console.log('1st sequence finished.'); }
});
// After 1 1/2 seconds, subscribe again (should "miss" the first value).
setTimeout(() => {
multicastSequence.subscribe({ multicastSequence.subscribe({
next(num) { console.log('2nd subscribe: ' + num); }, next(num) { console.log('1st subscribe: ' + num); },
complete() { console.log('2nd sequence finished.'); } complete() { console.log('1st sequence finished.'); }
}); });
}, 1500);
// Logs: // After 1 1/2 seconds, subscribe again (should "miss" the first value).
// (at 1 second): 1st subscribe: 1 setTimeout(() => {
// (at 2 seconds): 1st subscribe: 2 multicastSequence.subscribe({
// (at 2 seconds): 2nd subscribe: 2 next(num) { console.log('2nd subscribe: ' + num); },
// (at 3 seconds): 1st subscribe: 3 complete() { console.log('2nd sequence finished.'); }
// (at 3 seconds): 1st sequence finished });
// (at 3 seconds): 2nd subscribe: 3 }, 1500);
// (at 3 seconds): 2nd sequence finished
// #enddocregion multicast_sequence // Logs:
// (at 1 second): 1st subscribe: 1
// (at 2 seconds): 1st subscribe: 2
// (at 2 seconds): 2nd subscribe: 2
// (at 3 seconds): 1st subscribe: 3
// (at 3 seconds): 1st sequence finished
// (at 3 seconds): 2nd subscribe: 3
// (at 3 seconds): 2nd sequence finished
// #enddocregion multicast_sequence
}

View File

@ -0,0 +1,19 @@
import { docRegionObserver } from './subscribing';
describe('subscribing', () => {
it('should subscribe and emit', () => {
const console = {log: jasmine.createSpy('log')};
docRegionObserver(console);
expect(console.log).toHaveBeenCalledTimes(8);
expect(console.log.calls.allArgs()).toEqual([
['Observer got a next value: 1'],
['Observer got a next value: 2'],
['Observer got a next value: 3'],
['Observer got a complete notification'],
['Observer got a next value: 1'],
['Observer got a next value: 2'],
['Observer got a next value: 3'],
['Observer got a complete notification'],
]);
});
});

View File

@ -1,32 +1,35 @@
// #docplaster
import { of } from 'rxjs';
import { Observable, of } from 'rxjs'; export function docRegionObserver(console) {
// #docregion observer
// #docregion observer // Create simple observable that emits three values
const myObservable = of(1, 2, 3);
// Create simple observable that emits three values // Create observer object
const myObservable = of(1, 2, 3); const myObserver = {
next: x => console.log('Observer got a next value: ' + x),
error: err => console.error('Observer got an error: ' + err),
complete: () => console.log('Observer got a complete notification'),
};
// Create observer object // Execute with the observer object
const myObserver = { myObservable.subscribe(myObserver);
next: x => console.log('Observer got a next value: ' + x),
error: err => console.error('Observer got an error: ' + err),
complete: () => console.log('Observer got a complete notification'),
};
// Execute with the observer object // Logs:
myObservable.subscribe(myObserver); // Observer got a next value: 1
// Logs: // Observer got a next value: 2
// Observer got a next value: 1 // Observer got a next value: 3
// Observer got a next value: 2 // Observer got a complete notification
// Observer got a next value: 3
// Observer got a complete notification
// #enddocregion observer // #enddocregion observer
// #docregion sub_fn // #docregion sub_fn
myObservable.subscribe( myObservable.subscribe(
x => console.log('Observer got a next value: ' + x), x => console.log('Observer got a next value: ' + x),
err => console.error('Observer got an error: ' + err), err => console.error('Observer got an error: ' + err),
() => console.log('Observer got a complete notification') () => console.log('Observer got a complete notification')
); );
// #enddocregion sub_fn // #enddocregion sub_fn
}

View File

@ -2,7 +2,11 @@
"tests": [ "tests": [
{ {
"cmd": "yarn", "cmd": "yarn",
"args": [ "tsc", "--project", "./tsconfig.app.json" ] "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
},
{
"cmd": "yarn",
"args": ["jasmine", "out-tsc/**/*.spec.js"]
} }
] ]
} }

View File

@ -1,4 +1,4 @@
// TODO: Add unit tests for this file.
import { pipe, range, timer, zip } from 'rxjs'; import { pipe, range, timer, zip } from 'rxjs';
import { ajax } from 'rxjs/ajax'; import { ajax } from 'rxjs/ajax';
import { retryWhen, map, mergeMap } from 'rxjs/operators'; import { retryWhen, map, mergeMap } from 'rxjs/operators';