Repository: microsoft/playwright
File: packages/trace-viewer/src/ui/uiModeView.tsx (TypeScript)

Code before:
      newFilter.set(newFilter.entries().next().value![0], true);
    if (projectFilters.size !== newFilter.size || [...projectFilters].some(([k, v]) => newFilter.get(k) !== v))
      setProjectFilters(newFilter);
  }, [projectFilters, testModel]);

  // Update progress.
  React.useEffect(() => {
    if (isRunningTest && testModel?.progress)
      setProgress(testModel.progress);
    else if (!testModel)
      setProgress(undefined);
  }, [testModel, isRunningTest]);

  // Test tree is built from the model and filters.
  const { testTree } = React.useMemo(() => {
    if (!testModel)
      return { testTree: new TestTree('', new TeleSuite('', 'root'), [], projectFilters, queryParams.pathSeparator, mergeFiles) };
    const testTree = new TestTree('', testModel.rootSuite, testModel.loadErrors, projectFilters, queryParams.pathSeparator, mergeFiles);
    testTree.filterTree(filterText, statusFilters, isRunningTest ? runningState?.testIds : undefined);
    testTree.sortAndPropagateStatus();
    testTree.shortenRoot();
    testTree.flattenForSingleProject();
    return { testTree };
  }, [filterText, testModel, statusFilters, projectFilters, runningState, isRunningTest, mergeFiles]);

  const runTests = React.useCallback((mode: 'queue-if-busy' | 'bounce-if-busy', testTree: TestTree, testItems: Iterable<TreeItem>) => {
    if (!testServerConnection || !testModel)
      return;
    if (mode === 'bounce-if-busy' && isRunningTest)
      return;

    for (const testItem of testItems) {
      const { testIds, locations } = testTree.collectTestIds(testItem);
      for (const testId of testIds)
        runTestBacklog.current.testIds.add(testId);
      for (const location of locations)
        runTestBacklog.current.locations.add(location);
    }
    commandQueue.current = commandQueue.current.then(async () => {
      const { testIds, locations } = runTestBacklog.current;
      runTestBacklog.current = { testIds: new Set(), locations: new Set() };
      if (!testIds.size)
        return;

      // Clear test results.
      {
        for (const test of testModel.rootSuite?.allTests() || []) {
          if (testIds.has(test.id)) {
            test.results = [];
            const result = (test as TeleTestCase)._createTestResult('pending');
            (result as any)[statusEx] = 'scheduled';

Review comment (suggestion):
I don't like that we pass `TestTree` here, especially when it differs from `testTree` declared on the line 249. Can we pass `testIds` + `locations` instead? Perhaps introduce a type for this pair.

Code after:
      newFilter.set(newFilter.entries().next().value![0], true);
    if (projectFilters.size !== newFilter.size || [...projectFilters].some(([k, v]) => newFilter.get(k) !== v))
      setProjectFilters(newFilter);
  }, [projectFilters, testModel]);

  // Update progress.
  React.useEffect(() => {
    if (isRunningTest && testModel?.progress)
      setProgress(testModel.progress);
    else if (!testModel)
      setProgress(undefined);
  }, [testModel, isRunningTest]);

  // Test tree is built from the model and filters.
  const { testTree } = React.useMemo(() => {
    if (!testModel)
      return { testTree: new TestTree('', new TeleSuite('', 'root'), [], projectFilters, queryParams.pathSeparator, mergeFiles) };
    const testTree = new TestTree('', testModel.rootSuite, testModel.loadErrors, projectFilters, queryParams.pathSeparator, mergeFiles);
    testTree.filterTree(filterText, statusFilters, isRunningTest ? runningState?.testIds : undefined);
    testTree.sortAndPropagateStatus();
    testTree.shortenRoot();
    testTree.flattenForSingleProject();
    return { testTree };
  }, [filterText, testModel, statusFilters, projectFilters, runningState, isRunningTest, mergeFiles]);

  const runTests = React.useCallback((mode: 'queue-if-busy' | 'bounce-if-busy', filter: { testIds: Iterable<string>, locations: Iterable<string> }) => {
    if (!testServerConnection || !testModel)
      return;
    if (mode === 'bounce-if-busy' && isRunningTest)
      return;

    for (const testId of filter.testIds)
      runTestBacklog.current.testIds.add(testId);
    for (const location of filter.locations)
      runTestBacklog.current.locations.add(location);
    commandQueue.current = commandQueue.current.then(async () => {
      const { testIds, locations } = runTestBacklog.current;
      runTestBacklog.current = { testIds: new Set(), locations: new Set() };
      if (!testIds.size)
        return;

      // Clear test results.
      {
        for (const test of testModel.rootSuite?.allTests() || []) {
          if (testIds.has(test.id)) {
            test.results = [];
            const result = (test as TeleTestCase)._createTestResult('pending');
            (result as any)[statusEx] = 'scheduled';
          }
        }
        setTestModel({ ...testModel });