ronantakizawa/codereview-bench huggingface dataset:
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/classic/action-row.tsx (TypeScript)

Code before:
  hasPreview: boolean;
  areInstructionsDisplayable: boolean;
  isDailyCodingChallenge: boolean;
  setDailyCodingChallengeLanguage: (
    language: DailyCodingChallengeLanguages
  ) => void;
  showConsole: boolean;
  showNotes: boolean;
  showInstructions: boolean;
  showPreviewPane: boolean;
  showPreviewPortal: boolean;
  togglePane: (pane: string) => void;
}

interface InteractiveEditorProps {
  hasInteractiveEditor: boolean;
  showInteractiveEditor: boolean;
  toggleInteractiveEditor: () => void;
}

type ActionRowProps = ClassicLayoutProps | InteractiveEditorProps;

const ActionRow = (props: ActionRowProps): JSX.Element => {
  const { t } = useTranslation();

  if ('hasInteractiveEditor' in props && props.hasInteractiveEditor) {
    const { toggleInteractiveEditor, showInteractiveEditor } = props;

    return (
      <div className='action-row'>
        <div className='tabs-row'>
          <div className='tabs-row-right'>
            <button
              aria-expanded={!!showInteractiveEditor}
              aria-describedby='interactive-editor-desc'
              onClick={toggleInteractiveEditor}
            >
              {t('learn.editor-tabs.interactive-editor')}
            </button>
            <span id='interactive-editor-desc' className='sr-only'>
              {t('aria.interactive-editor-desc')}
            </span>
          </div>
        </div>
      </div>
    );
  }

  const classicLayoutProps = props as ClassicLayoutProps;
  const {
    togglePane,

Review comment (suggestion):
```suggestion
  if (props.hasInteractiveEditor) {
```
Bit cleaner.

Code after:
  hasPreview: boolean;
  areInstructionsDisplayable: boolean;
  isDailyCodingChallenge: boolean;
  setDailyCodingChallengeLanguage: (
    language: DailyCodingChallengeLanguages
  ) => void;
  showConsole: boolean;
  showNotes: boolean;
  showInstructions: boolean;
  showPreviewPane: boolean;
  showPreviewPortal: boolean;
  togglePane: (pane: string) => void;
  hasInteractiveEditor?: never;
}

interface InteractiveEditorProps {
  hasInteractiveEditor: true;
  showInteractiveEditor: boolean;
  toggleInteractiveEditor: () => void;
}

type ActionRowProps = ClassicLayoutProps | InteractiveEditorProps;

const ActionRow = (props: ActionRowProps): JSX.Element => {
  const { t } = useTranslation();

  if (props.hasInteractiveEditor) {
    const { toggleInteractiveEditor, showInteractiveEditor } = props;

    return (
      <div className='action-row'>
        <div className='tabs-row'>
          <div className='tabs-row-right'>
            <button
              aria-expanded={!!showInteractiveEditor}
              aria-describedby='interactive-editor-desc'
              onClick={toggleInteractiveEditor}
            >
              {t('learn.editor-tabs.interactive-editor')}
            </button>
            <span id='interactive-editor-desc' className='sr-only'>
              {t('aria.interactive-editor-desc')}
            </span>
          </div>
        </div>
      </div>
    );
  }

  const {
    togglePane,
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/classic/action-row.tsx (TypeScript)

Code before:
  const { t } = useTranslation();

  if ('hasInteractiveEditor' in props && props.hasInteractiveEditor) {
    const { toggleInteractiveEditor, showInteractiveEditor } = props;

    return (
      <div className='action-row'>
        <div className='tabs-row'>
          <div className='tabs-row-right'>
            <button
              aria-expanded={!!showInteractiveEditor}
              aria-describedby='interactive-editor-desc'
              onClick={toggleInteractiveEditor}
            >
              {t('learn.editor-tabs.interactive-editor')}
            </button>
            <span id='interactive-editor-desc' className='sr-only'>
              {t('aria.interactive-editor-desc')}
            </span>
          </div>
        </div>
      </div>
    );
  }

  const classicLayoutProps = props as ClassicLayoutProps;
  const {
    togglePane,
    hasPreview,
    hasNotes,
    areInstructionsDisplayable,
    showConsole,
    showNotes,
    showInstructions,
    showPreviewPane,
    showPreviewPortal,
    isDailyCodingChallenge,
    dailyCodingChallengeLanguage,
    setDailyCodingChallengeLanguage
  } = classicLayoutProps;

  // sets screen reader text for the two preview buttons
  function getPreviewBtnsSrText() {
    // no preview open
    const previewBtnsSrText = {
      pane: t('aria.show-preview'),
      portal: t('aria.open-preview-in-new-window')
    };

    // preview open in main window
    if (showPreviewPane && !showPreviewPortal) {

Review comment (suggestion):
```suggestion
```
No longer necessary, since TS can infer the type now.

Code after:
const ActionRow = (props: ActionRowProps): JSX.Element => {
  const { t } = useTranslation();

  if (props.hasInteractiveEditor) {
    const { toggleInteractiveEditor, showInteractiveEditor } = props;

    return (
      <div className='action-row'>
        <div className='tabs-row'>
          <div className='tabs-row-right'>
            <button
              aria-expanded={!!showInteractiveEditor}
              aria-describedby='interactive-editor-desc'
              onClick={toggleInteractiveEditor}
            >
              {t('learn.editor-tabs.interactive-editor')}
            </button>
            <span id='interactive-editor-desc' className='sr-only'>
              {t('aria.interactive-editor-desc')}
            </span>
          </div>
        </div>
      </div>
    );
  }

  const {
    togglePane,
    hasPreview,
    hasNotes,
    areInstructionsDisplayable,
    showConsole,
    showNotes,
    showInstructions,
    showPreviewPane,
    showPreviewPortal,
    isDailyCodingChallenge,
    dailyCodingChallengeLanguage,
    setDailyCodingChallengeLanguage
  } = props;

  // sets screen reader text for the two preview buttons
  function getPreviewBtnsSrText() {
    // no preview open
    const previewBtnsSrText = {
      pane: t('aria.show-preview'),
      portal: t('aria.open-preview-in-new-window')
    };

    // preview open in main window
    if (showPreviewPane && !showPreviewPortal) {
Repository: freeCodeCamp/freeCodeCamp
File: api/src/schemas/user/get-session-user.ts (TypeScript)

Code before:
import { Type } from '@fastify/type-provider-typebox';
import { DailyCodingChallengeLanguage } from '@prisma/client';
import { examResults, profileUI, savedChallenge } from '../types.js';

const languages = Object.values(DailyCodingChallengeLanguage).map(k =>
  Type.Literal(k)
) as [
  ReturnType<typeof Type.Literal<'javascript'>>,
  ReturnType<typeof Type.Literal<'python'>>
];

export const getSessionUser = {
  response: {
    200: Type.Object({
      user: Type.Record(
        Type.String(),
        Type.Object({
          about: Type.String(),
          acceptedPrivacyTerms: Type.Boolean(),
          calendar: Type.Record(Type.Number(), Type.Literal(1)),
          completedChallenges: Type.Array(
            Type.Object({
              id: Type.String(),
              completedDate: Type.Number(),
              solution: Type.Optional(Type.String()),
              githubLink: Type.Optional(Type.String()),
              challengeType: Type.Optional(Type.Number()),
              // Technically, files is optional, but the db default was [] and
              // the client treats null, undefined and [] equivalently.
              // TODO(Post-MVP): make this optional.
              files: Type.Array(
                Type.Object({
                  contents: Type.String(),
                  key: Type.String(),
                  ext: Type.String(),

Review comment (bug):
```suggestion
const languages = Type.Array(
  Type.Union([Type.Literal('javascript'), Type.Literal('python')])
);
```
If we changes the languages, TS will tell us something is wrong. There's no need to import from prisma types at all.

Code after:
import { Type } from '@fastify/type-provider-typebox';
import { examResults, profileUI, savedChallenge } from '../types.js';

const languages = Type.Array(
  Type.Union([Type.Literal('javascript'), Type.Literal('python')])
);

export const getSessionUser = {
  response: {
    200: Type.Object({
      user: Type.Record(
        Type.String(),
        Type.Object({
          about: Type.String(),
          acceptedPrivacyTerms: Type.Boolean(),
          calendar: Type.Record(Type.Number(), Type.Literal(1)),
          completedChallenges: Type.Array(
            Type.Object({
              id: Type.String(),
              completedDate: Type.Number(),
              solution: Type.Optional(Type.String()),
              githubLink: Type.Optional(Type.String()),
              challengeType: Type.Optional(Type.Number()),
              // Technically, files is optional, but the db default was [] and
              // the client treats null, undefined and [] equivalently.
              // TODO(Post-MVP): make this optional.
              files: Type.Array(
                Type.Object({
                  contents: Type.String(),
                  key: Type.String(),
                  ext: Type.String(),
                  name: Type.String(),
                  path: Type.Optional(Type.String())
                })
              ),
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/exam-download/show.tsx (TypeScript)

Code before:
    challengeNode: ChallengeNode;
    allChallengeNode: { nodes: ChallengeNode[] };
  };
  completedChallenges: CompletedChallenge[];
  isChallengeCompleted: boolean;
  isSignedIn: boolean;
}

function ShowExamDownload({
  data: {
    challengeNode: {
      challenge: { id, title, translationPending }
    },
    allChallengeNode: { nodes }
  },
  completedChallenges,
  isChallengeCompleted,
  isSignedIn
}: ShowExamDownloadProps): JSX.Element {
  const [latestVersion, setLatestVersion] = useState<string | null>(null);

  const [downloadLink, setDownloadLink] = useState<string | undefined>('');
  const [downloadLinks, setDownloadLinks] = useState<string[]>([]);
  const [missingPrerequisites, setMissingPrerequisites] = useState<
    PrerequisiteChallenge[]
  >([]);

  const getExamsQuery = examAttempts.useGetExamsQuery();
  const examIdsQuery = examAttempts.useGetExamIdsByChallengeIdQuery(id);

  useEffect(() => {
    if (!examIdsQuery.data) {
      return;
    }

    const examId = examIdsQuery.data.at(0)?.examId;
    if (examId === undefined) {
      return;
    }

    if (!getExamsQuery.data) {
      return;
    }

    const exam = getExamsQuery.data.find(examItem => examItem.id === examId);
    if (!exam) {
      return;
    }

    const unmetPrerequisites = exam.prerequisites.filter(
      prereq => !completedChallenges.some(challenge => challenge.id === prereq)

Review comment (suggestion):
```suggestion
```
It's not necessary, since `missingPrerequisites` can be derived from existing props and state

Code after:
  isChallengeCompleted: boolean;
  isSignedIn: boolean;
}

function ShowExamDownload({
  data: {
    challengeNode: {
      challenge: { id, title, translationPending }
    },
    allChallengeNode: { nodes }
  },
  completedChallenges,
  isChallengeCompleted,
  isSignedIn
}: ShowExamDownloadProps): JSX.Element {
  const [latestVersion, setLatestVersion] = useState<string | null>(null);

  const [downloadLink, setDownloadLink] = useState<string | undefined>('');
  const [downloadLinks, setDownloadLinks] = useState<string[]>([]);

  const getExamsQuery = examAttempts.useGetExamsQuery();
  const examIdsQuery = examAttempts.useGetExamIdsByChallengeIdQuery(id);

  const os = useDetectOS();

  const { t } = useTranslation();

  function handleDownloadLink(downloadLinks: string[]) {
    const win = downloadLinks.find(link => link.match(/\.exe/));
    const macARM = downloadLinks.find(
      link => link.match(/aarch64/) && link.match(/\.dmg/)
    );
    const macX64 = downloadLinks.find(
      link => link.match(/x64/) && link.match(/\.dmg/)
    );

    const linuxARM = downloadLinks.find(
      link => link.match(/aarch64/) && link.match(/tar\.gz/)
    );

    const linuxX64 = downloadLinks.find(
      link => link.match(/amd64/) && link.match(/AppImage/)
    );

    if (os.os === 'WIN') {
      if (isEmpty(win)) return '';

      return win;
    }

    if (os.os === 'MAC') {
Repository: freeCodeCamp/freeCodeCamp
File: e2e/preview.spec.ts (TypeScript)

Code before:
import { expect, test } from '@playwright/test';
import translations from '../client/i18n/locales/english/translations.json';
import { focusEditor } from './utils/editor';

test.beforeEach(async ({ page }) => {
  await page.goto(
    '/learn/2022/responsive-web-design/learn-html-by-building-a-cat-photo-app/step-3'
  );
});

test.describe('Challenge Preview Component', () => {
  test('should render correct output of default code', async ({
    page,
    isMobile
  }) => {
    if (isMobile) {
      await page
        .getByRole('tab', { name: translations.learn['editor-tabs'].preview })
        .click();
    }
    await expect(
      page
        .frameLocator('.challenge-preview-frame')
        .first()
        .getByRole('heading', { name: 'CatPhotoApp' })
    ).toBeVisible();
  });

  test('should render correct output of changed code', async ({
    page,
    isMobile
  }) => {
    await focusEditor({ page, isMobile });

    await page.keyboard.insertText('<h1>FreeCodeCamp</h1>');
    if (isMobile) {
      await page
        .getByRole('tab', { name: translations.learn['editor-tabs'].preview })
        .click();
    }
    await expect(
      page
        .frameLocator('.challenge-preview-frame')
        .first()
        .getByRole('heading', { name: 'FreeCodeCamp' })
    ).toBeVisible();
  });
});


Review comment (suggestion):
This method is deprecated. We also don't need it as there is only one iframe in the UI.

<img width="626" height="265" alt="Image" src="https://github.com/user-attachments/assets/882ca0b0-b0d2-40ad-a936-e9f3327915cd" />

Code after:
import { expect, test } from '@playwright/test';
import translations from '../client/i18n/locales/english/translations.json';
import { focusEditor } from './utils/editor';

test.beforeEach(async ({ page }) => {
  await page.goto(
    '/learn/2022/responsive-web-design/learn-html-by-building-a-cat-photo-app/step-3'
  );
});

test.describe('Challenge Preview Component', () => {
  test('should render correct output for default and changed code', async ({
    page,
    isMobile
  }) => {
    if (isMobile) {
      await page
        .getByRole('tab', { name: translations.learn['editor-tabs'].preview })
        .click();
    }

    // Check default code output
    await expect(
      page
        .frameLocator('.challenge-preview-frame')
        .getByRole('heading', { name: 'CatPhotoApp' })
    ).toBeVisible();

    // Change code
    await focusEditor({ page, isMobile });
    await page.keyboard.insertText('<h1>FreeCodeCamp</h1>');
    if (isMobile) {
      await page
        .getByRole('tab', { name: translations.learn['editor-tabs'].preview })
        .click();
    }

    // Check changed code output
    await expect(
      page
        .frameLocator('.challenge-preview-frame')
        .getByRole('heading', { name: 'FreeCodeCamp' })
    ).toBeVisible();
  });
});
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/components/scene/scene-helpers.test.ts (TypeScript)

Code before:
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        '\n<strong>Naomi</strong>: Hello\n' +
          '\n<strong>Naomi</strong>: How are you?\n'
      );
    });

    it('should handle dialogue with special characters', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 0,
          dialogue: {
            text: "I'm fine, thanks! How about you?",
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        "\n<strong>Naomi</strong>: I'm fine, thanks! How about you?\n"
      );
    });

    it('should handle dialogue with punctuation', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 0,
          dialogue: {
            text: 'What? No way!',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe('\n<strong>Naomi</strong>: What? No way!\n');
    });

    it('should handle dialogue with numbers', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 0,
          dialogue: {
            text: 'I have 3 apples and 5 oranges.',
            align: 'left'
          }
        }
      ];

Review comment (suggestion):
This test case is probably redundant. The `should build transcript from multiple commands with dialogue` already includes dialogue text with a punctuation mark.

Code after:
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        '\n<strong>Naomi</strong>: Hello\n' +
          '\n<strong>Naomi</strong>: How are you?\n'
      );
    });

    it('should handle dialogue with special characters', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: 'He said "I love TypeScript!" & she replied, \'I prefer Ruby! #ruby #rubyonrails\'',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        '\n<strong>Naomi</strong>: He said "I love TypeScript!" & she replied, \'I prefer Ruby! #ruby #rubyonrails\'\n'
      );
    });

    it('should handle empty dialogue text', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: '',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe('\n<strong>Naomi</strong>: \n');
    });

    it('should handle dialogue with newlines', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: 'Hello\nworld',
            align: 'left'
          }
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/components/scene/scene-helpers.test.ts (TypeScript)

Code before:
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        "\n<strong>Naomi</strong>: I'm fine, thanks! How about you?\n"
      );
    });

    it('should handle dialogue with punctuation', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 0,
          dialogue: {
            text: 'What? No way!',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe('\n<strong>Naomi</strong>: What? No way!\n');
    });

    it('should handle dialogue with numbers', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 0,
          dialogue: {
            text: 'I have 3 apples and 5 oranges.',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        '\n<strong>Naomi</strong>: I have 3 apples and 5 oranges.\n'
      );
    });

    it('should handle empty dialogue text', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 0,
          dialogue: {
            text: '',
            align: 'left'
          }

Review comment (suggestion):
We should fold this into either `should build transcript from single command with dialogue` or `should build transcript from multiple commands with dialogue`. Let's just have a dialogue text with a number in those cases.



Code after:
          dialogue: {
            text: 'He said "I love TypeScript!" & she replied, \'I prefer Ruby! #ruby #rubyonrails\'',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        '\n<strong>Naomi</strong>: He said "I love TypeScript!" & she replied, \'I prefer Ruby! #ruby #rubyonrails\'\n'
      );
    });

    it('should handle empty dialogue text', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: '',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe('\n<strong>Naomi</strong>: \n');
    });

    it('should handle dialogue with newlines', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: 'Hello\nworld',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe('\n<strong>Naomi</strong>: Hello\nworld\n');
    });

    it('should handle multiple consecutive commands from same character', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: 'First line',
            align: 'left'
          }
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/components/scene/scene-helpers.test.ts (TypeScript)

Code before:
import { describe, it, expect } from 'vitest';
import { SceneCommand } from '../../../../redux/prop-types';
import { buildTranscript } from './scene-helpers';

describe('scene-helpers', () => {
  describe('buildTranscript', () => {
    it('should return empty string for empty commands array', () => {
      const commands: SceneCommand[] = [];
      const result = buildTranscript(commands);
      expect(result).toBe('');
    });

    it('should build transcript from single command with dialogue', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: 'Hello world, I have 5 cats',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        '\n<strong>Naomi</strong>: Hello world, I have 5 cats\n'
      );

Review comment (suggestion):
```suggestion
import { type SceneCommand } from '../../../../redux/prop-types';
```

Code after:
import { describe, it, expect } from 'vitest';
import { type SceneCommand } from '../../../../redux/prop-types';
import { buildTranscript } from './scene-helpers';

describe('scene-helpers', () => {
  describe('buildTranscript', () => {
    it('should return empty string for empty commands array', () => {
      const commands: SceneCommand[] = [];
      const result = buildTranscript(commands);
      expect(result).toBe('');
    });

    it('should build transcript from single command with dialogue', () => {
      const commands: SceneCommand[] = [
        {
          character: 'Naomi',
          startTime: 1,
          dialogue: {
            text: 'Hello world, I have 5 cats',
            align: 'left'
          }
        }
      ];
      const result = buildTranscript(commands);
      expect(result).toBe(
        '\n<strong>Naomi</strong>: Hello world, I have 5 cats\n'
      );