Skip to content

Commit fee3d80

Browse files
committed
feat(constructobservablemarble): implement constructobservablemarble
1 parent 3c8138a commit fee3d80

File tree

1 file changed

+134
-2
lines changed

1 file changed

+134
-2
lines changed
+134-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,139 @@
1+
import { ObservableMarbleToken } from '../marbles/ObservableMarbleToken';
2+
import { SubscriptionMarbleToken } from '../marbles/SubscriptionMarbleToken';
13
import { TestMessage } from '../message/TestMessage';
24

3-
const constructObservableMarble = (_value: Array<TestMessage>) => {
4-
//noop
5+
/**
6+
* If marble provided custom values, we don't know original token - instead display
7+
* pseudo alphabet for object based values. If custom value is single-length char or number,
8+
* it'll be displayed as-is.
9+
*
10+
*/
11+
const token = Array.from(
12+
`äḅċḋëḟġḧïjḳḷṁṅöṗqṛṡẗüṿẅẍÿżÄḄĊḊЁḞĠḦЇJḲḶṀṄÖṖQṚṠṪÜṾẄẌŸŻ` +
13+
`ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ` +
14+
`🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩` +
15+
`⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵`
16+
);
17+
18+
/**
19+
* Take flattened array of test message, aggregate same-frame value into nested arrays.
20+
*
21+
*/
22+
const marbleGroupReducer = <T>(
23+
acc: Array<Array<TestMessage<T>>>,
24+
value: TestMessage<T>
25+
): Array<Array<TestMessage<T>>> => {
26+
const latestGroup = acc[acc.length - 1];
27+
if (!latestGroup || latestGroup.length === 0) {
28+
acc.push([value]);
29+
} else {
30+
const latestFrame = latestGroup[latestGroup.length - 1].frame;
31+
if (value.frame === latestFrame) {
32+
latestGroup.push(value);
33+
} else {
34+
acc.push([value]);
35+
}
36+
}
37+
return acc;
38+
};
39+
40+
//we don't restore identical marble to original - preserve metadata only
41+
const constructObservableMarble = <T = string>(
42+
value: Array<TestMessage<T>> | Readonly<Array<TestMessage<T>>>
43+
): string => {
44+
if (value.length === 0) {
45+
return Array.from(Array(30)).map(() => ObservableMarbleToken.TIMEFRAME).join('');
46+
}
47+
48+
const groupedMarble: Array<Array<TestMessage<T>>> = value.reduce(marbleGroupReducer, []);
49+
const tokens = [...token];
50+
51+
let group: Array<TestMessage<T>>;
52+
let completed: boolean = false;
53+
let timeFrame: number = 0;
54+
let marbleString = '';
55+
let shiftedFrame: number = Number.NEGATIVE_INFINITY;
56+
57+
const appendNotificationValue = (message: TestMessage<T>) => {
58+
const completed = message.notification.kind === 'C' || message.notification.kind === 'E';
59+
if (completed) {
60+
marbleString += message.notification.kind === 'C' ? ObservableMarbleToken.COMPLETE : ObservableMarbleToken.ERROR;
61+
return true;
62+
} else {
63+
const value = message.notification.value;
64+
if (value.toString().length === 1) {
65+
marbleString += message.notification.value;
66+
} else {
67+
//we can't recover original token when notification metadata has custom value, use pseudo alphabet instead.
68+
//do not support marble longer than predefined token char.
69+
marbleString += tokens.shift();
70+
}
71+
}
72+
return false;
73+
};
74+
75+
//iterate each groups of message per timeframe
76+
while ((group = groupedMarble.shift()!)) {
77+
const single = group.length === 1;
78+
let message: TestMessage<T>;
79+
80+
//interate each message in single group
81+
while ((message = group.shift()!)) {
82+
//determine if there's hot observable subscription, and value emitted before subscription
83+
if (message.frame < 0 && shiftedFrame < 0) {
84+
shiftedFrame = Math.abs(message.frame);
85+
}
86+
87+
//calcuate frame and calibrate frame to start from 0
88+
let adjustedFrame = shiftedFrame < 0 ? message.frame : message.frame + shiftedFrame;
89+
90+
//if frame's 0, value's immediately appended
91+
if (adjustedFrame === 0) {
92+
timeFrame++;
93+
}
94+
95+
if (adjustedFrame !== 0) {
96+
//if interval between message's long
97+
if (adjustedFrame - timeFrame >= 15) {
98+
while (timeFrame < adjustedFrame) {
99+
marbleString += `-`;
100+
const expandedTime = adjustedFrame - timeFrame - 2;
101+
marbleString += `...${expandedTime}...-`;
102+
timeFrame = adjustedFrame;
103+
}
104+
} else {
105+
while (adjustedFrame !== 0 && timeFrame++ < adjustedFrame) {
106+
marbleString +=
107+
timeFrame === shiftedFrame + 1 ? SubscriptionMarbleToken.SUBSCRIBE : ObservableMarbleToken.TIMEFRAME;
108+
}
109+
}
110+
}
111+
112+
//append single message value
113+
if (single) {
114+
if ((completed = appendNotificationValue(message))) {
115+
break;
116+
}
117+
} else {
118+
//append grouped message value
119+
marbleString += `(`;
120+
completed = appendNotificationValue(message);
121+
122+
while ((message = group.shift()!)) {
123+
if ((completed = appendNotificationValue(message))) {
124+
break;
125+
}
126+
}
127+
marbleString += `)`;
128+
}
129+
}
130+
}
131+
132+
if (!completed) {
133+
marbleString += `-----`;
134+
}
135+
136+
return marbleString;
5137
};
6138

7139
export { constructObservableMarble };

0 commit comments

Comments
 (0)