Skip to content

Commit be69d5f

Browse files
fixes and add tests
1 parent 3db9c6a commit be69d5f

File tree

3 files changed

+142
-19
lines changed

3 files changed

+142
-19
lines changed

static/app/components/replays/replayContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ function ProviderNonMemo({
495495
setVideoBuffering(buffering);
496496
},
497497
clipWindow,
498-
durationMs: durationMs,
498+
durationMs,
499499
});
500500
// `.current` is marked as readonly, but it's safe to set the value from
501501
// inside a `useEffect` hook.

static/app/components/replays/videoReplayer.spec.tsx

Lines changed: 121 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('VideoReplayer - no starting gap', () => {
8787
onFinished: jest.fn(),
8888
onLoaded: jest.fn(),
8989
onBuffer: jest.fn(),
90-
durationMs: 40,
90+
durationMs: 40000,
9191
});
9292
// @ts-expect-error private
9393
expect(inst._currentIndex).toEqual(0);
@@ -113,7 +113,7 @@ describe('VideoReplayer - no starting gap', () => {
113113
onFinished: jest.fn(),
114114
onLoaded: jest.fn(),
115115
onBuffer: jest.fn(),
116-
durationMs: 40,
116+
durationMs: 40000,
117117
});
118118
const playPromise = inst.play(18100);
119119
// @ts-expect-error private
@@ -142,7 +142,7 @@ describe('VideoReplayer - no starting gap', () => {
142142
onFinished: jest.fn(),
143143
onLoaded: jest.fn(),
144144
onBuffer: jest.fn(),
145-
durationMs: 40,
145+
durationMs: 40000,
146146
});
147147
const playPromise = inst.play(50000);
148148
// 15000 -> 20000 is a gap, so player should start playing @ index 3, from
@@ -165,7 +165,7 @@ describe('VideoReplayer - no starting gap', () => {
165165
onFinished: jest.fn(),
166166
onLoaded: jest.fn(),
167167
onBuffer: jest.fn(),
168-
durationMs: 40,
168+
durationMs: 40000,
169169
});
170170
const playPromise = inst.play(0);
171171
jest.advanceTimersByTime(2500);
@@ -185,7 +185,7 @@ describe('VideoReplayer - no starting gap', () => {
185185
onFinished: jest.fn(),
186186
onLoaded: jest.fn(),
187187
onBuffer: jest.fn(),
188-
durationMs: 50,
188+
durationMs: 50000,
189189
});
190190
// play at segment 7
191191
const playPromise = inst.play(45_003);
@@ -220,7 +220,7 @@ describe('VideoReplayer - no starting gap', () => {
220220
onFinished: jest.fn(),
221221
onLoaded: jest.fn(),
222222
onBuffer: jest.fn(),
223-
durationMs: 60,
223+
durationMs: 55000,
224224
});
225225
// play at segment 7
226226
const playPromise = inst.play(45_003);
@@ -294,7 +294,7 @@ describe('VideoReplayer - with starting gap', () => {
294294
onFinished: jest.fn(),
295295
onLoaded: jest.fn(),
296296
onBuffer: jest.fn(),
297-
durationMs: 40,
297+
durationMs: 40000,
298298
});
299299
// @ts-expect-error private
300300
expect(inst._currentIndex).toEqual(0);
@@ -318,7 +318,7 @@ describe('VideoReplayer - with starting gap', () => {
318318
onFinished: jest.fn(),
319319
onLoaded: jest.fn(),
320320
onBuffer: jest.fn(),
321-
durationMs: 40,
321+
durationMs: 40000,
322322
});
323323
const playPromise = inst.play(18100);
324324
// @ts-expect-error private
@@ -347,7 +347,7 @@ describe('VideoReplayer - with starting gap', () => {
347347
onFinished: jest.fn(),
348348
onLoaded: jest.fn(),
349349
onBuffer: jest.fn(),
350-
durationMs: 40,
350+
durationMs: 40000,
351351
});
352352
const playPromise = inst.play(50000);
353353
// 15000 -> 20000 is a gap, so player should start playing @ index 3, from
@@ -361,3 +361,115 @@ describe('VideoReplayer - with starting gap', () => {
361361
expect(inst.getVideo(inst._currentIndex)?.currentTime).toEqual(5);
362362
});
363363
});
364+
365+
describe('VideoReplayer - with ending gap', () => {
366+
beforeEach(() => {
367+
jest.clearAllTimers();
368+
});
369+
370+
const attachments = [
371+
{
372+
id: 0,
373+
timestamp: 2500,
374+
duration: 5000,
375+
},
376+
// no gap
377+
{
378+
id: 1,
379+
timestamp: 5000,
380+
duration: 5000,
381+
},
382+
{
383+
id: 2,
384+
timestamp: 10_001,
385+
duration: 5000,
386+
},
387+
// 5 second gap
388+
{
389+
id: 3,
390+
timestamp: 20_000,
391+
duration: 5000,
392+
},
393+
// 5 second gap
394+
{
395+
id: 4,
396+
timestamp: 30_000,
397+
duration: 5000,
398+
},
399+
{
400+
id: 5,
401+
timestamp: 35_002,
402+
duration: 5000,
403+
},
404+
];
405+
406+
it('keeps playing until the end if there is an ending gap', async () => {
407+
const root = document.createElement('div');
408+
const inst = new VideoReplayer(attachments, {
409+
videoApiPrefix: '/foo/',
410+
root,
411+
start: 0,
412+
onFinished: jest.fn(),
413+
onLoaded: jest.fn(),
414+
onBuffer: jest.fn(),
415+
durationMs: 50000,
416+
});
417+
// actual length of the segments is 40s
418+
// 10s gap at the end
419+
420+
// play at the last segment
421+
const playPromise = inst.play(36000);
422+
await playPromise;
423+
jest.advanceTimersByTime(4000);
424+
425+
// we're still within the last segment (5)
426+
// @ts-expect-error private
427+
expect(inst._currentIndex).toEqual(5);
428+
expect(inst.getCurrentTime()).toEqual(40000);
429+
430+
// now we are in the gap
431+
// timer should still be going since the duration is 50s
432+
jest.advanceTimersByTime(5000);
433+
// @ts-expect-error private
434+
expect(inst._isPlaying).toEqual(true);
435+
436+
// a long time passes
437+
// ensure the timer stops at the end duration (50s)
438+
jest.advanceTimersByTime(50000);
439+
expect(inst.getCurrentTime()).toEqual(50000);
440+
// @ts-expect-error private
441+
expect(inst._isPlaying).toEqual(false);
442+
});
443+
444+
it('ends at the proper time if seeking into a gap', async () => {
445+
const root = document.createElement('div');
446+
const inst = new VideoReplayer(attachments, {
447+
videoApiPrefix: '/foo/',
448+
root,
449+
start: 0,
450+
onFinished: jest.fn(),
451+
onLoaded: jest.fn(),
452+
onBuffer: jest.fn(),
453+
durationMs: 50000,
454+
});
455+
// actual length of the segments is 40s
456+
// 10s gap at the end
457+
458+
// play at the gap
459+
const playPromise = inst.play(40002);
460+
await playPromise;
461+
jest.advanceTimersByTime(4000);
462+
463+
// we should be still playing in the gap
464+
expect(inst.getCurrentTime()).toEqual(44002);
465+
// @ts-expect-error private
466+
expect(inst._isPlaying).toEqual(true);
467+
468+
// a long time passes
469+
// ensure the timer stops at the end duration (50s)
470+
jest.advanceTimersByTime(50000);
471+
expect(inst.getCurrentTime()).toBeLessThan(50100);
472+
// @ts-expect-error private
473+
expect(inst._isPlaying).toEqual(false);
474+
});
475+
});

