ronantakizawa/codereview-bench huggingface dataset:
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/components/side-panel.tsx (TypeScript)

Code before:
  useEffect(() => {
    const mathJaxChallenge =
      superBlock === SuperBlocks.RosettaCode ||
      superBlock === SuperBlocks.ProjectEuler ||
      block === 'intermediate-algorithm-scripting';
    initializeMathJax(mathJaxChallenge);
  }, [block, superBlock]);

  return (
    <div
      className='instructions-panel'
      ref={instructionsPanelRef}
      tabIndex={-1}
    >
      {challengeTitle}
      {hasDemo && (
        <>
          <Button size='small' onClick={() => openModal('projectPreview')}>
            {t('buttons.show-demo')}
          </Button>
          <Spacer size='xSmall' />
        </>
      )}
      {challengeDescription}
      {toolPanel}
      <TestSuite tests={tests} />
    </div>
  );
}

SidePanel.displayName = 'SidePanel';

export default connect(mapStateToProps, mapDispatchToProps)(SidePanel);


Review comment (suggestion):
Instead of a span, I would have made this a `button` with whatever styling is needed to make it look like the `a` element we want. I suppose with `role` of button, this does not make much of a difference.

Code after:
      superBlock === SuperBlocks.ProjectEuler ||
      block === 'intermediate-algorithm-scripting';
    initializeMathJax(mathJaxChallenge);
  }, [block, superBlock]);

  return (
    <div
      className='instructions-panel'
      ref={instructionsPanelRef}
      tabIndex={-1}
    >
      {challengeTitle}
      {hasDemo && (
        <p>
          <Trans i18nKey='learn.example-app'>
            <span
              className='example-app-link'
              onClick={() => openModal('projectPreview')}
              role='button'
              tabIndex={0}
              onKeyDown={e => {
                if (e.key === 'Enter' || e.key === ' ') {
                  openModal('projectPreview');
                }
              }}
            ></span>
          </Trans>
        </p>
      )}
      {challengeDescription}
      {toolPanel}
      <TestSuite tests={tests} />
    </div>
  );
}

SidePanel.displayName = 'SidePanel';

export default connect(mapStateToProps, mapDispatchToProps)(SidePanel);
Repository: freeCodeCamp/freeCodeCamp
File: shared/utils/shuffle-array.ts (TypeScript)

Code before:
/** Shuffle array using the Fisher–Yates shuffle algorithm */
export const shuffleArray = <T>(arrToShuffle: Array<T>) => {
  const arr = [...arrToShuffle];

  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));

    // tsc would throw an error here as it can't guarantee that
    // the elements being swapped would have the same type.
    // Type check can be ignored here since the function isn't interested in the element values.
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }

  return arr;
};


Review comment (bug):
I didn't get any errors in the previous PR (it guess the PR only changed client so it was spared?).

But changes to `api` seem to trigger `lint:ts`, which resulted some TS errors:

<img width="726" alt="Screenshot 2024-10-02 at 16 41 03" src="https://github.com/user-attachments/assets/6a953068-a44b-464a-935e-6d4c1c70205b">

I'm not sure if there is a better fix than `@ts-ignore`.

(Also, ESLint didn't give me any errors so I was quite confused/surprised when the CI failed.)

Code after:
/** Shuffle array using the Fisher–Yates shuffle algorithm */
export const shuffleArray = <T>(arrToShuffle: Array<T>) => {
  const arr = [...arrToShuffle];

  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));

    // We know that i and j are within the bounds of the array, TS doesn't
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
    [arr[i], arr[j]] = [arr[j]!, arr[i]!];
  }

  return arr;
};
Repository: freeCodeCamp/freeCodeCamp
File: e2e/lower-jaw.spec.ts (TypeScript)

