History & Walking Skeleton

Marketplace Project: Starting with the Marketplace


Learning Objectives

  • You can establish a relational marketplace through reversible dbmate migrations.
  • You can carry seller and category relationships through SQL, API JSON, and browser rendering.
  • You can write regression tests and verify the cumulative marketplace project across its boundaries.

We have followed a stored todo through the API and into the browser. Now you will use that same path to build the first version of a campus marketplace. Each listing belongs to a seller and a category, so we will need to carry those relationships from the database to the page. This first version is a local, read-only application with synthetic development records. Forms, authentication, and auctions come later.

You will build the cumulative marketplace project in a local marketplace repository that is separate from the practice application. The first card provides the course-managed Git repository and explains how to clone it into marketplace/. Use that same local marketplace repository for these six milestones and all later marketplace milestones. Each handout contains the complete requirements, local checks, and submission instructions for its increment. Submit the marketplace project by committing and pushing from the marketplace repository.

Later cards emphasize continuing in the same marketplace repository. They also offer a reference branch containing an instructor example through the previous milestone, available after that milestone passes. The handouts explain how to inspect it without replacing your work. A newly cloned copy of your marketplace repository instead retrieves your own pushed commits; it is not a sample solution.

Database foundation

Start with the data: the marketplace needs a schema and related example records before we can show any listings. Use dbmate to check that your first change can be applied, rolled back, and reapplied while preserving the neutral baseline.

Marketplace milestone 1: database foundation

0 / 60 points

Objective and reference

Establish a reproducible database foundation for a campus marketplace. In this milestone, add users, hierarchical categories, listings, and synthetic development records to the supplied marketplace repository.

The reversible todo migration in PostgreSQL, Migrations & Development Data is the worked example for this exercise. Parent rows must exist before rows that reference them. INSERT ... SELECT can retrieve an identity by a stable email address or slug. This milestone does not require authentication, forms, auctions, or other marketplace features.

Clone the marketplace repository

This card provides a course repository already populated with the walking skeleton.

  1. From the practice application (practice/), run docker compose down to free ports 5173 and 8000. An ordinary shutdown preserves its database.
  2. Use this card’s Copy clone command button.
  3. From the parent directory, paste the complete command, append marketplace to choose the destination folder, and run it.
  4. Enter and start the marketplace repository:
Terminal window
cd marketplace
git status
docker compose up --build
  1. Confirm that the counter and Load todos work at http://localhost:5173 before adding marketplace code.

Keep this marketplace repository for every marketplace milestone and keep the practice application for neutral experiments. If you already cloned the course-managed Git repository, continue in your existing marketplace repository. Do not extract a ZIP or run git init.

Use a distinct Compose project name for each copy. Do not override the names with a shared COMPOSE_PROJECT_NAME. The committed project.env supplies disposable local database settings.

Add a migration file and create tables

Keep the supplied database-migrations/001_create_todos.sql byte-for-byte unchanged. Create database-migrations/002_marketplace.sql.

The new dbmate migration must contain exactly one plain -- migrate:up marker followed by exactly one plain -- migrate:down marker.

In the up section, create these tables with exactly the columns described below. Do not add other columns.

  • users: an always-generated integer identity id primary key; required, unique text email; required text display_name; and optional TIMESTAMPTZ deleted_at;
  • categories: an always-generated integer identity id primary key; optional integer parent_id referencing categories(id) with an explicit ON DELETE RESTRICT; required text name; and required, unique text slug;
  • listings: an always-generated integer identity id primary key; required integer seller_id referencing users(id) and category_id referencing categories(id), with an explicit ON DELETE RESTRICT on both foreign keys; required text title; required text description defaulting to an empty string; and required integer starting_price constrained to be nonnegative.

In the down section, drop only listings, categories, and users, in that dependency-safe order. Leave the neutral todos baseline intact.

Add data

Insert the following records in dependency-safe order:

  1. Users Jamie (jamie@example.test) and Noor (noor@example.test).
  2. Root categories Electronics (electronics) and Books (books).
  3. Computers (computers) as a child of Electronics.
  4. These three listings:
SellerTitleCategoryDescriptionstarting_price
JamieUSB-C dockComputersDock with a power adapter.1500
NoorDiscrete Mathematics textbookBooksUsed textbook with notes.800
JamieDesk lampElectronicsSmall adjustable lamp.500

starting_price stores integer euro cents: 1500 means €15.00.

Resolve relationships from stable email addresses and slugs rather than assuming generated IDs. Milestone 1 assesses the resulting relationships; milestone 4 advances the identities before automatically testing these stable lookups.

Test

First, apply the migration in the marketplace repository:

Terminal window
docker compose run --rm database-migrations

Use a separate disposable Compose project for the complete up/down/up check. Replace wsd-marketplace-check with an unused name if a Compose project with that name already contains work you need. Use the same name for every command in the check.

1. Apply and inspect.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml run --rm database-migrations
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml exec database psql -U walking_skeleton -d walking_skeleton

At the psql prompt, inspect the tables and the seller/category relationships:

\dt
SELECT listings.title, users.email, categories.slug, listings.starting_price
FROM listings
JOIN users ON users.id = listings.seller_id
JOIN categories ON categories.id = listings.category_id
ORDER BY listings.title;
SELECT child.slug, parent.slug AS parent_slug
FROM categories AS child
JOIN categories AS parent ON parent.id = child.parent_id;
\q

Verify that there are two users, three categories, and three listings, and that Computers is a child of Electronics.

2. Roll back and inspect.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml run --rm database-migrations --no-dump-schema rollback
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml exec database psql -U walking_skeleton -d walking_skeleton

At the psql prompt, run \dt and SELECT * FROM todos;, then exit with \q. The marketplace tables must be absent and the original todo must remain.

3. Reapply and inspect.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml run --rm database-migrations
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml exec database psql -U walking_skeleton -d walking_skeleton

Repeat the inspection from step 1, then exit with \q. Reapplication must recreate the same records without duplicates. dbmate’s status command can also show the applied versions.

4. Remove the disposable Compose test project.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml down

This final command discards only the temporary test database.

Submit

In the marketplace repository, stage the new migration, inspect it, commit, and push:

Terminal window
git add database-migrations/002_marketplace.sql
git diff --cached
git commit -m "Add marketplace database foundation"
git push origin master

Push the marketplace project, including its supplied files. No hosted account or public URL is required. An unchanged marketplace repository is not a completed foundation submission.

The grader awards:

  • 40 points for the schema, constraints, relationships, and seed data;
  • 10 points for rolling back while preserving the migration 001 baseline; and
  • 10 points for correctly reapplying the migration.

Relational read API

With the records in place, the next step is to make them available through GET /api/listings and GET /api/categories. Each listing’s response carries its seller and actual category, using consistent public field names and price units. The /api prefix distinguishes these data endpoints from browser page routes introduced later.

Marketplace milestone 2: relational read API

0 / 50 points

Continue the marketplace project

After milestone 1 passes, continue in your marketplace repository. There is no new marketplace repository for this milestone. Run:

Terminal window
git fetch origin

This prepares the milestone for grading and makes its reference available without changing your files. Keep working on master.

The recovery command creates a fresh local marketplace repository from your own pushed master history in the course-managed remote Git repository. It cannot recover uncommitted changes or commits that you have not pushed.

Objective and reference

Expose the stored marketplace relationships through two database-backed read endpoints:

  • GET /api/listings
  • GET /api/categories

Use the route/repository separation in Tracing a Request as your model. Keep SQL in repository modules and request handling in api/src/app.ts.

Preserve the existing /health and /api/todos routes. Keep connection shutdown in app-run.ts; do not call sql.end() in a route or repository.

Add the repository modules

Create these files at the exact paths shown:

  • api/src/listingRepository.ts
  • api/src/categoryRepository.ts

Both files are part of the submitted contract. In each file:

  • import the shared sql client from ./database.ts;
  • export a type-correct const findAll function callable without arguments; and
  • order rows by ascending stored ID and return at most 100 rows.

