Skip to content

Commit

Permalink
Merge pull request #171 from testjavascript/refactor/typescript
Browse files Browse the repository at this point in the history
TypeScript support, mocking chapter, a generic test file setup helper and more
  • Loading branch information
goldbergyoni authored Feb 19, 2025
2 parents d39e40a + abd1897 commit 7843204
Show file tree
Hide file tree
Showing 57 changed files with 14,135 additions and 18,802 deletions.
32 changes: 17 additions & 15 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,27 @@ on:
branches:
- '*'
pull_request:
branches: [ master ]
branches: [master]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Cache node_modules
uses: actions/cache@v4
with:
path: |
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Read .nvmrc
run: echo "##[set-output name=NVMRC;]$(cat .nvmrc)"
id: nvm

- name: Use Node.js (.nvmrc)
uses: actions/setup-node@v2
with:
node-version: "${{ steps.nvm.outputs.NVMRC }}"

- run: npm ci
- run: npm test
- run: npm run test:nestjs
- run: npm run test:mocha
- run: npm ci
- run: npm test
- run: npm run test:nestjs
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18
22.11
824 changes: 502 additions & 322 deletions README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const MessageQueueClient = require('../../example-application/libraries/message-queue-client');
const MessageQueueClient = require('../example-application/libraries/message-queue-client');

beforeEach(async () => {
const messageQueueClient = new MessageQueueClient();
Expand Down
31 changes: 20 additions & 11 deletions example-application/business-logic/order-service.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const util = require('util');
const axios = require('axios');
const axios = require('axios').default;
const mailer = require('../libraries/mailer');
const axiosRetry = require('axios-retry');
const axiosRetry = require('axios-retry').default;
const OrderRepository = require('../data-access/order-repository');
const { AppError } = require('../error-handling');
const MessageQueueClient = require('../libraries/message-queue-client');
const { AxiosError } = require('axios');

const axiosHTTPClient = axios.create();
axiosRetry(axiosHTTPClient, { retries: 3 });
const axiosHTTPClient = axios.create({});

module.exports.addOrder = async function (newOrder) {
// validation
Expand All @@ -17,31 +17,38 @@ module.exports.addOrder = async function (newOrder) {

// verify user existence by calling external Microservice
const userWhoOrdered = await getUserFromUsersService(newOrder.userId);

console.log('userWhoOrdered', userWhoOrdered);
if (!userWhoOrdered) {
console.log('The user was not found');
throw new AppError(
'user-doesnt-exist',
`The user ${newOrder.userId} doesnt exist`,
404
404,
);
}

// A little logic to calculate the total price
if (newOrder.isPremiumUser) {
newOrder.totalPrice = Math.ceil(newOrder.totalPrice * 0.9);
}

// save to DB (Caution: simplistic code without layers and validation)
const DBResponse = await new OrderRepository().addOrder(newOrder);

console.log('DBResponse', DBResponse);
if (process.env.SEND_MAILS === 'true') {
await mailer.send(
'New order was placed',
`user ${DBResponse.userId} ordered ${DBResponse.productId}`,
'admin@app.com'
'admin@app.com',
);
}

// We should notify others that a new order was added - Let's put a message in a queue
await new MessageQueueClient().publish(
'order.events',
'order.events.new',
newOrder
newOrder,
);

return DBResponse;
Expand All @@ -60,19 +67,21 @@ async function getUserFromUsersService(userId) {
const getUserResponse = await axiosHTTPClient.get(
`http://localhost/user/${userId}`,
{
timeout: 2000,
timeout: process.env.HTTP_TIMEOUT
? parseInt(process.env.HTTP_TIMEOUT)
: 2000,
validateStatus: (status) => {
return status < 500;
},
}
},
);
return getUserResponse.data;
} catch (error) {
if (error?.code === 'ECONNABORTED') {
throw new AppError(
'user-verification-failed',
`Request to user service failed so user cant be verified`,
503
503,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@ module.exports = {
mode: {
type: Sequelize.STRING,
},
contactEmail: {
type: Sequelize.STRING,
},
userId: {
type: Sequelize.INTEGER,
},
productId: {
type: Sequelize.INTEGER,
},
totalPrice: {
type: Sequelize.INTEGER,
},
isPremiumUser: {
type: Sequelize.BOOLEAN,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
Expand All @@ -40,9 +49,9 @@ module.exports = {
},
name: {
type: Sequelize.STRING,
}
},
});
},

down: (queryInterface, Sequelize) => queryInterface.dropTable("Orders"),
down: (queryInterface, Sequelize) => queryInterface.dropTable('Orders'),
};
12 changes: 10 additions & 2 deletions example-application/data-access/order-repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = class OrderRepository {
'shop',
'myuser',
'myuserpassword',
sequelizeConfig
sequelizeConfig,
);
orderModel = repository.define('Order', {
id: {
Expand All @@ -30,16 +30,24 @@ module.exports = class OrderRepository {
userId: {
type: Sequelize.INTEGER,
},
contactEmail: {
type: Sequelize.STRING,
},
productId: {
type: Sequelize.INTEGER,
},
totalPrice: {
type: Sequelize.INTEGER,
},
isPremiumUser: {
type: Sequelize.BOOLEAN,
},
});
}
}

async getOrderById(id) {
return await orderModel.findOne({ where: { id } });

}

async addOrder(orderDetails) {
Expand Down
36 changes: 24 additions & 12 deletions example-application/data-access/seeders/20191229151823-countries.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
// ✅ Best Practice: Seed only metadata and not test record, read "Dealing with data" section for further information
await queryInterface.bulkInsert('Countries', [{
name: 'Italy',
name: 'USA',
name: 'India'
}], {});
await queryInterface.bulkInsert(
'Countries',
[
{
name: 'Italy',
name: 'USA',
name: 'India',
},
],
{},
);

// ❌ Anti-Pattern: Seed test records, read "Dealing with data" section for further information
const now = new Date();
await queryInterface.bulkInsert('Orders', [{
userId: 1,
productId: 5,
createdAt: now,
updatedAt: now,
}], {});
await queryInterface.bulkInsert(
'Orders',
[
{
userId: 1,
productId: 5,
createdAt: now,
updatedAt: now,
},
],
{},
);
},
down: (queryInterface, Sequelize) => {
/*
Expand All @@ -25,4 +37,4 @@ module.exports = {
return queryInterface.bulkDelete('People', null, {});
*/
},
};
};
102 changes: 0 additions & 102 deletions example-application/entry-points/api.js

This file was deleted.

Loading

0 comments on commit 7843204

Please sign in to comment.