Code before:
  } else if (browserName === 'webkit') {
    await expect(
      page.getByRole('button', {
        name: 'Submit and go to next challenge (Command + Enter)'
      })
    ).toBeVisible();
  } else {
    await expect(
      page.getByRole('button', {
        name: 'Submit and go to next challenge (Ctrl + Enter)'
      })
    ).toBeVisible();
  }
});

test('Hint text should not contain placeholders `fcc-expected`', async ({
  page,
  isMobile,
  browserName
}) => {
  await page.goto(
    'learn/2022/responsive-web-design/learn-css-transforms-by-building-a-penguin/step-4'
  );
  const editor = getEditors(page);
  const checkButton = page.getByRole('button', { name: 'Check Your Code' });
  await checkButton.click();
  await focusEditor({ page, isMobile });
  await clearEditor({ page, browserName });

  await editor.fill(
    'body{background:linear-gradient(45deg, rgb(118, 201, 255), rgb(247, 255, 222));margin:0;padding:0;width:5%;height:100vh}'
  );

  const failingHint = page.getByTestId('lowerJaw-failing-hint');
  const hintDescriptionElement = failingHint.locator('.hint-description');
  const hintDescription = hintDescriptionElement.locator('p');
  await expect(hintDescription).toContainText(
    'You should give body a width of 100%, but found 5%',
    { useInnerText: true }
  );
});


Review comment (suggestion):
```suggestion
```
This needs to be done after the `fill`

Code after:
  } else if (browserName === 'webkit') {
    await expect(
      page.getByRole('button', {
        name: 'Submit and go to next challenge (Command + Enter)'
      })
    ).toBeVisible();
  } else {
    await expect(
      page.getByRole('button', {
        name: 'Submit and go to next challenge (Ctrl + Enter)'
      })
    ).toBeVisible();
  }
});

test('Hint text should not contain placeholders `fcc-expected`', async ({
  page,
  isMobile,
  browserName
}) => {
  await page.goto(
    'learn/2022/responsive-web-design/learn-css-transforms-by-building-a-penguin/step-4'
  );
  const editor = getEditors(page);
  const checkButton = page.getByRole('button', { name: 'Check Your Code' });
  await focusEditor({ page, isMobile });
  await clearEditor({ page, browserName });

  await editor.fill(
    'body{background:linear-gradient(45deg, rgb(118, 201, 255), rgb(247, 255, 222));margin:0;padding:0;width:5%;height:100vh}'
  );
  await checkButton.click();

  const failingHint = page.getByTestId('lowerJaw-failing-hint');
  const hintDescriptionElement = failingHint.locator('.hint-description');
  const hintDescription = hintDescriptionElement.locator('p');
  await expect(hintDescription).toContainText(
    'You should give body a width of 100%, but found 5%',
    { useInnerText: true }
  );
});
Repository: freeCodeCamp/freeCodeCamp
File: e2e/lower-jaw.spec.ts (TypeScript)

Code before:
    await expect(
      page.getByRole('button', {
        name: 'Submit and go to next challenge (Ctrl + Enter)'
      })
    ).toBeVisible();
  }
});

test('Hint text should not contain placeholders `fcc-expected`', async ({
  page,
  isMobile,
  browserName
}) => {
  await page.goto(
    'learn/2022/responsive-web-design/learn-css-transforms-by-building-a-penguin/step-4'
  );
  const editor = getEditors(page);
  const checkButton = page.getByRole('button', { name: 'Check Your Code' });
  await checkButton.click();
  await focusEditor({ page, isMobile });
  await clearEditor({ page, browserName });

  await editor.fill(
    'body{background:linear-gradient(45deg, rgb(118, 201, 255), rgb(247, 255, 222));margin:0;padding:0;width:5%;height:100vh}'
  );

  const failingHint = page.getByTestId('lowerJaw-failing-hint');
  const hintDescriptionElement = failingHint.locator('.hint-description');
  const hintDescription = hintDescriptionElement.locator('p');
  await expect(hintDescription).toContainText(
    'You should give body a width of 100%, but found 5%',
    { useInnerText: true }
  );
});