The listings query must obtain seller and actual category names through the stored foreign-key relationships. Each returned row has exactly these fields:

FieldJSON typeSource
idnumberstored listing ID
titlestringstored title
descriptionstringstored description
startingPricenumberstored integer euro cents
sellerIdnumberstored seller ID
categoryIdnumberstored category ID
sellerNamestringrelated user’s display name
categoryNamestringrelated category’s name

Do not expose email or return any additional fields. Do not hardcode names, synthesize replacements, or infer relationships from row positions. A listing belongs to its actual category, not that category’s parent. For example, the USB-C dock belongs to Jamie and Computers, not Electronics.

Each category row has exactly id, parentId, name, and slug. Return parentId as a number for a child and null for a root.

Use quoted SQL aliases to preserve camel-case JSON names, for example:

l.starting_price AS "startingPrice"

The contracts must continue to work when the database contains more than the original seed rows and when those rows have different relationships.

Add the routes

In api/src/app.ts:

  1. Import the two findAll functions under distinct names, such as findAllListings and findAllCategories.
  2. Add GET /api/listings and GET /api/categories.
  3. Await the appropriate repository query in each route.
  4. Return its result as JSON with status 200.

Test

After the API watcher restarts, open:

  • http://localhost:8000/api/listings
  • http://localhost:8000/api/categories

Check the field names, numeric types, row order, seller relationships, category relationships, and root/child parentId values. A successful /health response alone does not exercise the database queries.

Run the supplied API tests in the separate Compose test project using the README commands. With host Deno, you can instead run:

Terminal window
deno task test:api

Submit

Stage the two new repositories and the changed application module, inspect the staged diff, commit, and push from the same marketplace repository:

Terminal window
git add api/src/listingRepository.ts api/src/categoryRepository.ts api/src/app.ts
git diff --cached
git commit -m "Add marketplace read API"
git push origin master

Submit the marketplace project, including the migrations from milestone 1. The grader awards:

  • 5 points for type correctness and the required API-module exports;
  • 5 points for the listing response contract;
  • 15 points for varied seller and category relationships;
  • 10 points for public values and integer-cent prices;
  • 10 points for the category response contract; and
  • 5 points for deterministic ordering and the 100-row limits.

Type correctness is assessed only by the 5-point API-module check. Its compiler diagnostics appear in that test result. A type error does not block the other 45 points: the grader runs API behavior tests independently when the code can execute.

Browser marketplace read

Now bring the listings into the browser. Add a component that requests data when the user activates it and displays the returned relationships and prices, while keeping the original counter and todo interactions working.

Marketplace milestone 3: browser marketplace read

0 / 50 points

Continue the marketplace project

After milestone 2 passes, continue in your marketplace repository. There is no new marketplace repository for this milestone. Run:

Terminal window
git fetch origin

This prepares the milestone for grading and makes its reference available without changing your files. Keep working on master.

The recovery command creates a fresh local marketplace repository from your own pushed master history in the course-managed remote Git repository. It cannot recover uncommitted changes or commits that you have not pushed.

Objective and reference

Display the related listing data from GET /api/listings in the browser. Follow the request pattern used by the supplied TodoList.svelte component in Tracing a Request.

Create client/src/lib/ListingList.svelte and update client/src/routes/+page.svelte. Both files are part of the submitted contract.

Empty, error, and retry-state design is developed in Part 2. This milestone assesses the initial, pending, and successful nonempty states.

Create the listing component

In client/src/lib/ListingList.svelte, add a Load listings button and an asynchronous const loading function.

The request behavior must meet this contract:

  • Do not request listings on initial render.
  • Make one GET /api/listings request each time the user activates the button.
  • Build the URL using import.meta.env.VITE_API_BASE_URL; do not hardcode a host or port.
  • Display the exact text Listings: not loaded initially.
  • Display the exact text Listings: loading while the request is pending.
  • Display the exact text Listings: loaded after a successful, nonempty response.
  • Put the status text in a polite live region.

