export function TestPlan()

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>
  );
}