templates2.Store.Next/tests/e2e/smoke.mjs
w4c-templates bb75e7c742
Some checks are pending
CI / quality (push) Waiting to run
chore(template): publish Store.Next
2026-10-11 23:24:13 +03:00

106 lines
5.1 KiB
JavaScript

#!/usr/bin/env node
/**
* End-to-end smoke test — drives the running server over HTTP (no browser needed).
* Store case: catalog → cart validation → mock checkout → order, plus the base auth/roles flow.
* Start the app first (`npm start`), then: QA_BASE_URL=http://127.0.0.1:3000 node tests/e2e/smoke.mjs
*/
const BASE = (process.env.QA_BASE_URL || 'http://127.0.0.1:3000').replace(/\/$/, '');
const jar = new Map();
const failures = [];
function store(res) {
const list = typeof res.headers.getSetCookie === 'function' ? res.headers.getSetCookie() : [];
for (const raw of list) {
const pair = raw.split(';')[0];
const eq = pair.indexOf('=');
if (eq > 0) jar.set(pair.slice(0, eq), pair.slice(eq + 1));
}
}
async function req(path, { method = 'GET', body } = {}) {
const headers = {};
if (body !== undefined) headers['Content-Type'] = 'application/json';
if (jar.size) headers.cookie = [...jar.entries()].map(([k, v]) => `${k}=${v}`).join('; ');
const res = await fetch(BASE + path, { method, headers, body: body === undefined ? undefined : JSON.stringify(body), redirect: 'manual' });
store(res);
const json = await res.clone().json().catch(() => null);
return { status: res.status, json };
}
function check(name, cond, detail = '') {
if (cond) console.log(` ok ${name}`);
else {
console.log(` FAIL ${name}${detail ? ` — ${detail}` : ''}`);
failures.push(name);
}
}
async function run() {
const email = `smoke_${Date.now().toString(36)}@example.com`;
const health = await req('/api/health');
check('health 200', health.status === 200 && health.json?.status === 'ok', `status=${health.status}`);
// --- catalog
const catalog = await req('/api/products');
const items = catalog.json?.items ?? [];
check('catalog returns products', catalog.status === 200 && items.length > 0, `status=${catalog.status} count=${items.length}`);
const product = items.find((p) => p.name === 'Classic Tee') ?? items[0];
const filtered = await req('/api/products?category=home');
check('category filter works', filtered.status === 200 && (filtered.json?.items ?? []).every((p) => p.category?.slug === 'home'), `status=${filtered.status}`);
// --- checkout validation (server-side)
const empty = await req('/api/checkout', { method: 'POST', body: { email, address: '123 Test St', lines: [] } });
check('empty cart rejected', empty.status === 400, `status=${empty.status}`);
if (product) {
const limited = items.find((p) => typeof p.stock === 'number' && p.stock >= 1 && p.stock < 99) ?? product;
const tooMany = await req('/api/checkout', {
method: 'POST',
body: { email, address: '123 Test St', lines: [{ productId: limited.id, quantity: Math.min(99, limited.stock + 1) }] },
});
check('over-stock rejected', tooMany.status === 409, `status=${tooMany.status} stock=${limited.stock}`);
const order = await req('/api/checkout', {
method: 'POST',
body: { email, address: '123 Test St, Springfield', lines: [{ productId: product.id, quantity: 1 }] },
});
check('mock checkout creates a paid order', order.status === 201 && order.json?.order?.status === 'paid', `status=${order.status} order=${JSON.stringify(order.json?.order)}`);
}
// --- base auth / roles
const reg = await req('/api/auth/register', { method: 'POST', body: { email, password: 'password123', name: 'Smoke' } });
check('register 201', reg.status === 201, `status=${reg.status}`);
const newUserAdmin = await req('/api/admin/orders');
check('non-admin blocked from admin orders', newUserAdmin.status === 403, `status=${newUserAdmin.status}`);
await req('/api/auth/logout', { method: 'POST' });
const afterLogout = await req('/api/auth/me');
check('logout clears session', afterLogout.status === 401, `status=${afterLogout.status}`);
const adminLogin = await req('/api/auth/login', { method: 'POST', body: { email: 'admin@example.com', password: 'password' } });
check('seeded admin can sign in', adminLogin.status === 200 && adminLogin.json?.user?.role === 'admin', `status=${adminLogin.status}`);
const adminProducts = await req('/api/admin/products');
check('admin sees products', adminProducts.status === 200 && (adminProducts.json?.products ?? []).length >= 12, `status=${adminProducts.status}`);
const adminOrders = await req('/api/admin/orders');
check('admin sees orders', adminOrders.status === 200 && Array.isArray(adminOrders.json?.orders), `status=${adminOrders.status}`);
await req('/api/auth/logout', { method: 'POST' });
const customerLogin = await req('/api/auth/login', { method: 'POST', body: { email: 'customer@example.com', password: 'password' } });
check('seeded customer can sign in', customerLogin.status === 200, `status=${customerLogin.status}`);
const myOrders = await req('/api/orders');
check('customer sees own orders', myOrders.status === 200 && (myOrders.json?.orders ?? []).length >= 2, `status=${myOrders.status}`);
console.log(failures.length ? `\n${failures.length} check(s) failed` : '\nAll e2e checks passed');
process.exit(failures.length ? 1 : 0);
}
run().catch((e) => {
console.error(e);
process.exit(1);
});