Render the returned listings as a keyed list. Each listing must be a list item containing:

  • its title as a heading;
  • its seller name after the visible label Seller:;
  • its actual category name after the visible label Category:; and
  • its starting price after the visible label Starting price:.

Convert integer euro cents to euros with two decimal places. For example, 1500 must appear as Starting price: €15.00.

Render the values returned by the API, including varied titles, names, categories, and prices. Do not hardcode the seed records. A local Listing type can describe numeric id and startingPrice fields and string title, sellerName, and categoryName fields.

Update the page

In client/src/routes/+page.svelte:

  1. Change both the level-one heading and document title to Campus Marketplace.
  2. Import the new component through #lib/ListingList.svelte.
  3. Render it in a page section exposed as an accessible region with the exact name Listings. For example, give the section a Listings heading and connect the section to that heading with aria-labelledby.

Place the Load listings button, rendered listing items, and exactly one polite listing-status element inside the Listings region.

Preserve the supplied Counter.svelte and TodoList.svelte components and their page sections. The todo section must keep the accessible region name Todos, and the original Load todos and counter interactions must still work.

Keep the current SvelteKit 3 scaffold and #lib import mapping. Do not replace the supplied framework configuration or dependency pins.

Test

Check the page locally:

  1. Confirm the initial status appears before any listing request.
  2. Activate Load listings and inspect the request and pending status.
  3. Compare the visible titles, seller names, category names, and formatted prices with the API response.
  4. Confirm that Load todos and the counter still work.

Run the component and browser suites using the README’s isolated test commands. Then verify the production build:

Terminal window
docker compose run --build --rm --no-deps client deno task build

Submit

Stage the new component and changed page, inspect the staged diff, commit, and push:

Terminal window
git add client/src/lib/ListingList.svelte client/src/routes/+page.svelte
git diff --cached
git commit -m "Add marketplace listing view"
git push origin master