Review comment (suggestion):
```suggestion
  await checkButton.click();
  
```


Code after:
    await expect(
      page.getByRole('button', {
        name: 'Submit and go to next challenge (Ctrl + Enter)'
      })
    ).toBeVisible();
  }
});

test('Hint text should not contain placeholders `fcc-expected`', async ({
  page,
  isMobile,
  browserName
}) => {
  await page.goto(
    'learn/2022/responsive-web-design/learn-css-transforms-by-building-a-penguin/step-4'
  );
  const editor = getEditors(page);
  const checkButton = page.getByRole('button', { name: 'Check Your Code' });
  await focusEditor({ page, isMobile });
  await clearEditor({ page, browserName });

  await editor.fill(
    'body{background:linear-gradient(45deg, rgb(118, 201, 255), rgb(247, 255, 222));margin:0;padding:0;width:5%;height:100vh}'
  );
  await checkButton.click();

  const failingHint = page.getByTestId('lowerJaw-failing-hint');
  const hintDescriptionElement = failingHint.locator('.hint-description');
  const hintDescription = hintDescriptionElement.locator('p');
  await expect(hintDescription).toContainText(
    'You should give body a width of 100%, but found 5%',
    { useInnerText: true }
  );
});
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/generic/show.tsx (TypeScript)

