чернетка

на комерції тести переважно не пишуть

тестові програми можуть бути довші ніж сам код

-------

напишемо код програми перевірки , яка перевірятиме фомат символів імені юзера.

в utils створимо userNamesHandler.js

/**
 * User name handler function.
 * @param {string} name - user name
 * @returns {string}
 */
module.exports = (name) => {
  if (typeof name !== 'string') return '';

  const handledUserNameArray = name
    .normalize('NFD')
    .replace(/\p{Diacritic}/gu, '')
    .toLowerCase()
    .replace(/[^a-z]/g, ' ')
    .split(' ');

  const resultArray = [];

  for (const item of handledUserNameArray) {
    if (item) resultArray.push(item.charAt(0).toUpperCase() + item.slice(1));
  }

  return resultArray.join(' ');
};

В коді вище отримується рялок, замінюються діакратичні символи, а також літери всі символи крім латинки після цього розбивається на масив по пробілу.

укінці збільшуємо перший символ, і обїєднуємо масив в рядок через пробіл.

тепер в userServices.js в функції signupUser допишемо такий код.

const userNamesHandler = require("../utils/userNamesHandler")
exports.signupUser = async (userData) => {
  const { name, ...restUserData } = userData;
  const newUserData = {
    ...restUserData,
    name: userNamesHandler(name),
    role: userRolesEnum.USER,
  };

  const newUser = await User.create(newUserData);
  newUser.password = undefined;

  const token = signToken(newUser.id);
  return { user: newUser, token };
};

тепер при створенні контакта через постмен. нам будуть виправлятися оці неправильні внесені дані

Але нам треба потестити цю функцію.

ставимо пакети в режимі розробника

npm i -D jest supertest

впекедж джсон ставимо скрипт на тес

    "test": "jest"

А в папці, де лежить наш код для перевірки (тобто в утілс) створимо папку __test__

ці файди не будуть застосовані в застосунку, а тільки для тестів використовуватися

і за таким само форматом всередині створимо файл userNamesHandler.test.js.

створимо програмку для тестування для перевірки роботи (окремий юніт тест)

// Set test suite.
const calc = (a, b) => a + b;

describe("Test example", () => {
  // Set single test
  test("test calc sum 2 + 2", () => {
    expect(calc(2, 2)).toBe(4);
  });

  // Set single test
  it("should return 5", () => {
    expect(calc(2, 3)).toBe(5);
  });
});

тут describe - приймає назву тесту і колбек функцію. а в ній команду test (замість test можна писати it("should", ()=>{})), яка першим параметром приймає назву тесту, а другим також кодбек функцію, в якій вписуємо expect (тобто шо робимо) і toBe (тобто що маємо отримати)

щоб запустити тест потрібно в консолі запустити

npm run test

перевіряємо результати тесту в консолі

тепрер напишемо теск кейс для нашого прикдалу перевірки логіна

можна задати масив кейсів і циклом пройтися перевірити всі кейси

аде також можна писати тес і окремо під кожен окремий кейс.

const userNameHandler = require("../userNamesHandler");

const testingData = [
  { input: "Jimi Hendrix", output: "Jimi Hendrix" },
  { input: "jimi hendrix", output: "Jimi Hendrix" },
  { input: "jimi Hendrix", output: "Jimi Hendrix" },
  { input: "   Jimi  hendriX ", output: "Jimi Hendrix" },
  { input: "Jimi_Hendrix", output: "Jimi Hendrix" },
  { input: "jimi.hendrix", output: "Jimi Hendrix" },
  { input: "jimi@hend@rix", output: "Jimi Hend Rix" },
  { input: "_jimi * hendrix", output: "Jimi Hendrix" },
  { input: "jimi hèndrix__", output: "Jimi Hendrix" },
  { input: "jimi中村hèndrix__", output: "Jimi Hendrix" },
  { input: "jimi de Hèndrix__", output: "Jimi De Hendrix" },
  { input: "中村哲二", output: "" },
  { input: undefined, output: "" },
  { input: null, output: "" },
  { input: true, output: "" },
];

describe("User names handler tests", () => {
  it("should returns Jimi Hendrix", () => {
    expect(userNameHandler(testingData[0].input)).toBe(testingData[0].output);
  });

  test("test all user name cases", () => {
    for (const item of testingData) {
      const normalizedName = userNameHandler(item.input);

      expect(normalizedName).toBe(item.output);
    }
  });
});

Тут ми перебираємо циклом варіанти і якщо хоч один тест не виконається, то в консолі буде помилка

Тепер напишемо тест на цілий роут логіна

спочатку в індекс файлі експортнемо наз запуск сервера

// ===========SERVER INIT=========== //
module.exports = app.listen(3000, () => {
  console.log("server is up and runned on port 3000");
});

Відповідно у папці контролерів створюємо папку __test__ і в ній файл який будемо тестувати authController.test.js

const request = require("supertest");

const app = require("../../index");

