Files
goodgo-platform/e2e/api/listings.spec.ts
Ho Ngoc Hai da10ac64c6 test(e2e): update all E2E specs for latest API and fixtures
Update 17 E2E test files including admin, auth, inquiries, listings,
payments, search, subscriptions, and MCP specs. Update listings fixture
and global setup to align with latest schema changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:40:45 +07:00

185 lines
5.9 KiB
TypeScript

import { test, expect, registerUser } from '../fixtures';
import { createTestListing, createListing } from '../fixtures/listings.fixture';
test.describe('Listings API', () => {
let accessToken: string;
test.beforeAll(async ({ request }) => {
const { accessToken: token } = await registerUser(request);
accessToken = token;
});
test.describe('POST /listings — Create listing', () => {
test('creates a listing with valid data', async ({ request }) => {
const data = createTestListing();
const res = await request.post('listings', {
data,
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(res.status()).toBe(201);
const body = await res.json();
expect(body).toHaveProperty('listingId');
expect(body).toHaveProperty('status');
});
test('rejects listing with missing required fields', async ({ request }) => {
const res = await request.post('listings', {
data: { title: 'Incomplete' },
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(400);
});
test('rejects listing with invalid property type', async ({ request }) => {
const data = createTestListing({ propertyType: 'INVALID_TYPE' });
const res = await request.post('listings', {
data,
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(400);
});
test('rejects unauthenticated request', async ({ request }) => {
const res = await request.post('listings', {
data: createTestListing(),
});
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(401);
});
test('creates a RENT listing', async ({ request }) => {
const data = createTestListing({
transactionType: 'RENT',
rentPriceMonthly: '15000000',
});
const res = await request.post('listings', {
data,
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(res.status()).toBe(201);
const body = await res.json();
expect(body).toHaveProperty('listingId');
});
});
test.describe('GET /listings — Search listings', () => {
test('returns paginated listing results', async ({ request }) => {
const res = await request.get('listings');
expect(res.ok()).toBeTruthy();
const body = await res.json();
expect(body).toHaveProperty('data');
expect(Array.isArray(body.data)).toBeTruthy();
expect(body).toHaveProperty('total');
});
test('filters by property type', async ({ request }) => {
const res = await request.get('listings', {
params: { propertyType: 'APARTMENT' },
});
expect(res.ok()).toBeTruthy();
const body = await res.json();
for (const listing of body.data) {
expect(listing.property.propertyType).toBe('APARTMENT');
}
});
test('filters by transaction type', async ({ request }) => {
const res = await request.get('listings', {
params: { transactionType: 'SALE' },
});
expect(res.ok()).toBeTruthy();
const body = await res.json();
for (const listing of body.data) {
expect(listing.transactionType).toBe('SALE');
}
});
test('filters by city', async ({ request }) => {
const res = await request.get('listings', {
params: { city: 'Hồ Chí Minh' },
});
expect(res.ok()).toBeTruthy();
});
test('paginates correctly', async ({ request }) => {
const res = await request.get('listings', {
params: { page: 1, limit: 2 },
});
expect(res.ok()).toBeTruthy();
const body = await res.json();
expect(body.data.length).toBeLessThanOrEqual(2);
});
});
test.describe('GET /listings/:id — Get listing detail', () => {
test('returns listing by id', async ({ request }) => {
// First create a listing
const { listing } = await createListing(request, accessToken);
const res = await request.get(`listings/${listing.listingId}`);
expect(res.status()).toBe(200);
const body = await res.json();
expect(body.id).toBe(listing.listingId);
expect(body).toHaveProperty('property');
expect(body.property).toHaveProperty('title');
expect(body.property).toHaveProperty('address');
});
test('returns 404 for non-existent listing', async ({ request }) => {
const res = await request.get('listings/non-existent-id-12345');
expect(res.ok()).toBeFalsy();
expect([404, 400]).toContain(res.status());
});
});
test.describe('PATCH /listings/:id/status — Update listing status', () => {
test('updates listing status', async ({ request }) => {
const { listing } = await createListing(request, accessToken);
const res = await request.patch(`listings/${listing.listingId}/status`, {
data: { status: 'ACTIVE' },
headers: { Authorization: `Bearer ${accessToken}` },
});
// DRAFT → ACTIVE may be rejected by business rules (e.g. moderation required)
expect([200, 400, 403]).toContain(res.status());
});
test('rejects invalid status value', async ({ request }) => {
const { listing } = await createListing(request, accessToken);
const res = await request.patch(`listings/${listing.listingId}/status`, {
data: { status: 'INVALID_STATUS' },
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(400);
});
test('rejects unauthenticated status update', async ({ request }) => {
const { listing } = await createListing(request, accessToken);
const res = await request.patch(`listings/${listing.listingId}/status`, {
data: { status: 'ACTIVE' },
});
expect(res.status()).toBe(401);
});
});
});