Code before:
      challengeType,
      helpCategory
    });
    challengeMounted(challengeMeta.id);
    container.current?.focus();
    // This effect should be run once on mount
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    updateChallengeMeta({
      ...challengeMeta,
      title,
      challengeType,
      helpCategory
    });
    challengeMounted(challengeMeta.id);
  }, [
    title,
    challengeMeta,
    challengeType,
    helpCategory,
    challengeMounted,
    updateChallengeMeta
  ]);

  // video
  const [videoIsLoaded, setVideoIsLoaded] = useState(false);

  const handleVideoIsLoaded = () => {
    setVideoIsLoaded(true);
  };

  // assignments
  const [allAssignmentsCompleted, setAllAssignmentsCompleted] = useState(false);
  const [assignmentsCompleted, setAssignmentsCompleted] = useState(0);

  const handleAssignmentChange = (
    event: React.ChangeEvent<HTMLInputElement>,
    totalAssignments: number
  ) => {
    const newAssignmentsCompleted = event.target.checked
      ? assignmentsCompleted + 1
      : assignmentsCompleted - 1;

    setAssignmentsCompleted(newAssignmentsCompleted);
    setAllAssignmentsCompleted(totalAssignments === newAssignmentsCompleted);
  };

  // submit
  const handleSubmit = () => {

Review comment (suggestion):
```suggestion
  const [assignmentsCompleted, setAssignmentsCompleted] = useState(0);
  const allAssignmentCompleted = assignmentsCompleted === assignments.length;
```

Code after:
  useEffect(() => {
    updateChallengeMeta({
      ...challengeMeta,
      title,
      challengeType,
      helpCategory
    });
    challengeMounted(challengeMeta.id);
  }, [
    title,
    challengeMeta,
    challengeType,
    helpCategory,
    challengeMounted,
    updateChallengeMeta
  ]);

  // video
  const [videoIsLoaded, setVideoIsLoaded] = useState(false);

  const handleVideoIsLoaded = () => {
    setVideoIsLoaded(true);
  };

  // assignments
  const [assignmentsCompleted, setAssignmentsCompleted] = useState(0);
  const allAssignmentsCompleted = assignmentsCompleted === assignments.length;

  const handleAssignmentChange = (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    const isCompleted = event.target.checked; // extract value before target is nullified
    setAssignmentsCompleted(a => (isCompleted ? a + 1 : a - 1));
  };

  // submit
  const handleSubmit = () => {
    if (assignments.length == 0 || allAssignmentsCompleted) {
      openCompletionModal();
    }
  };

  return (
    <Hotkeys
      executeChallenge={handleSubmit}
      containerRef={container}
      nextChallengePath={nextChallengePath}
      prevChallengePath={prevChallengePath}
    >
      <LearnLayout>
        <Helmet
Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Challenges/generic/show.tsx (TypeScript)

Code before:

  useEffect(() => {
    updateChallengeMeta({
      ...challengeMeta,
      title,
      challengeType,
      helpCategory
    });
    challengeMounted(challengeMeta.id);
  }, [
    title,
    challengeMeta,
    challengeType,
    helpCategory,
    challengeMounted,
    updateChallengeMeta
  ]);

  // video
  const [videoIsLoaded, setVideoIsLoaded] = useState(false);

  const handleVideoIsLoaded = () => {
    setVideoIsLoaded(true);
  };

  // assignments
  const [allAssignmentsCompleted, setAllAssignmentsCompleted] = useState(false);
  const [assignmentsCompleted, setAssignmentsCompleted] = useState(0);

  const handleAssignmentChange = (
    event: React.ChangeEvent<HTMLInputElement>,
    totalAssignments: number
  ) => {
    const newAssignmentsCompleted = event.target.checked
      ? assignmentsCompleted + 1
      : assignmentsCompleted - 1;

    setAssignmentsCompleted(newAssignmentsCompleted);
    setAllAssignmentsCompleted(totalAssignments === newAssignmentsCompleted);
  };

  // submit
  const handleSubmit = () => {
    if (assignments.length > 0 && allAssignmentsCompleted) {
      openCompletionModal();
    }
  };

  return (
    <Hotkeys
      executeChallenge={handleSubmit}

Review comment (refactor):
```suggestion
  const handleAssignmentChange = (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    const isCompleted = event.target.checked; // extract value before target is nullified
    setAssignmentsCompleted(a => (isCompleted ? a + 1 : a - 1));
  };
```
This is all we need. Bit out of scope, but there's no need to pass around the event. We can just pass `isCompleted` since that's all this function uses. 

Code after:
  }, [
    title,
    challengeMeta,
    challengeType,
    helpCategory,
    challengeMounted,
    updateChallengeMeta
  ]);

  // video
  const [videoIsLoaded, setVideoIsLoaded] = useState(false);

  const handleVideoIsLoaded = () => {
    setVideoIsLoaded(true);
  };

  // assignments
  const [assignmentsCompleted, setAssignmentsCompleted] = useState(0);
  const allAssignmentsCompleted = assignmentsCompleted === assignments.length;

  const handleAssignmentChange = (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    const isCompleted = event.target.checked; // extract value before target is nullified
    setAssignmentsCompleted(a => (isCompleted ? a + 1 : a - 1));
  };

  // submit
  const handleSubmit = () => {
    if (assignments.length == 0 || allAssignmentsCompleted) {
      openCompletionModal();
    }
  };

  return (
    <Hotkeys
      executeChallenge={handleSubmit}
      containerRef={container}
      nextChallengePath={nextChallengePath}
      prevChallengePath={prevChallengePath}
    >
      <LearnLayout>
        <Helmet
          title={`${blockNameTitle} | ${t('learn.learn')} | freeCodeCamp.org`}
        />
        <Container>
          <Row>
            <Spacer size='medium' />
            <ChallengeTitle
              isCompleted={isChallengeCompleted}
              translationPending={translationPending}
Repository: freeCodeCamp/freeCodeCamp
File: client/src/components/layouts/default.tsx (TypeScript)

Code before:
            crossOrigin='anonymous'
            href={latoBoldURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
            crossOrigin='anonymous'
            href={jpSansRegularURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
            crossOrigin='anonymous'
            href={jpSansLightURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
            crossOrigin='anonymous'
            href={jpSansBoldURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
            crossOrigin='anonymous'
            href={hackZeroSlashRegularURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
            crossOrigin='anonymous'
            href={hackZeroSlashBoldURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
            crossOrigin='anonymous'
            href={hackZeroSlashItalicURL}
            rel='preload'
            type='font/woff'
          />
        </Helmet>
        <div className={`default-layout`}>
          <Header
            fetchState={fetchState}

Review comment (suggestion):
Thanks for the updates.

Could you please insert these `<link>` tags for NotoSansJP only if the clientLocale is Japanese?

We always need Lato. And we additionally need NotoSansJP only for Japanese.

Code after:
          <link
            as='font'
            crossOrigin='anonymous'
            href={latoBoldURL}
            rel='preload'
            type='font/woff'
          />
          {isJapanese && (
            <link
              as='font'
              crossOrigin='anonymous'
              href={jpSansRegularURL}
              rel='preload'
              type='font/woff'
            />
          )}
          {isJapanese && (
            <link
              as='font'
              crossOrigin='anonymous'
              href={jpSansLightURL}
              rel='preload'
              type='font/woff'
            />
          )}
          {isJapanese && (
            <link
              as='font'
              crossOrigin='anonymous'
              href={jpSansBoldURL}
              rel='preload'
              type='font/woff'
            />
          )}

          <link
            as='font'
            crossOrigin='anonymous'
            href={hackZeroSlashRegularURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
            crossOrigin='anonymous'
            href={hackZeroSlashBoldURL}
            rel='preload'
            type='font/woff'
          />
          <link
            as='font'
Repository: freeCodeCamp/freeCodeCamp
File: tools/client-plugins/gatsby-source-challenges/create-challenge-nodes.js (JavaScript)

Code before:
const crypto = require('crypto');

const createdIds = new Set();

function createChallengeNode(
  challenge,
  reporter,
  { isReloading } = { isReloading: false }
) {
  if (!challenge.description) challenge.description = '';
  if (!challenge.instructions) challenge.instructions = '';
  if (!challenge.seed) challenge.seed = '';

  const contentDigest = crypto
    .createHash('md5')
    .update(JSON.stringify(challenge))
    .digest('hex');
  const internal = {
    contentDigest,
    type: challenge.challengeType === 7 ? 'CertificateNode' : 'ChallengeNode'
  };

  if (internal.type === 'ChallengeNode') {
    const { tests = [], block, dashedName, superBlock } = challenge;
    const slug = `/learn/${superBlock}/${block}/${dashedName}`;
    const blockHashSlug = `/learn/${superBlock}/#${block}`;

    challenge.fields = {
      slug,
      blockName: block,
      tests,
      blockHashSlug
    };
  }

  // Challenge id should be unique for CertificateNodes, but not for
  // ChallengeNodes

Review comment (suggestion):
Sorry to be a pain, but this belongs in the `generateChallengeCreator` function in get-challenges. That sets up the challenge, so it's better not to spread the responsibility around.

Also, the output of get-challenges goes through the challenge schema, so we can validate that these properties are defined.

Code after:
const crypto = require('crypto');

const createdIds = new Set();

function createChallengeNode(
  challenge,
  reporter,
  { isReloading } = { isReloading: false }
) {
  const contentDigest = crypto
    .createHash('md5')
    .update(JSON.stringify(challenge))
    .digest('hex');
  const internal = {
    contentDigest,
    type: challenge.challengeType === 7 ? 'CertificateNode' : 'ChallengeNode'
  };

  if (internal.type === 'ChallengeNode') {
    const { tests = [], block, dashedName, superBlock } = challenge;
    const slug = `/learn/${superBlock}/${block}/${dashedName}`;
    const blockHashSlug = `/learn/${superBlock}/#${block}`;

    challenge.fields = {
      slug,
      blockName: block,
      tests,
      blockHashSlug
    };
  }

  // Challenge id should be unique for CertificateNodes, but not for
  // ChallengeNodes
  const id =
    internal.type === 'ChallengeNode' ? challenge.fields.slug : challenge.id;

  if (createdIds.has(id) && !isReloading) {
Repository: freeCodeCamp/freeCodeCamp
File: api/src/exam-environment/routes/exam-environment.ts (TypeScript)

Code before:
/**
 * Handles screenshots, sending them to the screenshot service for storage.
 *
 * Requires token to be validated.
 */
async function postScreenshotHandler(
  this: FastifyInstance,
  _req: UpdateReqType<typeof schemas.examEnvironmentPostScreenshot>,
  reply: FastifyReply
) {
  return reply.code(418);
}

async function getExams(
  this: FastifyInstance,
  req: UpdateReqType<typeof schemas.examEnvironmentExams>,
  reply: FastifyReply
) {
  const user = req.user!;
  const exams = await this.prisma.envExam.findMany();

  const availableExams = exams.map(exam => {
    const isExamPrerequisitesMet = checkPrerequisites(user, true);

    return {
      examId: exam.id,
      config: exam.config,
      canTake: isExamPrerequisitesMet
    };
  });

  return reply.send({
    data: {
      exams: availableExams
    }
  });
}


Review comment (suggestion):
This is explicit enough, seeing as we are returning `exams[].id`

```suggestion
      id: exam.id,
```

Code after:
/**
 * Handles screenshots, sending them to the screenshot service for storage.
 *
 * Requires token to be validated.
 */
async function postScreenshotHandler(
  this: FastifyInstance,
  _req: UpdateReqType<typeof schemas.examEnvironmentPostScreenshot>,
  reply: FastifyReply
) {
  return reply.code(418);
}

async function getExams(
  this: FastifyInstance,
  req: UpdateReqType<typeof schemas.examEnvironmentExams>,
  reply: FastifyReply
) {
  const user = req.user!;
  const exams = await this.prisma.envExam.findMany({
    select: {
      id: true,
      config: true
    }
  });

  const availableExams = exams.map(exam => {
    const isExamPrerequisitesMet = checkPrerequisites(user, true);

    return {
      id: exam.id,
      config: {
        name: exam.config.name,
        note: exam.config.note,
        totalTimeInMS: exam.config.totalTimeInMS
      },
      canTake: isExamPrerequisitesMet
    };
  });

  return reply.send({
    data: {
      exams: availableExams
    }
  });
}
Repository: freeCodeCamp/freeCodeCamp
File: api/src/exam-environment/schemas/exams.ts (TypeScript)

Code before:
import { Type } from '@fastify/type-provider-typebox';
import { STANDARD_ERROR } from '../utils/errors';
export const examEnvironmentExams = {
  headers: Type.Object({
    'exam-environment-authorization-token': Type.String()
  }),
  response: {
    200: Type.Union([
      Type.Object({
        data: Type.Object({
          exams: Type.Array(
            Type.Object({
              examId: Type.String(),
              config: Type.Object({
                name: Type.String(),
                note: Type.String(),
                totalTimeInMS: Type.Number()
              }),
              canTake: Type.Boolean()
            })
          )
        })
      }),
      STANDARD_ERROR
    ])
  }
};


Review comment (suggestion):
```suggestion
              id: Type.String(),
```

Code after:
import { Type } from '@fastify/type-provider-typebox';
import { STANDARD_ERROR } from '../utils/errors';
export const examEnvironmentExams = {
  headers: Type.Object({
    'exam-environment-authorization-token': Type.String()
  }),
  response: {
    200: Type.Union([
      Type.Object({
        data: Type.Object({
          exams: Type.Array(
            Type.Object({
              id: Type.String(),
              config: Type.Object({
                name: Type.String(),
                note: Type.String(),
                totalTimeInMS: Type.Number()
              }),
              canTake: Type.Boolean()
            })
          )
        })
      }),
      STANDARD_ERROR
    ])
  }
};