Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit dd0a3b1

Browse files
authored
fix: LSDV-4881: Fix missed points in optimized timeseries (#1317)
* fix: LSDV-4881: Fix missed point in optimized timeseries * test: LSDV-4881: Extend TimeSeries test to detect missed points * Add --verbose temporally * fix yarn command * Fix flaky test * Clean temporary changes
1 parent ee6ed69 commit dd0a3b1

File tree

5 files changed

+92
-4
lines changed

5 files changed

+92
-4
lines changed

e2e/fragments/AtTimeSeries.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ module.exports = {
66
_overviewSelector: '.htx-timeseries-overview .overlay',
77
_westHandleSelector: '.htx-timeseries-overview .handle--w',
88
_eastHandleSelector: '.htx-timeseries-overview .handle--e',
9+
_stickSelector: '[text-anchor="start"]',
10+
911
get _channelStageSelector() {
1012
return `${this._rootSelector} .htx-timeseries-channel .new_brush`;
1113
},
14+
get _channelStickSelector() {
15+
return `${this._rootSelector} .htx-timeseries-channel [text-anchor="start"]`;
16+
},
1217
_stageBBox: { x: 0, y: 0, width: 0, height: 0 },
1318

1419
WEST: 'west',
@@ -21,8 +26,25 @@ module.exports = {
2126
this._stageBBox = bbox;
2227
},
2328

29+
/**
30+
* Retrieves timestamp value from a text element of timeseries' stick (cursor).
31+
* **should be used inside async with `await`** operator.
32+
*
33+
* ```js
34+
* let timestamp = await I.grabStickTime();
35+
* ```
36+
* @returns timestamp value
37+
*
38+
* {{ react }}
39+
*/
40+
grabStickTime() {
41+
// xPath cannot find `text` tag so we exchange it with `*`
42+
return I.grabTextFrom(locate(this._channelStickSelector).find('*').at(2));
43+
},
44+
2445
/**
2546
* Select range on overview to zoom in
47+
* **should be used inside async with `await`** operator.
2648
* @param {number} from - relative position of start between 0 and 1
2749
* @param {number} to - relative position of finish between 0 and 1
2850
* @returns {Promise<void>}
@@ -54,6 +76,7 @@ module.exports = {
5476

5577
/**
5678
* Move overview handle by mouse drag
79+
* **should be used inside async with `await`** operator.
5780
* @param {number} where - position between 0 and 1
5881
* @param {"west"|"east"} [which="west"] - handler name
5982
* @returns {Promise<void>}
@@ -74,6 +97,7 @@ module.exports = {
7497

7598
/**
7699
* Zoom by mouse wheel over the channel
100+
* **should be used inside async with `await`** operator.
77101
* @param {number} deltaY
78102
* @param {Object} [atPoint] - Point where will be called wheel action
79103
* @param {number} [atPoint.x=0.5] - relative X coordinate
@@ -93,7 +117,29 @@ module.exports = {
93117
const channelBBox = await I.grabElementBoundingRect(this._channelSelector);
94118

95119
I.moveMouse(channelBBox.x + channelBBox.width * x, channelBBox.y + channelBBox.height * y);
120+
I.pressKeyDown('Control');
96121
I.mouseWheel({ deltaY });
122+
I.pressKeyUp('Control');
123+
},
124+
125+
/**
126+
* Move mouse over the channel
127+
* **should be used inside async with `await`** operator.
128+
* @param {Object} [atPoint] - Point where will be called wheel action
129+
* @param {number} [atPoint.x=0.5] - relative X coordinate
130+
* @param {number} [atPoint.y=0.5] - relative Y coordinate
131+
* @returns {Promise<void>}
132+
*
133+
* @example
134+
* await AtTimeSeries.moveMouseOverChannel({ x: .01 });
135+
*/
136+
async moveMouseOverChannel(atPoint) {
137+
const { x = 0.5, y = 0.5 } = atPoint;
138+
139+
I.scrollPageToTop();
140+
const channelBBox = await I.grabElementBoundingRect(this._channelSelector);
141+
142+
I.moveMouse(channelBBox.x + channelBBox.width * x, channelBBox.y + channelBBox.height * y);
97143
},
98144

99145
/**

e2e/setup/feature-flags.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
module.exports = {
66
fflag_feat_front_lsdv_4659_skipduplicates_060323_short: true,
77
fflag_fix_font_lsdv_1148_hotkeys_namespaces_01022023_short: true,
8+
fflag_fix_front_lsdv_4881_timeseries_points_missing_140423_short: true,
89
};

e2e/tests/timeseries.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,26 @@ Scenario('TimeSeries with optimized data', async ({ I, LabelStudio, AtTimeSeries
268268
await doNotSeeProblems();
269269
await AtTimeSeries.selectOverviewRange(.9, .8999);
270270
await doNotSeeProblems();
271+
272+
I.say('check that every timestamps from timeseries data is available to display');
273+
await AtTimeSeries.selectOverviewRange(.001, .000);
274+
for (let i = 0; i < 20; i++) {
275+
await AtTimeSeries.zoomByMouse(-100, { x: .001 });
276+
}
277+
278+
let lastTimestamp;
279+
280+
for (let i = 0; i < 15;i++) {
281+
AtTimeSeries.moveMouseOverChannel({ x: .01 + .025 * i });
282+
const timestamp = await AtTimeSeries.grabStickTime();
283+
284+
if (lastTimestamp !== undefined) {
285+
I.say(`I see ${timestamp}`);
286+
287+
assert(timestamp === lastTimestamp || timestamp - lastTimestamp === 1,
288+
`Timestamps should not be skipped. Got ${lastTimestamp} and ${timestamp} but ${timestamp - 1} is missed`);
289+
}
290+
lastTimestamp = timestamp;
291+
}
292+
271293
});

src/tags/object/TimeSeries/Channel.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { checkD3EventLoop, getOptimalWidth, getRegionColor, sparseValues } from
1010
import { markerSymbol } from './symbols';
1111
import { errorBuilder } from '../../../core/DataValidator/ConfigValidator';
1212
import { TagParentMixin } from '../../../mixins/TagParentMixin';
13-
import { FF_DEV_3391, isFF } from '../../../utils/feature-flags';
13+
import { FF_DEV_3391, FF_LSDV_4881, isFF } from '../../../utils/feature-flags';
1414
import { fixMobxObserve } from '../../../utils/utilities';
1515

1616
/**
@@ -543,6 +543,17 @@ class ChannelD3 extends React.Component {
543543

544544
this.useOptimizedData = series.length > optimizedWidthWithZoom;
545545

546+
let originalSeries, originalTimes;
547+
548+
if (isFF(FF_LSDV_4881)) {
549+
originalSeries = series.filter(x => {
550+
return x[column] !== null;
551+
});
552+
originalTimes = originalSeries.map(x => {
553+
return x[time];
554+
});
555+
}
556+
546557
if (this.useOptimizedData) {
547558
this.optimizedSeries = sparseValues(series, optimizedWidthWithZoom);
548559
series = this.optimizedSeries;
@@ -607,10 +618,11 @@ class ChannelD3 extends React.Component {
607618

608619
const stick = screenX => {
609620
const dataX = x.invert(screenX);
610-
let i = d3.bisectRight(times, dataX, 0, times.length - 1);
621+
const stickTimes = isFF(FF_LSDV_4881) ? originalTimes : times;
622+
let i = d3.bisectRight(stickTimes, dataX, 0, stickTimes.length - 1);
611623

612-
if (times[i] - dataX > dataX - times[i - 1]) i--;
613-
return [times[i], values[i]];
624+
if (stickTimes[i] - dataX > dataX - stickTimes[i - 1]) i--;
625+
return [stickTimes[i], isFF(FF_LSDV_4881) ? originalSeries[i][column] : values[i]];
614626
};
615627

616628
this.x = x;

src/utils/feature-flags.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ export const FF_LSDV_4659 = 'fflag_feat_front_lsdv_4659_skipduplicates_060323_sh
217217
*/
218218
export const FF_LSDV_4832 = 'fflag_feat_front_lsdv_4832_new_ranker_tag_120423_short';
219219

220+
/**
221+
* Fixing issue with missed steps in timeseries with optimized data and zoom
222+
*
223+
* @link https://app.launchdarkly.com/default/production/features/fflag_fix_front_lsdv_4881_timeseties_points_missing_140423_short
224+
*/
225+
export const FF_LSDV_4881 = 'fflag_fix_front_lsdv_4881_timeseries_points_missing_140423_short';
226+
220227
Object.assign(window, {
221228
APP_SETTINGS: {
222229
...(window.APP_SETTINGS ?? {}),

0 commit comments

Comments
 (0)