static/app/components/replays/videoReplayer.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ export class VideoReplayer {
115115
this.stopReplay();
116116
});
117117
} else {
118-
// Otherwise, if there's no clip window set, we should
118+
// Tell the timer to stop at the replay end
119+
this._timer.addNotificationAtTime(this._durationMs, () => {
120+
this.stopReplay();
121+
});
122+
// If there's no clip window set, we should
119123
// load the first segment by default so that users are not staring at a
120124
// blank replay. This initially caused some issues
121125
// (https://github.com/getsentry/sentry/pull/67911), but the problem was
@@ -251,12 +255,16 @@ export class VideoReplayer {
251255
this._isPlaying = true;
252256
this._timer.start(videoOffsetMs);
253257

254-
// This is used when a replay clip is restarted
258+
// This is used when a replay is restarted
255259
// Add another stop notification so the timer doesn't run over
256260
if (this._clipDuration) {
257261
this._timer.addNotificationAtTime(this._clipDuration, () => {
258262
this.stopReplay();
259263
});
264+
} else {
265+
this._timer.addNotificationAtTime(this._durationMs, () => {
266+
this.stopReplay();
267+
});
260268
}
261269
}
262270

@@ -301,13 +309,16 @@ export class VideoReplayer {
301309

302310
// No more segments
303311
if (nextIndex >= this._attachments.length) {
304-
// If we're at the end of a segment, but there's a gap
305-
// at the end, force the replay to play until the end duration
306-
// rather than stopping right away.
307-
this._timer.addNotificationAtTime(this._durationMs, () => {
308-
this.stopReplay();
309-
});
310-
return;
312+
if (this.getCurrentTime() < this._durationMs) {
313+
// If we're at the end of a segment, but there's a gap
314+
// at the end, force the replay to play until the end duration
315+
// rather than stopping right away.
316+
this._timer.addNotificationAtTime(this._durationMs, () => {
317+
this.stopReplay();
318+
});
319+
return;
320+
}
321+
this.stopReplay();
311322
}
312323

313324
// Final check in case replay was stopped immediately after a video

0 commit comments

Comments
 (0)