Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules
playwright-report
test-results
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,46 @@ Once running, Counterfact provides:
- An interactive REPL for inspecting and manipulating server state at runtime
- A Swagger UI for exploring and testing the API

### Pet Store UI

A single-page app is included in the `ui/` directory. After starting the mock server, open `ui/index.html` in a browser, or serve it with any static file server:

```bash
npx serve ui
```

Then visit `http://localhost:3000` (or whatever port `serve` uses). The UI communicates with the Counterfact backend at `http://localhost:3100` by default. To point it at a different API server, pass the `api` query parameter:

```
http://localhost:3000/?api=http://localhost:3100
```

The UI covers all petstore APIs:

- **Pets tab** – browse pets by status, search by tags, add, edit, and delete pets
- **Store tab** – view live inventory, place orders, look up and delete orders
- **Users tab** – list, add, edit, look up, and delete users

## Testing

### Unit tests

```bash
npm test
```

Runs the context unit tests with Node's built-in test runner.

### UI tests (Playwright)

```bash
npm run test:ui
```

Starts the Counterfact API server on port 3101 and a static file server on port 8080, then runs Playwright browser tests against the full UI + backend stack.

Playwright reports are written to `playwright-report/`. Test failure artefacts (screenshots, videos) go to `test-results/`.

## Project Structure

```
Expand All @@ -41,6 +81,12 @@ Once running, Counterfact provides:
│ └── types/ # Generated TypeScript types from the OpenAPI spec
├── spec/
│ └── petstore.yaml # OpenAPI specification for the Petstore API
├── test/
│ ├── context.test.ts # Unit tests for the Context class
│ └── petstore.ui.test.ts # Playwright end-to-end UI tests
├── ui/
│ └── index.html # Single-page application
├── playwright.config.ts # Playwright configuration
└── package.json
```

Expand Down
6 changes: 3 additions & 3 deletions api/routes/pet/{petId}.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import type { updatePetWithForm } from "../../types/paths/pet/{petId}.types.js";
import type { deletePet } from "../../types/paths/pet/{petId}.types.js";

export const GET: getPetById = ($) => {
const pet = $.context.getPetById($.path.petId);
const pet = $.context.getPetById(Number($.path.petId));
if (!pet) {
return $.response[404];
}
return $.response[200].json(pet);
};

export const POST: updatePetWithForm = ($) => {
const pet = $.context.getPetById($.path.petId);
const pet = $.context.getPetById(Number($.path.petId));
if (!pet) {
return $.response[400];
}
Expand All @@ -29,7 +29,7 @@ export const POST: updatePetWithForm = ($) => {
};

export const DELETE: deletePet = ($) => {
const deleted = $.context.deletePet($.path.petId);
const deleted = $.context.deletePet(Number($.path.petId));
if (!deleted) {
return $.response[400];
}
Expand Down
2 changes: 1 addition & 1 deletion api/routes/pet/{petId}/uploadImage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { uploadFile } from "../../../types/paths/pet/{petId}/uploadImage.types.js";

export const POST: uploadFile = ($) => {
const pet = $.context.getPetById($.path.petId);
const pet = $.context.getPetById(Number($.path.petId));
if (!pet) {
return $.response[404];
}
Expand Down
4 changes: 2 additions & 2 deletions api/routes/store/order/{orderId}.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import type { getOrderById } from "../../../types/paths/store/order/{orderId}.ty
import type { deleteOrder } from "../../../types/paths/store/order/{orderId}.types.js";

export const GET: getOrderById = ($) => {
const order = $.context.getOrderById($.path.orderId);
const order = $.context.getOrderById(Number($.path.orderId));
if (!order) {
return $.response[404];
}
return $.response[200].json(order);
};

export const DELETE: deleteOrder = ($) => {
const deleted = $.context.deleteOrder($.path.orderId);
const deleted = $.context.deleteOrder(Number($.path.orderId));
if (!deleted) {
return $.response[404];
}
Expand Down
Loading