Svelte Commerce
SvelteKit으로 만든 독립형 오픈소스 스토어프론트, 26개 상거래 백엔드와 헤드리스 연동, MIT 라이선스
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기A realistic outdoor-gear web shop, built for QA engineers to practise testing UI, API and data. It behaves like a production store, with real business rules, a staff admin app and a mock payment provider. It also hides 18 planted bugs you can switch on, each one caught by exactly one SQL rule.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A realistic outdoor-gear web shop, built for QA engineers to practise testing UI, API and data.
It behaves like a production store, with real business rules, a staff admin app and a mock payment provider. It also hides 18 planted bugs you can switch on, each one caught by exactly one SQL rule.
🛒 Open the shop · 🗂️ Staff admin · 📘 API docs (try it live) · 🧾 OpenAPI 3.1 spec · 🐞 Planted bugs · 🧪 Start testing
https://github.com/user-attachments/assets/fd3de253-b4d2-4c7c-b0e8-b383a2d962fd
[!NOTE] Halden Outfitters is not a real store. Nothing is for sale, no email is ever sent, and payments go to HaldenPay, a mock card provider with test card numbers. The shop is free to test against, from a browser or from your own automation.
Most practice sites are either too simple (a login form and a to-do list) or too fragile to automate against. Halden is a complete shop you can test the way you test real software:
| You want to practise… | Halden gives you… |
|---|---|
| UI testing (Playwright, Cypress, Selenium) | A modern Next.js storefront, desktop and mobile, light and dark, with stable roles and labels to select by |
| API testing (Postman, REST Assured, k6, curl) | 89 documented operations, an OpenAPI 3.1 spec, problem+json errors with stable codes, idempotency keys, rate limits |
| Business-rule testing | Checkout, payments, cancellations, returns, refunds, store credit, loyalty points and reviews, each with written rules |
| Data and SQL checks | qa/rules.sql: 38 rules that must hold in the database, and a recorder that checks them per test step |
| Bug hunting | 18 planted bugs you can switch on. Can your tests catch them? |
| Time-based rules | A shop clock you can move forward (31 days, 13 months) to test return windows and point expiry |
| Email flows | A fake inbox that shows every email the shop would send, with its links |
| Back-office testing | A separate staff admin: dashboard with charts, orders, warehouse, returns, reviews, products (add, edit, restock), coupons, customers, audit log |
[email protected] or [email protected] (see why).4000 0000 0000 3220. You land on the test bank
step: approve it.Things worth automating: the cart drawer, the 4-step checkout with its validation, the bank step, cancel countdowns, the 3-step returns wizard, reviews and votes, light/dark theme, and the mobile menu.
Selectors: use roles and labels (getByRole('button', { name: 'Add to cart' }), getByLabel('Email')).
Lists that tests need also carry data-testid values (my-review, bug-<key>, audit-row, …).
// Playwright: a shopper signs up and lands on their account
import { test, expect } from '@playwright/test';
test('sign up with a test address', async ({ page }) => {
await page.goto('https://halden-outfitters.vercel.app/register');
await page.getByLabel('Your name').fill('Pat Tester');
await page.getByLabel('Email').fill(`pat-${Date.now()}@qa.test`);
await page.getByLabel('Password', { exact: true }).fill('a long walk in the woods');
await page.getByRole('button', { name: 'Create account' }).click();
await expect(page).toHaveURL(/\/account/);
});
The repo's own Playwright suites are a good reference: apps/web/e2e covers browsing, checkout, the bank step,
cancelling, returns, reviews and accessibility (axe); apps/admin/e2e covers the staff journeys, the dashboard,
coupons, adding products and accessibility in both themes.
The API lives at https://halden-api.vercel.app/api/v1 (or /api/v1 on the shop's or the admin's domain). The session is an
HttpOnly cookie, so keep a cookie jar. Every error is application/problem+json with a stable code.
API=https://halden-api.vercel.app/api/v1
EMAIL="api-$(date +%s)@qa.test"
# 1. Sign up (you are signed in straight away)
curl -s -c jar -b jar -H 'content-type: application/json' \
-d "{\"name\":\"Api Tester\",\"email\":\"$EMAIL\",\"password\":\"a long walk in the woods\"}" $API/auth/register
# 2. Read the confirmation email in the fake inbox and confirm
TOKEN=$(curl -s "$API/qa/inbox?email=$EMAIL" | grep -o 'token=[A-Za-z0-9_-]*' | head -1 | cut -d= -f2)
curl -s -b jar -H 'content-type: application/json' -d "{\"token\":\"$TOKEN\"}" $API/auth/verify-email
# 3. Add an address and a product
ADDRESS=$(curl -s -b jar -H 'content-type: application/json' \
-d '{"name":"Api Tester","line1":"1 Test Way","city":"Testville","postcode":"12345","countryCode":"US"}' \
$API/account/addresses | grep -o '"id":"[0-9]*"' | head -1 | cut -d'"' -f4)
curl -s -b jar -H 'content-type: application/json' -d '{"productId":"10","qty":1}' $API/cart/items
# 4. Check out (the Idempotency-Key makes a retry safe: the card is charged once)
curl -s -b jar -H 'content-type: application/json' -H "Idempotency-Key: $(uuidgen)" \
-d "{\"shipTo\":{\"addressId\":\"$ADDRESS\"},\"shippingMethod\":\"standard\",\"card\":{\"number\":\"4242424242424242\",\"expMonth\":12,\"expYear\":2030,\"cvc\":\"123\",\"name\":\"Api Tester\"}}" \
$API/checkout
Worth testing: validation errors (one entry per field), 401 vs 403 vs 404 (you can't see other people's
orders, so they're 404), idempotency (same key and body returns the first response, a different body is 422),
rate limits (429 with Retry-After), optimistic locking on staff product edits (If-Match, 412), cross-site
writes refused by the Origin check, and the order state machine (allowedActions on every order).
The API docs list every route, field and error, and can send requests for you. The written guide is docs/API.md; for automation practice, see the API testing guide (70 scenarios, test data, a Postman collection).
qa/rules.sql holds 38 rules the database must always satisfy, such as the card is authorized for
what the order leaves to pay or a line is never refunded more than was paid for it. Each rule is a query that
returns the rows breaking it.
Propmaster records what each test step changed and checks the rules against exactly those rows:
propmaster record start "Return a damaged mug" # then run your UI or API test, marking steps
propmaster record step "Grade it faulty"
propmaster record stop
propmaster record check qa/rules.sql # which rules broke, with the offending rows
propmaster find --recipes qa/recipes "order that can still be returned" --claim
propmaster seed --scenarios qa/scenarios delivered-order-for-return
qa/recipes/halden.sql holds 25 recipes for finding test data, for example a shopper
with points or an order past its return window.qa/scenarios holds 22 scenarios for creating it and cleaning it up afterwards.The staff admin is its own app, with its own sign-in. Each staff role sees only its sections:
| Section | What you can do there | Roles |
|---|---|---|
| Dashboard | revenue, orders, average order, return rate against the previous period; revenue and orders per day; orders by status; return reasons; low stock (7, 30 or 90 days of shop time) | all staff |
| Orders | every order with its payment and history; cancel; goodwill refunds | all staff |
| Warehouse | pack confirmed orders, ship packed ones, mark them delivered | warehouse, admin |
| Returns | approve or reject requests; receive parcels and grade each line (refund and restock follow the grade) | all staff |
| Reviews | publish held reviews, hide published ones | support, admin |
| Products | add products with photos, edit details and prices (with optimistic locking), restock and adjust stock | all staff (add: admin) |
| Coupons | create, schedule, limit and switch coupons on or off | admin |
| Customers, settings, audit log | roles, shipping rates, the tax rate, and every staff action with its before and after | admin (customers: support too) |
| QA lab | switch planted bugs, move the shop clock, run the time-based sweeps, replay payment webhooks | admin (viewer: read only) |
Press Ctrl+K anywhere to jump to a section or open an order by its number.
Just want to look around? Sign in to the admin as [email protected] with the password just-looking-around.
It's read-only: you see the dashboard, products, coupons, settings and the QA lab's bug list, nothing with customer
details, and every save is refused. The other staff accounts are not public: ask the owner of the instance you test
(see demo accounts).
Every bug is off until an admin switches it on in the admin's QA lab. Each one breaks exactly one rule in
qa/rules.sql; apps/api/test/rules.test.ts proves that on every CI run. Try to catch one with your own test
before you open the hint.
| # | Bug | Caught by the rule | How to see it |
|---|---|---|---|
| 1 | Coupon applied twice | The discount is exactly what the coupon gives | hintAdd two different full-price products, apply WELCOME10 and check out. The discount is double. |
| 2 | Cancelling keeps the stock held | Open stock holds belong to open orders | hintOrder the last units of a low-stock product, then cancel: it still shows as sold out. |
| 3 | Payment misses shipping | The card is authorized for what the order leaves to pay | hintCheck out an order under 75.00 so it pays shipping. The payment is lower than the total. |
| 4 | Coupon use limit ignored | A coupon is never used more often than allowed | hintTRAILHEAD15 allows 3 uses and the demo orders used all three. It still applies. |
| 5 | Per-customer coupon limit ignored | A customer never uses a coupon more often than allowed | hintWELCOME10 is once per customer. Use it twice with one account. |
| 6 | Cancel keeps the coupon use | Cancelled orders hold no coupon use | hintUse a limited coupon, cancel the order: the use is still counted. |
| 7 | Cancel leaves the card authorized | Cancelled orders have no open authorization | hintCancel a confirmed order: its payment still shows as authorized. |
| 8 | Europe charged the domestic rate | Shipping matches the zone rate | hintCheck out to an address in Sweden: shipping is 7.95 instead of 14.95. |
| 9 | Refund ignores the coupon share | A line is never refunded more than was paid for it | hintBuy two items with CAMP20, return one: the refund is more than you paid for it. |
| 10 | Refunds can exceed the payment | Refunds never exceed what was paid | hintRefund an order in full through a return, then add a goodwill refund on top. |
| 11 | A cancelled return restarts the window | Returns are requested within 30 days of delivery | hintMove the clock 25 days after delivery, start a return and cancel it. 10 days later a new return is still accepted. |
| 12 | Damaged returns go back on the shelf | Written-off returns are never restocked |
Your own accounts: register as many as you like. Mail to test domains is public in the fake inbox:
*.test, *.example, *.invalid, *.localhost, and example.com, example.net, example.org. Mail to any
other address is shown only to its signed-in owner, so use a test domain for automation.
Demo accounts: the seeded staff and shopper accounts are protected and their passwords are not published, so
nobody can lock others out or change shared data. Register your own shopper account instead (it takes a minute
with the fake inbox); for staff access, ask the owner of the instance you test. Whoever deploys the shop sets the
passwords (SEED_*_PASSWORD) and can change them without losing data: npm run db:set-passwords.
| Role | Can | |
|---|---|---|
| Admin | [email protected] | everything in the staff admin, including the QA lab |
| Support | [email protected] | orders, cancellations, return decisions, goodwill up to 50.00, reviews, customers |
| Warehouse | [email protected] | pack, ship, deliver, receive and grade returns, stock |
| Shoppers | maya@, jonas@, [email protected] | order history, a delivered order to return, reviews |
| Viewer (public) | [email protected], password just-looking-around | read the staff admin's sections without customer details; change nothing |
Test cards (any future expiry, any CVC):
| Card | Result |
|---|---|
4242 4242 4242 4242 | authorized |
4000 0000 0000 0002 | declined (402) |
4000 0000 0000 9995 | insufficient funds (402) |
4000 0000 0000 3220 | needs the bank step (202), then authorized or failed |
4000 0000 0000 0259 | authorized; a dispute webhook arrives after capture |
Coupons:
| Code | What it does |
|---|---|
WELCOME10 | 10% off your first order, once per customer |
CAMP20 | 20% off everything except shelter |
SAVE20 | 20.00 off orders of 150.00 or more |
FREESHIP | free shipping on 40.00 or more |
ALLIN25 | 25% off, sale items included, twice per customer |
TRAILHEAD15 | 15% off 100.00 or more; all 3 uses already taken |
SPRING5 | expired |
STAFF50 | switched off |
flowchart LR
B[Browser or your tests] -->|pages| W[Shop: Next.js<br/>halden-outfitters.vercel.app]
S[Staff] -->|pages| M[Staff admin: Next.js<br/>halden-admin.vercel.app]
W -->|/api/v1/* rewrite<br/>cookies stay first-party| A[API: Fastify<br/>halden-api.vercel.app]
M -->|/api/v1/* rewrite| A
M -->|product photos| BL[(Vercel Blob)]
T[API tests] -->|/api/v1| A
A --> D[(PostgreSQL<br/>Neon)]
A -.->|signed webhooks| A
P[Propmaster] -->|records changes,<br/>checks qa/rules.sql| D
packages/contracts (Zod schemas used by every app) and packages/money (integer-cent maths and
exact allocation).stateDiagram-v2
[*] --> pending_payment: checkout (bank step)
[*] --> confirmed: checkout (card authorized)
pending_payment --> confirmed: bank approves
pending_payment --> cancelled: bank declines / 30 min pass
confirmed --> packed: warehouse
confirmed --> cancelled: customer or support
packed --> shipped: warehouse (card charged)
packed --> cancelled: support
shipped --> delivered: warehouse
delivered --> completed: return window closes
completed --> [*]
cancelled --> [*]
Returns run alongside the order after delivery: requested → approved → received → refunded, or rejected, expired or cancelled.
The full rules are in docs/DESIGN.md section 3, and each service file starts with the rules it enforces.
Needs Node 22.12+ (24 recommended) and PostgreSQL 13+. Full guide: docs/DEVELOPING.md.
npm install
cp apps/api/.env.example apps/api/.env # set DATABASE_URL and the three SEED_*_PASSWORD values
npm run db:reset # migrate and seed a local database
npm run dev # API :4000, shop http://localhost:3001, admin http://localhost:3002
| Command | Runs |
|---|---|
npm test | unit tests and API integration tests against real Postgres: concurrency races, security regressions and the planted-bug proof |
npm run test:e2e | the shop's Playwright journeys on desktop and mobile (start the API with RATE_LIMITS=false) |
npm run test:e2e -w @halden/admin | the admin's Playwright journeys and axe scan (the API needs ALLOWED_ORIGINS=http://localhost:3002) |
npm run db:set-passwords | give the demo accounts new SEED_*_PASSWORD values without reseeding |
npm run typecheck, npm run lint | TypeScript and ESLint across the workspace |
CI runs all of these, plus a JavaScript budget and Lighthouse budgets on mobile.
apps/api Fastify API (also the Vercel project "halden-api")
apps/web Next.js shop (the Vercel project "halden-outfitters")
apps/admin Next.js staff admin (the Vercel project "halden-admin")
packages/contracts Zod schemas shared by the API, the shop and the admin
packages/money integer-cent arithmetic and exact allocation
db/migrations forward-only SQL migrations
qa/ Propmaster rules, recipes and scenarios
docs/ design, API guide, developer guide
Made as a QA practice target · MIT licence · Report an issue
| hintReceive a return and grade it faulty: the stock goes up. |
| 13 | Store-credit refunds also refund the card | Each refund is paid out once | hintReturn an item for store credit: the credit balance and the card refund both go up. |
| 14 | Support can refund any amount as goodwill | Support goodwill refunds stay within 50.00 per order | hintSign in as support and give a 120.00 goodwill refund. |
| 15 | Points earned on tax and shipping | Points are earned on goods only | hintDeliver an order that paid shipping: it earns more points than its goods are worth. |
| 16 | Returned items keep their points | Returned lines hold no points | hintReturn a delivered item before the window closes: the pending points stay. |
| 17 | Hidden reviews still count in the rating | A product rating is the average of its published reviews | hintHide a 1-star review in the staff admin: the rating doesn't go up. |
| 18 | Price edits rewrite past orders | An order subtotal is the sum of its lines | hintChange the price of a product someone ordered: their order lines no longer add up. |