Hapi.js рд╡реЗрдм рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдПрдХ рд░реВрдкрд░реЗрдЦрд╛ рд╣реИред рдЗрд╕ рдкреЛрд╕реНрдЯ рдореЗрдВ рдПрдХ рдЧрд░реНрдо рд╢реБрд░реБрдЖрдд рдХреЗ рд▓рд┐рдП рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рд╣реИрдВред рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рд▓реЗрдЦрдХ рдмрд┐рд▓реНрдХреБрд▓ рднреА рд▓реЗрдЦрдХ рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдмрд╣реБрдд рд╕рд╛рд░реЗ рдХреЛрдб рдФрд░ рдХреБрдЫ рд╢рдмреНрдж рд╣реЛрдВрдЧреЗред
рдПрдорд╡реАрдкреА
рдирд┐рд░реНрднрд░рддрд╛ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рд░рдЦреЛ:
npm i @hapi/hapi @hapi/boom filepaths hapi-boom-decorators
- hapi / hapi - рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рд╣рдорд╛рд░рд╛ рд╕рд░реНрд╡рд░
- hapi / рдмреВрдо - рдорд╛рдирдХ рдЙрддреНрддрд░ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдореЙрдбреНрдпреВрд▓
- рд╣реИрдкреНрдкреА-рдмреВрдо-рдбреЗрдХреЛрд░реЗрдЯрд░реНрд╕ - рд╣реЗрд▓реНрдкреА / рдмреВрдо рдХреЗ рд▓рд┐рдП рд╕рд╣рд╛рдпрдХ
- filepaths - рдПрдХ рдЙрдкрдпреЛрдЧрд┐рддрд╛ рдЬреЛ рдкреБрдирд░рд╛рд╡рд░реНрддреА рдлрд╝реЛрд▓реНрдбрд░ рдкрдврд╝рддреА рд╣реИ
рдПрдХ рдлрд╝реЛрд▓реНрдбрд░ рд╕рдВрд░рдЪрдирд╛ рдФрд░ рдкреНрд░рд╛рд░рдВрдн рдлрд╝рд╛рдЗрд▓реЛрдВ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдмрдирд╛рдПрдБ:

