Review and Stretch Goals

8 min read

You have a working CLI tool. Take a moment — that's a real, useful thing you built from scratch with seven chapters of JavaScript. This final lesson is for review, reflection, and the optional extensions that turn the project from "it works" into something a team would actually adopt. Pick the stretch goals that interest you; skip the ones that don't.

Self-assessment checklist

Run through each line. Answer yes or no. The aim isn't a perfect score — it's to know where the gaps are.

  • Running node qa-datagen.js --type users --count 10 produces a file with exactly 10 user objects.
  • The same command twice produces different data (the generator is actually random).
  • --output output/users.json creates the output/ folder if it doesn't exist.
  • Each user object has all six fields: id, firstName, lastName, email, role, createdAt.
  • Emails look plausible and match the format <first>.<last>@<domain> from the config pools.
  • Running with no --type prints a clear error and exits with a non-zero status code.
  • Running with --type penguins prints a clear error listing the known types.
  • Running with malformed config.json (try adding a trailing comma) prints a clear error.
  • The output file round-trips: JSON.parse(fs.readFileSync("output/users.json", "utf-8")) returns an array of 10 users without throwing.
  • Your code is split across files — generators in generators/, main script in qa-datagen.js — not a single 200-line file.

If any line is "no," that's a clear signal of where to invest the next 30 minutes. Don't move to the stretch goals until each box is ticked.

Reflection questions

Write your answers in a comment at the top of qa-datagen.js — they'll be useful in a year when you re-read this code.

  • Which JavaScript concept was hardest for you? Async? Destructuring? Arrow function this? Naming the harder areas surfaces what to dig deeper into next.
  • Did you use functions to organise your code, or is everything in main()? A main() that's 50 lines long is doing too much. Refactoring into named helpers — parseArgs, loadConfig, generateUser, writeOutput — is the move from "it works" to "I'd be happy if a teammate read this."
  • How would you add a new data type — say, addresses? What lines change in the config? In the dispatcher? In the generators folder? If your answer is "I'd add one file and one switch case," the architecture is right. If it's a sprawling change, the architecture is leaking.
  • What's the single thing you'd improve next? Validation? Tests for the tool itself? A proper argument parser? Documentation? Whatever you name is your next learning project.

Stretch goals

Pick whichever appeals. Each one extends the tool and exercises a specific JavaScript skill from the course.

1. Products and orders generators

Create generators/products.js with one exported generateProduct(config, i) function. Each product needs id, name, price, category, inStock. Add a products block to config.json with pools (names, categories, priceRange). Wire it into the switch in main().

Repeat for ordersid, userId, productId, status, total, placedAt. Orders are interesting because they reference IDs from users and products, so consider how to keep them consistent (load existing fixtures? generate a fresh user-and-product set first?).

Skill: chapter 4 (objects, arrays, array methods).

2. CSV output (--format csv)

A second flag — --format csv — that writes comma-separated rows instead of JSON. The implementation is satisfying to write:

function toCsv(items) {
  if (items.length === 0) return "";
  const headers = Object.keys(items[0]);
  const rows = items.map(item => headers.map(h => JSON.stringify(item[h])).join(","));
  return [headers.join(","), ...rows].join("\n");
}

Two lines of array methods (map, join) replace twenty lines of imperative code. Wire --format csv into main() and switch the file extension and serialisation accordingly.

Skill: chapter 4 (map, Object.keys, spread).

3. Validation

Right now the tool happily generates users with duplicate emails and orders with negative totals. Add a validation pass:

  • Unique emails. Track a Set of seen emails; regenerate any duplicate.
  • Positive prices. Reject anything <= 0.
  • Valid dates. new Date(value) returns Invalid Date (not a thrown error) on bad input — isNaN(date.getTime()) is the canonical check.
  • Numeric --count. Currently --count abc produces an empty file. Reject it explicitly with a clear error.

Throw a single descriptive Error if validation fails after a few retries. Skill: chapters 2, 7 (control flow, error handling).

4. --seed for reproducible output

Math.random() is non-deterministic — every run produces different data. Sometimes you want the same data every time (snapshot tests, debugging a specific failure, sharing a fixture across machines). Accept a --seed <number> flag and use a seeded random function.

A minimal seeded random — Mulberry32, four lines of arithmetic:

function mulberry32(seed) {
  return function () {
    seed = (seed + 0x6D2B79F5) | 0;
    let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

Replace Math.random calls with this seeded version. Same --seed, same output, every run. Skill: closures, chapter 3 (functions returning functions).

5. Coloured console output

Two-line wrappers around ANSI escape codes:

const green = (s) => `\x1b[32m${s}\x1b[0m`;
const red   = (s) => `\x1b[31m${s}\x1b[0m`;
 
console.log(green(`Generated ${count} ${type} → ${output}`));
console.error(red(`qa-datagen: ${error.message}`));

Better signal in CI logs. Some terminals don't support colours — for production CLIs, gate the colours on process.stdout.isTTY.

Skill: template literals (chapter 1).

Where to go next

The course is over. Where you go from here depends on what kind of QA work you want to do.

JavaScript for QA
  • – Cypress with TypeScript
  • – Playwright with TypeScript
  • – Selectors deep-dive
  • – Postman scripts
  • – Supertest
  • – Contract testing
  • – TypeScript for QA
  • – Add types to qa-datagen
  • – Strict null checks
  • CI/CD for tests –
  • Docker for QA –
  • Reporting & dashboards –

Three concrete first picks if you're unsure:

  • Going to do front-end test automation? Cypress or Playwright. Both speak the JavaScript you've just learned. The Cypress commands cheat sheet is a useful reference once you've got a project running.
  • Going to do API testing? Tools like Postman and Supertest lean on fetch, JSON, and async/await — all from chapters 4 and 5. The patterns transfer directly.
  • Want to make this very project safer? Add TypeScript. Every function in qa-datagen would gain types — parseArgs(argv: string[]): Args, generateUser(config: UserConfig, index: number): User — and the compiler would catch a class of bugs your tests never could.

You're done

Eight chapters ago this course started with console.log("Hello from QA!"). You now have a real CLI tool in a real folder structure that exercises every skill in between — variables, control flow, functions, objects, arrays, JSON, async, error handling, modular file organisation. None of it is novelty syntax; it's the actual surface that Cypress, Playwright, Postman, Jest, and every JavaScript-based test tool runs on.

The next bug report you read, the next test you run, the next CI failure you triage — you'll see JavaScript underneath. That's the foundation. Build on it.

// tip to track lessons you complete and pick up where you left off across devices.