Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/studio-server/src/test/update-sequence-props.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,31 @@ export const Example: React.FC = () => {
expect(output).toContain('freeze={null}');
});

test('updateSequenceProps should remove an optional attribute', async () => {
const input = `import {Sequence} from 'remotion';

export const Example: React.FC = () => {
return (
<Sequence from={0} freeze={12}>
<div />
</Sequence>
);
};
`;

const {output, oldValueStrings} = await updateSequenceProps({
videoConfigValues: null,
input,
nodePath: lineColumnToNodePath(input, 5),
updates: [{key: 'freeze', value: undefined, defaultValue: null}],
schema: NoReactInternals.sequenceSchema,
prettierConfigOverride: null,
});

expect(oldValueStrings[0]).toBe('12');
expect(output).not.toContain('freeze=');
});

test('updateSequenceProps should set boolean true as shorthand', async () => {
const {output} = await updateSequenceProps({
videoConfigValues: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ export const shouldShowFreezeFrameMenuItem = (sequence: TSequence): boolean => {
return sequence.type !== 'audio';
};

export const isSequenceVisibleAtTimelinePosition = ({
sequence,
timelinePosition,
}: {
readonly sequence: TSequence;
readonly timelinePosition: number;
}): boolean => {
return (
timelinePosition >= sequence.from &&
timelinePosition < sequence.from + sequence.duration
);
};

export const calculateSequenceFreezeFrame = ({
sequence,
sequenceFrameOffset,
Expand Down Expand Up @@ -58,6 +71,7 @@ export const useSequenceFreezeFrameMenuItem = ({
typeof freezeStatus.codeValue === 'number';

const canToggleFreeze =
isSequenceVisibleAtTimelinePosition({sequence, timelinePosition}) &&
clientId !== null &&
Boolean(sequence.controls) &&
nodePath !== null &&
Expand Down Expand Up @@ -92,7 +106,7 @@ export const useSequenceFreezeFrameMenuItem = ({
fileName: validatedSource,
nodePath,
fieldKey: 'freeze',
value: remove ? null : freezeFrame,
value: remove ? undefined : freezeFrame,
defaultValue: null,
schema: sequence.controls.schema,
},
Expand Down
23 changes: 23 additions & 0 deletions packages/studio/src/test/sequence-context-menu-items.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {getSequenceContextMenuItems} from '../components/Timeline/get-sequence-c
import {getTimelineMediaStartFrame} from '../components/Timeline/get-timeline-media-start-frame';
import {
calculateSequenceFreezeFrame,
isSequenceVisibleAtTimelinePosition,
shouldShowFreezeFrameMenuItem,
} from '../components/Timeline/use-sequence-freeze-frame-menu-item';

Expand Down Expand Up @@ -205,6 +206,28 @@ test('sequence freeze frame accounts for trimBefore', () => {
).toBe(139);
});

test('sequence freeze frame can only be toggled while the sequence is visible', () => {
const sequence = {
from: 20,
duration: 40,
premountDisplay: 10,
postmountDisplay: 10,
} as TSequence;

expect(
isSequenceVisibleAtTimelinePosition({sequence, timelinePosition: 19}),
).toBe(false);
expect(
isSequenceVisibleAtTimelinePosition({sequence, timelinePosition: 20}),
).toBe(true);
expect(
isSequenceVisibleAtTimelinePosition({sequence, timelinePosition: 59}),
).toBe(true);
expect(
isSequenceVisibleAtTimelinePosition({sequence, timelinePosition: 60}),
).toBe(false);
});

test('video freeze preserves the media frame under the playhead at different playback rates', () => {
const mediaFrameAtSequenceZero = 5;
const playbackRate = 2;
Expand Down
Loading