Submit the marketplace project. The grader builds client/src/** with the course’s pinned scaffold and supplies varied API responses. It awards:

  • 5 points for the production build;
  • 5 points for the page heading and document title;
  • 10 points for the explicit load action;
  • 25 points for rendered titles, relationships, and prices; and
  • 5 points for preserving the todo and counter interactions.

Type checking is optional in this milestone and does not affect points. The grader assesses executable tests and application behavior.

Varied reproducible data

Let’s give the application more varied records to work with. Add another reversible migration with different seller/category combinations. Its rollback must remove only its own additions, so use stable record values rather than assumed generated identifiers.

Marketplace milestone 4: varied reproducible data

0 / 40 points

Continue the marketplace project

After milestone 3 passes, continue in your marketplace repository. There is no new marketplace repository for this milestone. Run:

Terminal window
git fetch origin

This prepares the milestone for grading and makes its reference available without changing your files. Keep working on master.

The recovery command creates a fresh local marketplace repository from your own pushed master history in the course-managed remote Git repository. It cannot recover uncommitted changes or commits that you have not pushed.

Objective and reference

Add varied development records that reveal whether the API and browser page continue to work with additional relationships.

Use the reversible migration in PostgreSQL, Migrations & Development Data as your model. Resolve relationships through stable values in both migration directions. Existing rows may have generated IDs that are neither small nor consecutive.

Create the migration

Create this file:

database-migrations/003_more_development_data.sql

Treat migrations 001 and 002 as shared, already-applied history. Preserve both files and put all new work in migration 003. Use exactly one plain -- migrate:up marker followed by one plain -- migrate:down marker, and use dbmate’s default transaction.

Migration 003 only adds and removes its own development records. Do not alter the existing table structure, constraints, or rows during migration-up or migration-down.

The grader-reserved-m4- and [grader-reserved-m4] prefixes belong to hidden fixture data. Do not use either prefix in your migration.

Add varied data

In the up section, add all of the following:

  • one distinct synthetic user with a nonempty email and display name;
  • one distinct, named child category beneath Electronics, Books, or Computers, with a nonempty name and slug; and
  • exactly three distinct listings with nonempty titles.

The three listings must span at least two distinct (seller, category) pairs. At least one listing must use the new user, and at least one listing must use the new category. These can be different listings. Give every listing a nonnegative integer starting_price in euro cents.

Choose stable, migration-specific values for the new user’s email, the new category’s slug, and all three listing titles. Resolve the user and category relationships from stable emails and slugs rather than assumed generated IDs.

Add the rollback

In the down section:

  1. Remove exactly the three listings owned by migration 003.
  2. Remove the category owned by migration 003.
  3. Remove the user owned by migration 003.

Use stable values to identify those rows. Preserve every milestone 1 row and all other pre-existing or independently added data. Never clear an entire table to undo this migration.

Test

Test the complete migration chain in a separate disposable Compose project. If wsd-marketplace-check is already protecting work you need, replace it with an unused name and use that same name throughout.

1. Apply and inspect.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml run --rm database-migrations
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml exec database psql -U walking_skeleton -d walking_skeleton

At the first psql prompt, inspect the new user, category, listings, and their relationships. For example:

SELECT listings.title, users.email, categories.slug
FROM listings
JOIN users ON users.id = listings.seller_id
JOIN categories ON categories.id = listings.category_id
ORDER BY listings.title;
\q

2. Roll back and inspect.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml run --rm database-migrations --no-dump-schema rollback
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml exec database psql -U walking_skeleton -d walking_skeleton

Repeat the joined query and verify that only the migration 003 additions disappeared. The milestone 1 rows and all other existing data must remain. Exit with \q.

3. Reapply and inspect.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml run --rm database-migrations
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml exec database psql -U walking_skeleton -d walking_skeleton

Verify that reapplication restores the same email, slug, titles, and relationships without relying on the original generated IDs. Exit with \q.

4. Remove the disposable project.

Terminal window
docker compose -p wsd-marketplace-check -f compose.yaml -f compose.test.yaml down

This discards only the temporary test database.

Apply migration 003 to the normal development database without resetting it, then inspect the SQL data, API output, and browser page:

Terminal window
docker compose run --rm database-migrations

Submit

The grader records the new email, slug, and titles during the first application and requires those same stable values after rollback and reapplication.

Stage the new migration, inspect the staged diff, commit, and push:

Terminal window
git add database-migrations/003_more_development_data.sql
git diff --cached
git commit -m "Add varied marketplace development data"
git push origin master

Submit the marketplace project. The grader awards:

  • 20 points for the new related data over advanced identity values;
  • 10 points for removing only migration 003 data during rollback; and
  • 10 points for stable reapplication and coherent migration state.

Test the API relationship

First, protect the relationship where the API returns it. Write a test that distinguishes a listing’s actual category from another category. You can check this boundary without opening a browser, and use a deliberately faulty response to see whether the assertion notices the difference.

Marketplace milestone 5: API relationship regression test

0 / 25 points

Continue the marketplace project

After milestone 4 passes, continue in your marketplace repository. There is no new marketplace repository for this milestone. Run:

Terminal window
git fetch origin

This prepares the milestone for grading and makes its reference available without changing your files. Keep working on master.

The recovery command creates a fresh local marketplace repository from your own pushed master history. It cannot recover uncommitted changes or commits that you have not pushed.

Objective and reference

Add a Deno regression test that proves listings retain their actual category relationships. Create it at this exact path in the marketplace repository:

api/tests/student_category_test.ts

This milestone assesses the test as regression evidence: it must pass against a compatible correct API and fail against APIs with incorrect relationships.

Write the regression test

Import and use:

  • assertions from @std/assert;
  • app from @src/app.ts; and
  • sql from @src/database.ts.

Request GET /api/listings, assert status 200, find these records by title, and assert their actual categories:

  • USB-C dock: category Computers;
  • Discrete Mathematics textbook: category Books.

Assert both category values. Seller assertions for Jamie and Noor provide useful additional evidence but are not required for these points.

A compatible API may return additional listings in a different stored order. Therefore:

  • find the two records by title;
  • do not rely on array positions, generated IDs, or an exact row count; and
  • make the test reject both a constant category and categories attached to the wrong titles.

Keep the shared SQL client open for every awaited test step and close it once in finally. Keep the required test self-contained; additional helper modules are outside the returned-test contract.

Starting structure

You may begin with this structure:

import { assertEquals } from "@std/assert";
import { app } from "@src/app.ts";
import { sql } from "@src/database.ts";
Deno.test("listings preserve their category relationships", async (t) => {
try {
await sql`SELECT 1`;
await t.step("the dock has its expected relationships", async () => {
const response = await app.request("/api/listings");
// TODO: check the status, find the dock, and assert its category.
throw new Error("Complete the dock assertions");
});
await t.step("the textbook has different relationships", async () => {
const response = await app.request("/api/listings");
// TODO: check the status, find the textbook, and assert its category.
throw new Error("Complete the textbook assertions");
});
} finally {
await sql.end();
}
});

Replace the TODO comments and deliberate throw statements with meaningful assertions.

Test

From the marketplace repository root, run the test against an isolated test database. If wsd-marketplace-api-test is already protecting work you need, replace it with an unused name and use that same name for cleanup.

Terminal window
docker compose -p wsd-marketplace-api-test -f compose.yaml -f compose.test.yaml run --build --rm api deno task test tests/student_category_test.ts
docker compose -p wsd-marketplace-api-test -f compose.yaml -f compose.test.yaml down

The test must pass against the correct API and fail when every listing is reported with the same category or when the two known categories are exchanged. A timeout, crash, empty suite, skipped-only suite, or assertion that always succeeds is not regression evidence.

Submit

Stage the new test, inspect it, commit, and push the marketplace project from the same marketplace repository:

Terminal window
git add api/tests/student_category_test.ts
git diff --cached
git commit -m "Add listing relationship API regression test"
git push origin master

The grader uses pinned course dependencies and the complete cumulative project, but this milestone scores only the returned API test. It awards:

  • 5 points when the test passes against a compatible correct API; and
  • 20 points when it rejects incorrect title-to-category relationships.

Type checking is optional in this milestone and does not affect points. The grader assesses executable tests and application behavior.

Test the browser and complete the application

Now follow the same relationship into the page. Add a browser regression test and submit the complete application. The final milestone checks both whether the browser test detects defects and whether a real request reaches the database and returns data to the page from a fresh start. Your accepted API test and the earlier application features remain prerequisites; they do not earn their points a second time.

Marketplace milestone 6: browser regression and Part 1 acceptance

0 / 45 points

Continue the marketplace project

After milestone 5 passes, continue in your marketplace repository. There is no new marketplace repository for this milestone. Run:

Terminal window
git fetch origin

This prepares the final Part 1 milestone for grading and makes its reference available without changing your files. Keep working on master.

The recovery command creates a fresh local marketplace repository from your own pushed master history. It cannot recover uncommitted changes or commits that you have not pushed.

Objective and reference

Add a Playwright regression test for the browser listing flow, then submit the cumulative marketplace project for Part 1 acceptance. Create the test at this exact path:

e2e-tests/tests/student_listing.spec.ts

This milestone assesses both your returned browser test and the cumulative data flow from a fresh database through the API to the browser.

Empty, error, and retry-state design is developed and assessed in Part 2 and is not required here.

Write the browser regression test

Import from @playwright/test and use the configured base URL. In the test:

  1. Navigate to /.
  2. Activate Load listings.
  3. Locate the USB-C dock and Discrete Mathematics textbook by their headings.
  4. Within each listing item, assert these visible labels:
  • USB-C dock: Seller: Jamie and Category: Computers;
  • Discrete Mathematics textbook: Seller: Noor and Category: Books.

Use waiting, web-first assertions. Do not hardcode a development host or port, and do not merely search for the four values somewhere on the page. Scope the seller and category assertions to the corresponding listing item.

Compatible fixtures may include other listings in a different order. Find the two named items instead of assuming positions or an exact list length. The test must reject:

  • a missing Load listings action;
  • a seller or category value taken from the other named listing;
  • any incorrect seller/category relationship for either named listing; and
  • one constant category reported for every listing.

Keep the visible labels themselves as Seller: and Category:. A swap defect assigns relationship values to the wrong listing; it does not exchange the two label words.

Starting structure

You may begin with this structure:

import { expect, test } from "@playwright/test";
test("loading listings displays their relationships", async ({ page }) => {
await page.goto("/");
// TODO: activate Load listings.
// TODO: locate each listing item and assert its labelled values.
throw new Error("Complete the browser assertions");
});

Replace the TODO comments and deliberate throw with meaningful locators and assertions.

Preserve the cumulative application

The pushed project must still support the complete Part 1 flow:

  • a fresh database applies migrations 001 through 003 and reconstructs the marketplace schema and data;
  • /health, /api/todos, /api/listings, and /api/categories work;
  • the client produces a production build;
  • the original counter and todo read still work; and
  • the marketplace list renders each title, seller, actual category, and integer-cent price formatted as euros.

Keep the API relationship test from milestone 5. Every path listed under Required files on this card must exist in the pushed cumulative tree. A structurally incomplete tree is rejected before individual checks begin.

The grader uses canonical pinned dependencies and configuration. It does not execute submitted Docker, Compose, or package scripts.

Test

Run the browser test with an isolated Compose project. The first command starts a fresh database, applies the migrations, builds the application, and then runs Playwright. If wsd-marketplace-browser-test is already protecting work you need, replace it with an unused name and use the same name for every command, including cleanup.

Terminal window
docker compose -p wsd-marketplace-browser-test -f compose.yaml -f compose.test.yaml run --build --rm e2e-tests npx playwright test tests/student_listing.spec.ts
docker compose -p wsd-marketplace-browser-test -f compose.yaml -f compose.test.yaml run --build --rm --no-deps client deno task test
docker compose -p wsd-marketplace-browser-test -f compose.yaml -f compose.test.yaml run --build --rm --no-deps client deno task build
docker compose -p wsd-marketplace-browser-test -f compose.yaml -f compose.test.yaml down

The first command checks your returned browser evidence through the real local stack. The component suite checks the preserved counter and todo components; the build command checks the cumulative client artifact. Browser traces from failures appear in e2e-tests/test-results/.

Submit

Stage the new browser test and any cumulative corrections. Inspect the staged changes, commit, and push from the same marketplace repository:

Terminal window
git add e2e-tests/tests/student_listing.spec.ts
git add PATHS_YOU_CORRECTED
git diff --cached
git commit -m "Add marketplace browser regression test"
git push origin master

Omit the second git add command when no application correction was needed. Replace PATHS_YOU_CORRECTED with explicit paths rather than staging build or test output.

The grader awards 30 points for the returned browser test:

  • 5 points for passing against the compatible reference;
  • 5 points for detecting a missing load action;
  • 10 points for detecting seller/category values assigned to the wrong named listing or another incorrect relationship in either named listing; and
  • 10 points for detecting a constant category.

A further 15 points require the browser to display the correctly related sellers, categories, and formatted prices through the submitted API and a fresh database. The migration, API, original-component, and build checks are readiness gates for that flow. They identify the blocking layer but do not award separate points.

Type checking is optional in this milestone and does not affect points. The grader assesses executable tests and application behavior.

For now, the marketplace establishes a successful read from storage to the browser. Part 2 develops the listing view further: it distinguishes empty collections and failed requests, clears stale rows, and supports recovery after a failure. You do not need to add those later interaction states to finish Part 1.

Once these milestones are complete, you have a first marketplace that connects related records to a browser view, along with tests to help you keep it working. Keep the marketplace repository after Part 1 acceptance; you will continue the marketplace project there in Part 2.

Check Your Understanding

  1. How does a listing remain connected to its seller and category from the database to the browser?
  2. Why should development examples include more than one seller/category combination?
  3. What must a migration’s down section preserve when it removes that migration’s additions?
  4. How do API tests and browser tests help check different parts of the marketplace?
  5. Why does each submission contain the cumulative marketplace project rather than only the newest files?