Skip to content

Commit

Permalink
feat(debug): add extra subscribe, unsubscribe and finalize extra noti…
Browse files Browse the repository at this point in the history
…fs (#107) (#114)
  • Loading branch information
LcsGa authored Oct 14, 2023
1 parent c1f5486 commit 8dc8330
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 46 deletions.
23 changes: 22 additions & 1 deletion docs/src/content/docs/utilities/Operators/debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const out$ = in$.pipe(
debug('after')
);

out$.subscribe(() => {});
out$.subscribe();
// LOGS:
//<timestampUTC> [before: Next] 1
//<timestampUTC> [after: Next] 2
Expand All @@ -41,3 +41,24 @@ out$.subscribe(() => {});
//<timestampUTC> [before: Complete]
//<timestampUTC> [after: Complete]
```

In addition to `tag` you can also provide extra notifications like `subscribe`, `unsubscribe` and `finalize` by passing an optional object of type

```ts
type ExtraNotifications = {
subscribe?: boolean;
unsubscribe?: boolean;
finalize?: boolean;
};
```

```ts
const in$ = of('hello world');
const out$ = in$.pipe(debug('test', { subscribe: true }));

out$.subscribe();
// LOGS:
//<timestampUTC> [test: Subscribed]
//<timestampUTC> [test: Next] hello world
//<timestampUTC> [test: Completed]
```
109 changes: 74 additions & 35 deletions libs/ngxtension/debug/src/debug.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { EMPTY, from, map } from 'rxjs';
import { EMPTY, NEVER, from, map, tap } from 'rxjs';
import { debug } from './debug';

let consoleArgs: any[] = [];
const mockConsole =
(type: 'L' | 'W' | 'E') =>
(type: 'L' | 'W' | 'E' | 'I') =>
(...args: any[]) => {
if (
typeof args[0] === 'string' &&
Expand All @@ -16,14 +16,27 @@ const mockConsole =
};

describe(debug.name, () => {
it('given an observable that complete >| should console.warn completed', () => {
const origConsole = { ...console };

beforeEach(() => {
//MOCKING CONSOLE
const origConsole = { ...console };
consoleArgs = [];
console.log = mockConsole('L');
console.warn = mockConsole('W');
console.error = mockConsole('E');
console.info = mockConsole('I');
});

afterEach(() => {
//RESTORE CONSOLE
console.log = origConsole.log;
console.warn = origConsole.warn;
console.error = origConsole.error;
console.info = origConsole.info;
consoleArgs = [];
});

it('given an observable that complete >| should console.warn completed', () => {
const in$ = EMPTY;
const out$ = in$.pipe(debug('test3'));

Expand All @@ -42,22 +55,9 @@ describe(debug.name, () => {
expect(out).toEqual('>|');
expect(consoleArgs.length).toEqual(1);
expect(consoleArgs[0]).toEqual(['W', '[test3: Completed]']);

//RESTORE CONSOLE
console.log = origConsole.log;
console.warn = origConsole.warn;
console.error = origConsole.error;
consoleArgs = [];
});

it('given an observable that throw >1-X should console.log emitted values until console.error', () => {
//MOCKING CONSOLE
const origConsole = { ...console };
consoleArgs = [];
console.log = mockConsole('L');
console.warn = mockConsole('W');
console.error = mockConsole('E');

const in$ = from([1, 2, 3]);
const out$ = in$.pipe(
map((n) => {
Expand All @@ -83,22 +83,9 @@ describe(debug.name, () => {
expect(consoleArgs.length).toEqual(2);
expect(consoleArgs[0]).toEqual(['L', '[test2: Next]', 1]);
expect(consoleArgs[1]).toEqual(['E', '[test2: Error]', '[ERR]']);

//RESTORE CONSOLE
console.log = origConsole.log;
console.warn = origConsole.warn;
console.error = origConsole.error;
consoleArgs = [];
});

it('given an observable >1-2-3| should console.log all emitted values + warn completed', () => {
//MOCKING CONSOLE
const origConsole = { ...console };
consoleArgs = [];
console.log = mockConsole('L');
console.warn = mockConsole('W');
console.error = mockConsole('E');

const in$ = from([1, 2, 3]);
const out$ = in$.pipe(debug('test1'));

Expand All @@ -120,11 +107,63 @@ describe(debug.name, () => {
expect(consoleArgs[1]).toEqual(['L', '[test1: Next]', 2]);
expect(consoleArgs[2]).toEqual(['L', '[test1: Next]', 3]);
expect(consoleArgs[3]).toEqual(['W', '[test1: Completed]']);
});

//RESTORE CONSOLE
console.log = origConsole.log;
console.warn = origConsole.warn;
console.error = origConsole.error;
consoleArgs = [];
it('given an observable >1-2-3| should console.info subscribe + log all emitted values + warn completed + info finalized', () => {
const in$ = from([1, 2, 3]);
const out$ = in$.pipe(debug('test4', { subscribe: true, finalize: true }));

let out = '>';
out$.subscribe({
next(s) {
out += s + '-';
},
error(e) {
out += e + 'X';
},
complete() {
out += '|';
},
});
expect(out).toEqual('>1-2-3-|');
expect(consoleArgs.length).toEqual(6);
expect(consoleArgs[0]).toEqual(['I', '[test4: Subscribed]']);
expect(consoleArgs[1]).toEqual(['L', '[test4: Next]', 1]);
expect(consoleArgs[2]).toEqual(['L', '[test4: Next]', 2]);
expect(consoleArgs[3]).toEqual(['L', '[test4: Next]', 3]);
expect(consoleArgs[4]).toEqual(['W', '[test4: Completed]']);
expect(consoleArgs[5]).toEqual(['I', '[test4: Finalized]']);
});

it('given an observable that unsubscribe >! should console.info unsubscribe + finalize', () => {
const in$ = NEVER;
const out$ = in$.pipe(
debug('test5', { unsubscribe: true, finalize: true })
);

let out = '>';
out$
.pipe(
tap({
next(s) {
out += s + '-';
},
error(e) {
out += e + 'X';
},
complete() {
out += '|';
},
unsubscribe() {
out += '!';
},
})
)
.subscribe()
.unsubscribe();
expect(out).toEqual('>!');
expect(consoleArgs.length).toEqual(2);
expect(consoleArgs[0]).toEqual(['I', '[test5: Unsubscribed]']);
expect(consoleArgs[1]).toEqual(['I', '[test5: Finalized]']);
});
});
33 changes: 23 additions & 10 deletions libs/ngxtension/debug/src/debug.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import { tap } from 'rxjs/operators';

export type ExtraNotifications = {
subscribe?: boolean;
unsubscribe?: boolean;
finalize?: boolean;
};

//INSPIRED BY @netbasal ARTICLE https://netbasal.com/creating-custom-operators-in-rxjs-32f052d69457
export function debug<T>(tag: string) {
export function debug<T>(tag: string, extraNotifications?: ExtraNotifications) {
const formatNotif = (notif: string, data?: unknown) => [
new Date().toISOString(),
`[${tag}: ${notif}]`,
data,
];
return tap<T>({
next(value) {
console.log(new Date().toISOString(), `[${tag}: Next]`, value);
},
error(err) {
console.error(new Date().toISOString(), `[${tag}: Error]`, err);
},
complete() {
console.warn(new Date().toISOString(), `[${tag}: Completed]`);
},
next: (value) => console.log(...formatNotif('Next', value)),
error: (err) => console.error(...formatNotif('Error', err)),
complete: () => console.warn(...formatNotif('Completed')),
subscribe: () =>
extraNotifications?.subscribe &&
console.info(...formatNotif('Subscribed')),
unsubscribe: () =>
extraNotifications?.unsubscribe &&
console.info(...formatNotif(`Unsubscribed`)),
finalize: () =>
extraNotifications?.finalize && console.info(...formatNotif(`Finalized`)),
});
}

0 comments on commit 8dc8330

Please sign in to comment.