//Src/routes/ рдореЗрдВ, рд╣рдо рдПрдкреАрдЖрдИ рдПрдВрдбрдкреЙрдЗрдВрдЯреНрд╕, 1 рдлрд╛рдЗрд▓ - 1 рдПрдВрдбрдкреЙрдЗрдВрдЯ рдХрд╛ рд╡рд┐рд╡рд░рдг рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ:
./src/server.js - рд╡рд╣ рдореЙрдбреНрдпреВрд▓ рдЬреЛ рд╕рд░реНрд╡рд░ рдХреЛ рд╕реНрд╡рдпрдВ рдирд┐рд░реНрдпрд╛рдд рдХрд░рддрд╛ рд╣реИред
In//verver.js рд╣рдо рд╕рднреА рдХрд░рддреЗ рд╣реИрдВ рдХреЙрд▓ рдХреНрд░рд┐рдПрдЯрд╡рд░ ()
#!/usr/bin/env node const createServer = require('./src/server'); createServer();
рд╣рдо рд▓реЙрдиреНрдЪ рдХрд░рддреЗ рд╣реИрдВ
node server.js
рдФрд░ рдЬрд╛рдВрдЪреЗрдВ:
curl http://127.0.0.1:3030/ {"result":"ok","message":"Hello World!"} curl http://127.0.0.1:3030/test {"statusCode":404,"error":"Not Found","message":"Not Found"}
рдЬрдВрдЧрд▓реА рдореЗрдВ
рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ, рдХрдо рд╕реЗ рдХрдо, рд╣рдореЗрдВ рдПрдХ рдбреЗрдЯрд╛рдмреЗрд╕, рдПрдХ рд▓рдХрдбрд╝рд╣рд╛рд░рд╛, рдкреНрд░рд╛рдзрд┐рдХрд░рдг, рддреНрд░реБрдЯрд┐ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдФрд░ рдмрд╣реБрдд рдХреБрдЫ рдЪрд╛рд╣рд┐рдПред
рд╕реАрдХреНрд╡рд▓ рдЬреЛрдбрд╝реЗрдВ
ORM рдЕрдЧрд▓реА рдХрдбрд╝реА рдХреЛ рдПрдХ рдореЙрдбреНрдпреВрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИ:
... const Sequelize = require('sequelize'); ... await server.register([ ... { plugin: require('hapi-sequelizejs'), options: [ { name: config.db.database,
рдХреЙрд▓ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдорд╛рд░реНрдЧ рдХреЗ рдЕрдВрджрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рдЙрдкрд▓рдмреНрдз рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ:
async function response(request) { const model = request.getModel('_', '_'); }
рдЕрдиреБрд░реЛрдз рдореЗрдВ рдЕрддрд┐рд░рд┐рдХреНрдд рдореЙрдбреНрдпреВрд▓ рдЗрдВрдЬреЗрдХреНрдЯ рдХрд░реЗрдВ
"OnRequest" рдИрд╡реЗрдВрдЯ рдХреЛ рд░реЛрдХрдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рдЬрд┐рд╕рдХреЗ рдЕрдВрджрд░ рд╣рдо рдЕрдиреБрд░реЛрдз рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ рдХреЙрдиреНрдлрд┐рдЧрд░ рдФрд░ рд▓реЙрдЧрд░ рдЗрдВрдЬреЗрдХреНрдЯ рдХрд░реЗрдВрдЧреЗ:
... const Logger = require('./libs/Logger'); ... async function createServer(logLVL=config.logLVL) { ... const logger = new Logger(logLVL, 'my-hapi-app'); ... server.ext({ type: 'onRequest', method: async function (request, h) { request.server.config = Object.assign({}, config); request.server.logger = logger; return h.continue; } }); ... }
рдЙрд╕рдХреЗ рдмрд╛рдж, рдЕрдиреБрд░реЛрдз рд╣реИрдВрдбрд▓рд░ рдХреЗ рдЕрдВрджрд░, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░, рд▓реЙрдЧрд░ рдФрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рддрдХ рдкрд╣реБрдВрдЪ рд╣реЛрдЧреА, рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдореЙрдбреНрдпреВрд▓ рдмреЙрдбреА рдореЗрдВ рдХреБрдЫ рднреА рд╢рд╛рдорд┐рд▓ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЗ рдмрд┐рдирд╛:
рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдЗрдирдкреБрдЯ рдкрд░ рдЕрдиреБрд░реЛрдз рд╣реИрдВрдбрд▓рд░ рдХреЛ рдЗрд╕рдХреЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╕рдм рдХреБрдЫ рдкреНрд░рд╛рдкреНрдд рд╣реЛрдЧрд╛, рдФрд░ рд╕рдордп-рд╕рдордп рдкрд░ рдПрдХ рд╣реА рдореЙрдбреНрдпреВрд▓ рдХреЛ рд╢рд╛рдорд┐рд▓ рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред
рдкреНрд░рд╛рдзрд┐рдХрд░рдг
рд╣рд╛рдкреА рдореЗрдВ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдореЙрдбреНрдпреВрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред
... const AuthBearer = require('hapi-auth-bearer-token'); ... async function createServer(logLVL=config.logLVL) { ... await server.register([ AuthBearer, ... ]); server.auth.strategy('token', 'bearer-access-token', {
рдФрд░ рдорд╛рд░реНрдЧ рдХреЗ рдЕрдВрджрд░ рдЖрдкрдХреЛ рдпрд╣ рднреА рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рдХрд┐ рдХрд┐рд╕ рдкреНрд░рдХрд╛рд░ рдХреЗ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реИ:
module.exports = { method: 'GET', path: '/', auth: 'token',
рдпрджрд┐ рдХрдИ рдкреНрд░рдХрд╛рд░ рдХреЗ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВ:
auth: { strategies: ['token1', 'token2', 'something_else'] },
рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдореЗрдВ рддреНрд░реБрдЯрд┐
рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ, рдмреВрдо рдПрдХ рдорд╛рдирдХ рддрд░реАрдХреЗ рд╕реЗ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдЙрддреНрдкрдиреНрди рдХрд░рддрд╛ рд╣реИ, рдЕрдХреНрд╕рд░ рдЗрди рдЙрддреНрддрд░реЛрдВ рдХреЛ рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд▓рдкреЗрдЯрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред
server.ext('onPreResponse', function (request, h) {
рдбреЗрдЯрд╛ рд╕реНрдХреАрдорд╛
рдпрд╣ рдПрдХ рдЫреЛрдЯрд╛ рд▓реЗрдХрд┐рди рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╡рд┐рд╖рдп рд╣реИред рдбреЗрдЯрд╛ рдпреЛрдЬрдирд╛рдПрдВ рдЖрдкрдХреЛ рдЕрдиреБрд░реЛрдз рдХреА рд╡реИрдзрддрд╛ рдФрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреА рд╢реБрджреНрдзрддрд╛ рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреА рд╣реИрдВред рдЖрдк рдЗрди рдпреЛрдЬрдирд╛рдУрдВ рдХрд╛ рдХрд┐рддрдирд╛ рдЕрдЪреНрдЫрд╛ рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╕реНрд╡реИрдЧрд░ рдФрд░ рдСрдЯреЛрдЯреЗрд╕реНрдЯ рдРрд╕реА рдЧреБрдгрд╡рддреНрддрд╛ рдХреЗ рд╣реЛрдВрдЧреЗред
рд╕рднреА рдбреЗрдЯрд╛ рдпреЛрдЬрдирд╛рдУрдВ рдХреЛ рдЬреЛрдИ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдЖрдЗрдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЗ рд▓рд┐рдП рдПрдХ рдЙрджрд╛рд╣рд░рдг рдмрдирд╛рддреЗ рд╣реИрдВ:
const Joi = require('@hapi/joi'); const Boom = require('boom'); async function response(request) {
рдкрд░реАрдХреНрд╖рдг:
curl -X GET "http://localhost:3030/auth?login=pupkin@gmail.com&password=12345"

рдЕрдм рдореЗрд▓ рдХреЗ рдмрдЬрд╛рдп рднреЗрдЬреЗрдВ, рдХреЗрд╡рд▓ рд▓реЙрдЧрд┐рди рдХрд░реЗрдВ:
curl -X GET "http://localhost:3030/auth?login=pupkin&password=12345"

рдпрджрд┐ рдЙрддреНрддрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдпреЛрдЬрдирд╛ рд╕реЗ рдореЗрд▓ рдирд╣реАрдВ рдЦрд╛рддрд╛ рд╣реИ, рддреЛ рд╕рд░реНрд╡рд░ 500 рддреНрд░реБрдЯрд┐ рдореЗрдВ рднреА рдЧрд┐рд░ рдЬрд╛рдПрдЧрд╛ред
рдпрджрд┐ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдкреНрд░рддрд┐ рдШрдВрдЯреЗ 1 рд╕реЗ рдЕрдзрд┐рдХ рдЕрдиреБрд░реЛрдз рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рджреЗрддреА рд╣реИ, рддреЛ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЗ рд╕рддреНрдпрд╛рдкрди рдХреЛ рд╕реАрдорд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЬреИрд╕рд╛ рдХрд┐ рд╕рддреНрдпрд╛рдкрди рдПрдХ рд╕рдВрд╕рд╛рдзрди-рдЧрд╣рди рд╕рдВрдЪрд╛рд▓рди рд╣реИред рдЗрд╕рдХреЗ рд▓рд┐рдП рдПрдХ рдкреИрд░рд╛рдореАрдЯрд░ рд╣реИ: "рдирдореВрдирд╛"
module.exports = { method: 'GET', path: '/auth', options: { handler: response, validate: { query: requestScheme }, response: { sample: 50, schema: responseScheme } } };
рдЬреИрд╕реЗ, рдХреЗрд╡рд▓ 50% рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рдорд╛рдиреНрдп рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдПрдВ рдорд┐рд▓реЗрдВрдЧреАред
рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдорд╛рдиреЛрдВ рдФрд░ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдирд╛ рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ, рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдЙрдирдХрд╛ рдЙрдкрдпреЛрдЧ рдкреНрд░рд▓реЗрдЦрди рдФрд░ рдСрдЯреЛрдЯреЗрд╕реНрдЯ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред
рд╕реНрд╡реИрдЧрд░ / рдУрдкрдирдПрдкреАрдЖрдИ
рд╣рдореЗрдВ рдЕрддрд┐рд░рд┐рдХреНрдд рдореЙрдбреНрдпреВрд▓ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдЪрд╛рд╣рд┐рдП:
npm i hapi-swagger @hapi/inert @hapi/vision
рд╣рдо рдЙрдиреНрд╣реЗрдВ server.js рд╕реЗ рдХрдиреЗрдХреНрдЯ рдХрд░рддреЗ рд╣реИрдВ
... const Inert = require('@hapi/inert'); const Vision = require('@hapi/vision'); const HapiSwagger = require('hapi-swagger'); const Package = require('../package'); ... const swaggerOptions = { info: { title: Package.name + ' API Documentation', description: Package.description }, jsonPath: '/documentation.json', documentationPath: '/documentation', schemes: ['https', 'http'], host: config.swaggerHost, debug: true }; ... async function createServer(logLVL=config.logLVL) { ... await server.register([ ... Inert, Vision, { plugin: HapiSwagger, options: swaggerOptions }, ... ]); ... });
рдФрд░ рдкреНрд░рддреНрдпреЗрдХ рдорд╛рд░реНрдЧ рдореЗрдВ рдЖрдкрдХреЛ "рдПрдкреАрдЖрдИ" рдЯреИрдЧ рд▓рдЧрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:
module.exports = { method: 'GET', path: '/auth', options: { handler: response, tags: [ 'api' ],
рдЕрдм http: // рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ: 3030 / рдбреЙрдХреНрдпреВрдореЗрдВрдЯреЗрд╢рди рдореЗрдВ рд╡реЗрдм-рдлреЗрд╕ рдХреЗ рд╕рд╛рде рдбреЙрдХреНрдпреВрдореЗрдВрдЯреЗрд╢рди рдЙрдкрд▓рдмреНрдз рд╣реЛрдЧрд╛, рдФрд░ http: // рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ: 3030 / рдбреЙрдХреНрдпреВрдореЗрдВрдЯреЗрд╢рдиред Jsonред рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдПред

рдСрдЯреЛ рдЯреЗрд╕реНрдЯ рдЬрдирд░реЗрд╢рди
рдпрджрд┐ рд╣рдордиреЗ рдЧреБрдгрд╛рддреНрдордХ рд░реВрдк рд╕реЗ рдЕрдиреБрд░реЛрдз рдФрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдпреЛрдЬрдирд╛рдУрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд┐рдпрд╛ рд╣реИ, рддреЛ рдЕрдиреБрд░реЛрдз рдХреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЗ рдЕрдиреБрд░реВрдк рдПрдХ рдмреАрдЬ рдбреЗрдЯрд╛рдмреЗрд╕ рддреИрдпрд╛рд░ рдХрд┐рдпрд╛ рд╣реИ, рдлрд┐рд░, рдкреНрд░рд╕рд┐рджреНрдз рдпреЛрдЬрдирд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЖрдк рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдЕрдиреБрд░реЛрдз рдЙрддреНрдкрдиреНрди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рд╕рд░реНрд╡рд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛрдб рдХреА рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, GET рдореЗрдВ: / рд╕рд╛рдорд╛рдиреНрдп рд▓реЙрдЧрд┐рди рдФрд░ рдкрд╛рд╕рд╡рд░реНрдб рдкреИрд░рд╛рдореАрдЯрд░ рдЕрдкреЗрдХреНрд╖рд┐рдд рд╣реИрдВ, рд╣рдо рдЙрдиреНрд╣реЗрдВ рдЙрди рдЙрджрд╛рд╣рд░рдгреЛрдВ рд╕реЗ рд▓реЗрдВрдЧреЗ рдЬрд┐рдиреНрд╣реЗрдВ рд╣рдордиреЗ рдЖрд░реЗрдЦ рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд┐рдпрд╛ рд╣реИ:
const requestScheme =Joi.object({ login: Joi.string().email().required().example('pupkin@gmail.com'), password: Joi.string().required().example('12345') });
рдФрд░ рдпрджрд┐ рд╕рд░реНрд╡рд░ HTTP-200-OK рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░рддрд╛ рд╣реИ, рддреЛ рд╣рдо рдорд╛рди рд▓реЗрдВрдЧреЗ рдХрд┐ рдкрд░реАрдХреНрд╖рдг рдкрд╛рд╕ рд╣реЛ рдЧрдпрд╛ рд╣реИред
рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рдХреЛрдИ рддреИрдпрд╛рд░-рдЙрдкрдпреБрдХреНрдд рдЙрдкрдпреБрдХреНрдд рдореЙрдбреНрдпреВрд▓ рдирд╣реАрдВ рдерд╛, рдЖрдкрдХреЛ рдереЛрдбрд╝реА рд╕реА рдмрд╛рдд рдХрд░рдиреА рд╣реЛрдЧреА:
рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдордд рднреВрд▓рдирд╛:
npm i request-promise mocha sync-request
рдФрд░ package.json рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ
... "scripts": { "test": "mocha", "dbinit": "node ./scripts/dbInit.js" }, ...
рд╣рдо рдЬрд╛рдБрдЪ рдХрд░рддреЗ рд╣реИрдВ:
npm test

рдФрд░ рдЕрдЧрд░ рддрд╛рд▓рд╛ рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХреА рдбреЗрдЯрд╛ рдпреЛрдЬрдирд╛ рд╣реИ, рдпрд╛ рдЙрддреНрддрд░ рдпреЛрдЬрдирд╛ рд╕реЗ рдореЗрд▓ рдирд╣реАрдВ рдЦрд╛рддрд╛ рд╣реИ:

рдФрд░ рдпрд╣ рдордд рднреВрд▓реЛ рдХрд┐ рдЬрд┐рддрдиреА рдЬрд▓реНрджреА рдпрд╛ рдмрд╛рдж рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдбреЗрдЯрд╛ рдХреЗ рдкреНрд░рддрд┐ рд╕рдВрд╡реЗрджрдирд╢реАрд▓ рд╣реЛ рдЬрд╛рдПрдВрдЧреЗред рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдЖрдкрдХреЛ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЛ рдХрдо рд╕реЗ рдХрдо рдкреЛрдВрдЫрдирд╛ рд╣реЛрдЧрд╛ред
рд╕рдВрдкреВрд░реНрдг рд╕реНрд░реЛрдд