in example/src/screens/TestPlan.tsx [263:602]
export function TestPlan() {
const snapshot = useSnapshot(planState);
const [testPlan, setTestPlan] = React.useState(defaultPlan);
const playerRef = React.useRef<IVSPlayerRef>(null);
React.useEffect(() => {
return () => {
planState.url = '';
planState.props = {};
planState.events = new Set();
planState.inputs = [];
planState.actions = {};
planState.qualities = [];
planState.prefetchurls = [];
planState.prefetchsources = {};
};
}, []);
function runplan() {
const plandata = parse(testPlan);
Object.keys(plandata).forEach((name) => {
const lname = name.toLowerCase();
const value = plandata[name];
switch (lname) {
case 'url':
if (typeof value === 'string') {
planState.url = value;
} else {
// throw error with example input
}
break;
case 'inputs':
if (Array.isArray(value)) {
const newInputs: PlanInput[] = [];
value.forEach((input) => {
if (typeof input === 'string') {
const template = InputTemplates[input];
if (template) {
newInputs.push({ name: input, ...template });
}
} else {
Object.entries(input).forEach(([prop, data]) => {
const template = InputTemplates[prop];
if (template) {
planState.props[prop] = data;
newInputs.push({ name: prop, ...template });
}
});
}
});
planState.inputs = newInputs;
} else {
// throw error with example input
}
break;
case 'events':
if (Array.isArray(value)) {
const newEvents = new Set<string>();
value.forEach((event) => {
if (typeof event === 'string') {
newEvents.add(event);
} else {
// throw error with example input
}
});
planState.events = newEvents;
} else {
// throw error with example input
}
break;
case 'prefetch':
if (Array.isArray(value)) {
const newPrefetchurls: string[] = [];
value.forEach((input) => {
if (typeof input === 'string') {
newPrefetchurls.push(input);
planState.prefetchsources[input] =
playerRef.current?.preload(input);
} else {
// throw error with example input
}
});
planState.prefetchurls = newPrefetchurls;
planState.inputs.push({
name: 'prefetch',
type: PlanInputType.Action,
icon: 'web',
args: [PlanInputActionArg.Prefetch],
});
} else {
// throw error with example input
}
break;
default:
console.info(lname, plandata[name]);
break;
}
});
if (!planState.url) {
planState.url = defaultUrl;
}
}
function renderinput(input: PlanInput) {
const name = input.name ?? '';
const value = snapshot.props[name];
switch (input.type) {
case PlanInputType.Boolean:
return (
<>
<Subheading>{name}</Subheading>
<Chip
testID={name}
selected={!!value}
onPress={() => {
planState.props[name] = !value;
}}
>
{JSON.stringify(value)}
</Chip>
</>
);
case PlanInputType.Number:
return (
<>
<Subheading>{name}</Subheading>
<TextInput
testID={name}
dense
value={`${value}`}
onChangeText={(text) => {
const next = parseFloat(text);
planState.props[name] = Number.isNaN(next) ? 0 : next;
}}
/>
</>
);
case PlanInputType.Options:
return (
<>
<Subheading>{name}</Subheading>
{(input.options ?? []).map((option, index) => {
return (
<Chip
key={index}
testID={`${name}:${option.name}`}
selected={option.value === value}
onPress={() => {
planState.props[name] = option.value;
}}
>
{logstring(option.name, JSON.stringify(option.value))}
</Chip>
);
})}
</>
);
case PlanInputType.Quality:
return (
<>
<Subheading>{name}</Subheading>
<Chip
testID={`${name}:auto:-1`}
selected={value === undefined}
onPress={() => {
planState.props[name] = undefined;
}}
>
{logstring('auto', 'undefined')}
</Chip>
{snapshot.qualities.map((option, index) => {
return (
<Chip
key={index}
testID={`${name}:${option.name}`}
selected={qualitymatch(option, value)}
onPress={() => {
planState.props[name] = option;
}}
>
{logstring(option.name, JSON.stringify(option))}
</Chip>
);
})}
</>
);
case PlanInputType.Action:
return (
<>
<Subheading>{name}</Subheading>
<View style={styles.row}>
{(input.args ?? []).map((arg, i) => {
switch (arg) {
case PlanInputActionArg.Number:
return (
<TextInput
key={i}
testID={`${name}:${i}`}
dense
style={styles.rowInput}
value={`${planState.actions?.[name]?.[i] ?? ''}`}
onChangeText={(text) => {
if (!snapshot.actions[name]) {
planState.actions[name] = {};
}
const next = parseFloat(text);
planState.actions[name][i] = Number.isNaN(next)
? 0
: next;
}}
/>
);
case PlanInputActionArg.String:
return (
<TextInput
key={i}
testID={`${name}:${i}`}
dense
style={styles.rowInput}
value={planState.actions?.[name]?.[i] ?? ''}
onChangeText={(text) => {
if (!snapshot.actions[name]) {
planState.actions[name] = {};
}
planState.actions[name][i] = text;
}}
/>
);
case PlanInputActionArg.Prefetch:
return (
<View style={styles.col}>
{snapshot.prefetchurls.map((url, index) => {
return (
<View key={url} style={styles.row}>
<IconButton
testID={`${name}:${index}`}
icon={
snapshot.prefetchsources[url] !== undefined
? 'web'
: 'warning'
}
onPress={() => {
const maybesource =
snapshot.prefetchsources[url];
if (
maybesource !== undefined &&
playerRef.current
) {
playerRef.current.loadSource(maybesource);
}
}}
/>
<Text>{url}</Text>
</View>
);
})}
</View>
);
default:
return null;
}
})}
{name !== 'prefetch' && (
<ToggleButton
// @ts-expect-error docs say this prop exists?
testID={name}
icon={input.icon ?? ''}
status="checked"
onPress={() => {
if (!playerRef.current) {
return;
}
switch (name) {
case 'play':
playerRef.current.play();
break;
case 'pause':
playerRef.current.pause();
break;
case 'seekTo':
playerRef.current.seekTo(snapshot.actions[name][0]);
break;
case 'setOrigin':
playerRef.current.setOrigin(snapshot.actions[name][0]);
break;
case 'togglePip':
playerRef.current.togglePip();
break;
}
}}
/>
)}
</View>
</>
);
}
}
return (
<ScrollView style={styles.container}>
<View style={styles.player} testID="player">
<Player
streamUrl={snapshot.prefetchurls.length ? '' : snapshot.url}
playerRef={playerRef}
{...snapshot.props}
/>
</View>
<View style={styles.config}>
{snapshot.inputs.map((input, index) => (
<View key={input.name ?? index} style={styles.input}>
{renderinput(input as PlanInput)}
</View>
))}
<View style={styles.input}>
<Button testID="runPlan" mode="contained" onPress={runplan}>
Run Plan
</Button>
</View>
<TextInput
testID="testPlan"
style={styles.testPlan}
label="Test Plan"
dense
multiline
value={testPlan}
spellCheck={false}
autoCorrect={false}
autoCapitalize="none"
onChangeText={setTestPlan}
/>
</View>
</ScrollView>
);
}