Skip to content

How to Work on Lab Challenges

Labs are a type of challenge that presents an empty or almost empty editor to the camper, and a list of user stories to satisfy.

Lab descriptions should have this format:

[introduction]
**Objective:** Fulfill the user stories below and get all the tests to pass to complete the lab.
**User Stories:**
[numbered list with all the details needed to pass the lab]
Note/Hint: (this is optional)

These challenges are used to allow for practice and reinforce concepts and practice problem solving.

It can be that in the user stories there is written something that is not tested (for various reasons, including test limitations), but everything tested must be written in the user stories.

The demo projects (the code under the solution header in the markdown file) can be more involved, it should be a good example of what could be created going above and beyond the user stories. For the beginning labs, the demo projects are usually simpler, to avoid overwhelming the campers.

Labs that have a visual output (i.e. HTML-based) can have a demo project, in which case the frontmatter includes demoType: onClick. It is not mandatory, if the project design does not require it it can be omitted.

You can create the lab with pnpm run create-new-project. The CLI will ask you which certification, chapter, module, and position the lab belongs to. If you are unsure about these concepts, refer to the curriculum file structure page.

The metadata for labs require a blockLabel property with a value of lab, and a blockLayout with a value of link.

Labs have a dashedName starting with lab-, and titles follow the format “Build / Implement / Design / Debug [Name]”.

Each lab is a single Markdown file. The challengeType and seed language depend on the type of lab:

challengeTypeLab typeSeed language(s)Solution language(s)
25HTML/CSS(/JS/JSX)html + css, or + js, or + jsxsame combination
26JavaScriptjsjs
27Pythonpypy
13Bash/SQL/etc.no seed — runs via a CodeRoad url fieldnone

saveSubmissionToDB: true is only present in cert-project labs.

Here is the template for a JavaScript lab:

---
id: <ObjectId>
title: Build / Implement / Design / Debug [Name]
challengeType: X
dashedName: lab-name
demoType: onClick
---
# --description--
**Objective:** Fulfill the user stories below and get all the tests to pass to complete the lab.
**User Stories:**
1. ...
# --hints--
Description of what the test checks.
```js
// test code
```
# --seed--
## --seed-contents--
```lang
```
# --solutions--
```lang
// solution code
```

Hooks are optional code blocks that run at specific points during test execution. They can be used to set up shared state, install fake timers, or clean up after tests.

Hooks are supported for lab types 25, 26, and 27.

Four hooks are available:

HookWhen it runs
--before-all--Once, before any test runs
--before-each--Before each individual test
--after-each--After each individual test (in a finally block, so it always runs even if the test fails)
--after-all--Once, after all tests have finished

Execution order:

For challenges that have an HTML file, --before-all-- is injected as a <script> tag into the sandboxed iframe before the user’s HTML is parsed, so it runs before user code. It has full access to DOM APIs and global test helpers like __FakeTimers and $ (jQuery). The user’s code then renders into the page, and each test runs against the live DOM:

1. --before-all--
2. user code (evaluated once)
3. (for each test): --before-each-- → test → --after-each--
4.--after-all--

For challenges that do not have an HTML file, --before-all-- runs once before any test. However, because user code is re-evaluated as part of each individual test (concatenated with --before-each-- and the test in a single eval), variables declared in --before-all-- will not be in scope during tests. To share state across tests, assign to the global object directly (e.g. globalThis.x = 1 instead of let x = 1):

1. --before-all--
2. (for each test): --before-each-- → user code → test → --after-each--
3. --after-all--

Syntax:

Hooks use the same # --hook-name-- heading syntax as other sections. Each hook must contain exactly one code block. Hooks are placed after --description-- and before --hints--:

# --description--
...
# --before-all--
```js
// Runs once before any test. Set up shared state here.
let clock = __FakeTimers.install();
```
# --before-each--
```js
// Runs before each test.
```
# --after-each--
```js
// Runs after each test, even if it fails.
```
# --after-all--
```js
// Runs once after all tests. Clean up here.
clock.uninstall();
```
# --hints--
...

After you’ve committed your changes, check here for how to open a Pull Request.