Skip to content

src/__tests__/products.e2e.test.ts โ€‹

From Week 5 in-class: testing ยท original: si-679-f-26-week05-nyt-jessicadonoho/src/__tests__/products.e2e.test.ts ยท all files in this project

ts
import {
  afterAll,
  beforeAll,
  beforeEach,
  describe,
  expect,
  it
} from 'vitest';
import request from 'supertest';
import { MongoMemoryServer } from 'mongodb-memory-server';
import { app } from '../app.js';
import { connect, disconnect } from '../db/db.js';
import * as productsRepository from '../db/products-repository.js';
import type { Product } from '../models/product.js';
import { testProducts } from './test-products.js';

let mongo: MongoMemoryServer;

beforeAll(async () => {
  mongo = await MongoMemoryServer.create();
  await connect(mongo.getUri(), 'test');
});

afterAll(async () => {
  await disconnect();
  await mongo.stop();
});

beforeEach(async () => {
  await productsRepository._clearProducts();
  for (const product of testProducts) {
    await productsRepository.insertProduct(product);
  }
});

describe('GET /products', () => {
  it('lists the products that are in the database', async () => {
    const res = await request(app).get('/products');

    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(4);

    const manufacturers = res.body.map(
      (product: Product) => product.manufacturer
    );
    expect(manufacturers).toContain('Mr. Coffee');
    expect(manufacturers).not.toContain('Bialetti');
  });

  it('gives every product an id', async () => {
    const res = await request(app).get('/products');

    for (const product of res.body) {
      expect(product.id).toMatch(/^[0-9a-f]{24}$/);
      expect(product._id).toBeUndefined();
    }
  });

  it('returns only the low stock products when asked', async () => {
    const res = await request(app).get('/products?lowStock=true');

    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(2);

    const manufacturers = res.body.map(
      (product: Product) => product.manufacturer
    );
    expect(manufacturers).toEqual(['REVOTRA', 'Keurig']);
  });
});

describe('GET /products/:id', () => {
  it('returns the product with that id', async () => {
    const all = await request(app).get('/products');
    const { id, modelName } = all.body[0];

    const res = await request(app).get(`/products/${id}`);

    expect(res.status).toBe(200);
    expect(res.body.id).toBe(id);
    expect(res.body.modelName).toBe(modelName);
  });

  it('returns 404 for an id that is not in the database', async () => {
    const res = await request(app).get(`/products/${'a'.repeat(24)}`);

    expect(res.status).toBe(404);
  });
});

describe('POST /products', () => {
  it('adds a product we can then read back', async () => {
    const created = await request(app).post('/products').send({
      modelName: 'Moka Express',
      modelNumber: '1168',
      manufacturer: 'Bialetti',
      color: 'Aluminium',
      price: 34.95,
      quantity: 60
    });

    expect(created.status).toBe(201);
    expect(created.body.id).toMatch(/^[0-9a-f]{24}$/);

    const all = await request(app).get('/products');
    expect(all.body).toHaveLength(5);
  });

  it('fills in defaults for the fields that were left out', async () => {
    const created = await request(app)
      .post('/products')
      .send({ modelName: 'Mystery Machine' });

    const all = await request(app).get('/products');
    const stored = all.body.find(
      (product: Product) => product.id === created.body.id
    );

    expect(stored.modelName).toBe('Mystery Machine');
    expect(stored.modelNumber).toBe('');
    expect(stored.quantity).toBe(0);
  });
});

describe('PATCH /products/:id', () => {
  it('updates the field that was sent', async () => {
    const all = await request(app).get('/products');
    const { id } = all.body[0];

    const res = await request(app)
      .patch(`/products/${id}`)
      .send({ price: 12.5 });

    expect(res.status).toBe(200);
    expect(res.body.price).toBe(12.5);
  });

  it('rejects a change that would make quantity negative', async () => {
    const all = await request(app).get('/products');
    const { id } = all.body[0];

    const res = await request(app)
      .patch(`/products/${id}`)
      .send({ quantity: -1 });

    expect(res.status).toBe(400);
    expect(res.body.error).toBe('quantity cannot be negative');
  });

  it.todo('404s when the id is not in the database');

  it('rejects a change that would make the price negative', async()=>{
    const all = await request(app).get('/products');
    const {id} = all.body[0];

    const res = await request(app)
      .patch(`/products/${id}`)
      .send({price:-1});

    expect(res.status).toBe(400);
    expect(res.body.error).toBe("price cannot be negative")
  });

  it('leaves the other fields alone', async()=>{
    const all = await request(app).get('/products');
    const { id } = all.body[0];

    const res = await request(app)
      .patch(`/products/${id}`)
      .send({ price: 12.5 });

    expect(res.body.modelName).toBe("14 Cup Programmable Coffee Maker");
    expect(res.body.modelNumber).toBe("2143561");
    expect(res.body.manufacturer).toBe("Mr. Coffee");
    expect(res.body.color).toBe("Silver");
    expect(res.body.quantity).toBe(120);


  });

  it('leaves the product unchanged after a rejected change', async()=>{
    const all = await request(app).get('/products');
    const {id} = all.body[0];

    const res = await request(app)
      .patch(`/products/${id}`)
      .send({price:-1});

      const after = await request(app).get(`/products`);
      expect(after.body).toEqual(all.body)
  });
});