describe("POST /auth/login", () => {
  beforeAll(() => {
    console.log("before all");
  });

  beforeEach(() => {
    console.log("before each");
  });

  afterEach(() => {
    console.log("after each");
  });

  afterAll(() => {
    console.log("after all");
  });

  it("should return user object and jwt", async () => {
    const testData = {
      email: "khomiak3@gmail.com",
      password: "Password1234$",
    };

    const res = await request(app).post("/auth/login").send(testData);

    expect(res.statusCode).toBe(200);
    expect(res.body).toEqual(
      expect.objectContaining({
        token: expect.any(String),
        user: expect.any(Object),
      })
    );
  });

  it("should return unauth error", async () => {
    const testData = {
      email: "khomiak3@gmail.com",
      password: "Password1234$",
    };

    const res = await request(app).post("/auth/login").send(testData);

    expect(res.statusCode).toBe(401);
  });

  it("should return unauth error", async () => {
    const testData = {
      email: "khomiak3@gmail.com",
    };

    const res = await request(app).post("/auth/login").send(testData);

    expect(res.statusCode).toBe(401);
  });
});

розберемо той код. спочатку підключення імітатора запитів, а потім імпорт підключення самого сервера

beforeAll, beforeEach, afterEach, afterAll - це хуки в які можна прописувати різний функціонал

при запуску тесту перевірятимуться всі умови тесту

ТЕМПЛЕЙТ ДВІЖКИ (ЩАБЛОНІЗАТОРИ)

приклад шаблонізатора

https://ejs.co/

https://pugjs.org/api/getting-started.html

Розглянемо на прикладі паг

підключаються по-ріщному

можна в індекс фпйлі його підключити перед пілключенням до бази даних

app.set('view engine', 'pug');
app.set('views', path.join(__dirname, 'views'));

перший рядок підключення паг

другий рядок вказання папки де лежатимуть темплейти

відтак в корені кадалога створимо папку views

а в ньому підпапку layouts а вде у ній файл main.pug

Зашоблонізуємо в ньому html-файл

І цьому випадку закриваючі теги не потрібні, а вкладеність визначається табудяцією. стидізацію там де цсс будемо брати зі статікс, який ми щаримо назовні для всіх користувачів

у шаблонізаторах можна передавати змінні

подивимося яуий вигляд матиме файл main.pug

doctype html
html(lang="en")
  head
    meta(charset="UTF-8")
    meta(name="viewport" content="width=device-width, initial-scale=1.0")
    title #{title}
    link(rel="stylesheet" href="/css/main.css")

  body
    header.header
      nav.header__nav
        ul.header__item-list
          li.header__item
            a(href="/" class=(active === 'home' ? 'active' : '')) Home
          li.header__item
            a(href="/todos" class=(active === 'todos' ? 'active' : '')) Todos List

    main.main
      block content

    footer.footer
      .footer__info
        .footer__copy All rights reserved ©
        .footer__year 2023

можна використати плагін для vc beautify pug. він правиль чистить пробіли зайві і виставляє коректно табуляцію.

створимо додатково два файли home.pug і todos.pug

home.pug
extends layouts/main

block content
  h1 TODO APP!!
  p This our super hyper project!!

тут вказуємо що будемо вставляти дані в layouts/main а нижче вказуємо назва блоку і сам контент

todos.pug
extends layouts/main

block content
  h1 Todos list
  .todos
    each todo in todos
      .todo
        .owner
          figure.owner__pic
            img(src= todo.owner.avatar)
          p.owner__name= todo.owner.name
        .todo__content
          h3.h3= todo.title
          p.description= todo.description
          p.duedate= todo.due.toLocaleString('uk-UA')

примітно, що якшо зочемо передати змінну то ставимо дорівн.є, а інакше це буде передаватися текст

Шаблони творені і тепер їх треба роздати . завдяки окремим роутам. а підключаємо в індекс файлі де всі роути

const viewRoutes = require('./routes/viewRoutes');
app.use('/', viewRoutes);

тепер пропишемо сам роут (ці роути будуть просто передавати на фронт шаблони з певними даними)

viewRoutes.js
const { Router } = require('express');

const viewController = require('../controllers/viewController');

const router = Router();

router.get('/', viewController.home);
router.get('/todos', viewController.todos);

module.exports = router;

також напищемо контролер

viewController.js
const Todo = require('../models/todoModel');
const { catchAsync } = require('../utils');

exports.home = (req, res) => {
  res.status(200).render('home', {
    title: 'Todos Home',
    active: 'home',
  });
};

exports.todos = catchAsync(async (req, res) => {
  const todos = await Todo.find().populate('owner');

  res.status(200).render('todos', {
    title: 'Todos List',
    todos,
    active: 'todos',
  });
});

По суті це інструкція як з бекенду роздати фронтенд

щоб перевірити треба запустити сервер і пройти на http://localhost:3000/

але видалити раніще створений index.html або перейменувати його. і нам підвантажиться уся наща прописана логіка

Утім в індекс файлі може дублюватися і тому в рядках роуту в індекс пишуть rest-api в роутах, зоб не було збігів з тими роутами які ми роздаємо на зовні через браузер

на фронті після створення реакту це вже використовується досить рідко. переважно використовують для імейлів

Last updated