test: add testing framework with sample test

This commit is contained in:
Jon Deaves
2020-08-14 19:32:14 +01:00
committed by GitHub
parent 174524e8dc
commit a8dbd0c1ac
9 changed files with 1128 additions and 120 deletions

View File

@@ -3,23 +3,17 @@ module.exports = {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
plugins: [
"@typescript-eslint",
"eslint-comments",
"jest",
"promise",
"unicorn",
],
plugins: ['@typescript-eslint', 'eslint-comments', 'jest', 'promise', 'unicorn'],
extends: [
"airbnb-typescript",
"plugin:@typescript-eslint/recommended",
"plugin:eslint-comments/recommended",
"plugin:jest/recommended",
"plugin:promise/recommended",
"plugin:unicorn/recommended",
"prettier",
"prettier/react",
"prettier/@typescript-eslint",
'airbnb-typescript',
'plugin:@typescript-eslint/recommended',
'plugin:eslint-comments/recommended',
'plugin:jest/recommended',
'plugin:promise/recommended',
'plugin:unicorn/recommended',
'prettier',
'prettier/react',
'prettier/@typescript-eslint',
],
env: {
node: true,
@@ -28,32 +22,37 @@ module.exports = {
},
rules: {
// Too restrictive, writing ugly code to defend against a very unlikely scenario: https://eslint.org/docs/rules/no-prototype-builtins
"no-prototype-builtins": "off",
'no-prototype-builtins': 'off',
// https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
"import/prefer-default-export": "off",
"import/no-default-export": "off",
'import/prefer-default-export': 'off',
'import/no-default-export': 'off',
// Too restrictive: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/destructuring-assignment.md
"react/destructuring-assignment": "off",
'react/destructuring-assignment': 'off',
// No jsx extension: https://github.com/facebook/create-react-app/issues/87#issuecomment-234627904
"react/jsx-filename-extension": "off",
'react/jsx-filename-extension': 'off',
// Use function hoisting to improve code readability
"no-use-before-define": [
"error",
{ functions: false, classes: true, variables: true },
],
'no-use-before-define': ['error', { functions: false, classes: true, variables: true }],
// Makes no sense to allow type inferrence for expression parameters, but require typing the response
"@typescript-eslint/explicit-function-return-type": [
"error",
'@typescript-eslint/explicit-function-return-type': [
'error',
{ allowExpressions: true, allowTypedFunctionExpressions: true },
],
"@typescript-eslint/no-use-before-define": [
"error",
'@typescript-eslint/no-use-before-define': [
'error',
{ functions: false, classes: true, variables: true, typedefs: true },
],
// Common abbreviations are known and readable
"unicorn/prevent-abbreviations": "off",
"unicorn/filename-case": "off",
"unicorn/no-fn-reference-in-iterator": "off",
"no-underscore-dangle": ["error", { "allowAfterThis": true }]
'unicorn/prevent-abbreviations': 'off',
'unicorn/filename-case': 'off',
'unicorn/no-fn-reference-in-iterator': 'off',
'no-underscore-dangle': ['error', { allowAfterThis: true }],
},
}
overrides: [
{
files: ['*.spec.ts'],
rules: {
'jest/valid-expect': 0,
},
},
],
};

24
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Node.js CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: yarn install
- run: yarn build
- run: yarn test
env:
CI: true

View File

@@ -14,7 +14,8 @@
"build": "tsc -p tsconfig.build.json",
"start": "node dist/main.js",
"start:dev": "nodemon",
"lint": "run-p lint:code lint:markdown",
"test": "nyc --require ts-node/register mocha src/**/*.spec.ts --reporter spec --retries 3 --require 'node_modules/reflect-metadata/Reflect.js' --exit",
"lint": "lint:code",
"lint:code": "eslint src/**/*.{ts,js}",
"lint:markdown": "markdownlint **/*.md",
"format": "prettier \"**/*.ts\" --ignore-path ./.prettierignore --write",
@@ -32,14 +33,21 @@
"winston": "~3.3.3"
},
"devDependencies": {
"@commitlint/config-conventional": "^9.1.1",
"@types/chai": "^4.2.12",
"@types/chai-as-promised": "^7.1.3",
"@types/dotenv": "~8.2.0",
"@types/mocha": "^8.0.2",
"@types/mongodb": "~3.5.25",
"@types/node": "~14.0.27",
"@types/reflect-metadata": "^0.1.0",
"@types/sinon": "^9.0.4",
"@types/winston": "~2.4.4",
"@typescript-eslint/eslint-plugin": "^3.6.1",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chalk": "^4.1.0",
"commitlint": "^9.1.0",
"@commitlint/config-conventional": "^9.1.1",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^9.0.0",
"eslint-config-prettier": "^6.11.0",
@@ -54,10 +62,15 @@
"husky": "^4.2.5",
"lint-staged": "^10.2.11",
"markdownlint-cli": "^0.23.2",
"mocha": "^8.1.1",
"nodemon": "~2.0.4",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"prettier": "~2.0.5",
"sinon": "^9.0.3",
"sinon-chai": "^3.5.0",
"standard-version": "^8.0.2",
"supertest": "^4.0.2",
"ts-node": "~8.10.2",
"tsconfig-paths": "~3.9.0",
"typescript": "~3.9.7"
@@ -71,12 +84,11 @@
"lint-staged": {
"*.{ts,tsx,js}": [
"yarn format",
"yarn lint:code",
"yarn lint:markdown"
"yarn lint:code"
]
},
"engines": {
"node": ">=10 <=14",
"node": ">=12 <=14",
"yarn": ">=1.10.0"
}
}

View File

@@ -0,0 +1,81 @@
import 'mocha';
import { expect } from 'chai';
import ConfigService from './config.service';
describe('ConfigService', () => {
let configService: ConfigService;
beforeEach(() => {});
describe('isProd', () => {
it('should return true when environment is production', async () => {
process.env.NODE_ENV = 'production';
configService = new ConfigService();
const result = configService.isProd;
expect(result).to.be.equal(true);
});
it('should return false when environment is development', async () => {
process.env.NODE_ENV = 'development';
configService = new ConfigService();
const result = configService.isProd;
expect(result).to.be.equal(false);
});
it('should return false when environment is testing', async () => {
process.env.NODE_ENV = 'testing';
configService = new ConfigService();
const result = configService.isProd;
expect(result).to.be.equal(false);
});
});
describe('get', () => {
before(() => {
process.env = {
BOT_TRIGGER: '1',
DISCORD_BOT_TOKEN: '2',
MONGODB_URI: '3',
MONGODB_DB_NAME: '4',
NODE_ENV: '5',
LOG_LEVEL: '6',
};
configService = new ConfigService();
});
it('should return correct value for BOT_TRIGGER', async () => {
expect(configService.get('BOT_TRIGGER')).to.be.equal('1');
});
it('should return correct value for DISCORD_BOT_TOKEN', async () => {
expect(configService.get('DISCORD_BOT_TOKEN')).to.be.equal('2');
});
it('should return correct value for MONGODB_URI', async () => {
expect(configService.get('MONGODB_URI')).to.be.equal('3');
});
it('should return correct value for MONGODB_DB_NAME', async () => {
expect(configService.get('MONGODB_DB_NAME')).to.be.equal('4');
});
it('should return correct value for NODE_ENV', async () => {
expect(configService.get('NODE_ENV')).to.be.equal('5');
});
it('should return correct value for LOG_LEVEL', async () => {
expect(configService.get('LOG_LEVEL')).to.be.equal('6');
});
});
});

View File

@@ -1,6 +1,4 @@
import dotenv from 'dotenv';
import { injectable } from 'inversify';
import path from 'path';
import Config from '../types/Config';
import Environment from '../types/Environment';
@@ -17,22 +15,16 @@ export default class ConfigService {
private config: Config;
public get isProd(): boolean {
return this.get('ENVIRONMENT') === 'production';
return this.get('NODE_ENV') === 'production';
}
constructor() {
dotenv.config({ path: path.resolve(__dirname, '../../', '.env') });
this.setup();
}
private setup(): void {
this.config = {
BOT_TRIGGER: process.env.BOT_TRIGGER || '!',
DISCORD_BOT_TOKEN: process.env.DISCORD_BOT_TOKEN,
MONGODB_URI: process.env.MONGODB_URI || '',
MONGODB_DB_NAME: process.env.MONGODB_DB_NAME || '',
ENVIRONMENT: (process.env.NODE_ENV as Environment) || 'development',
NODE_ENV: (process.env.NODE_ENV as Environment) || 'development',
LOG_LEVEL: (process.env.LOG_LEVEL as LogLevel) || 'info',
};
}

View File

@@ -6,6 +6,6 @@ export default interface Config {
DISCORD_BOT_TOKEN: string;
MONGODB_URI: string;
MONGODB_DB_NAME: string;
ENVIRONMENT: Environment;
NODE_ENV: Environment;
LOG_LEVEL: LogLevel;
}

View File

@@ -1,4 +1,7 @@
import dotenv from 'dotenv';
import { exit } from 'process';
import path from 'path';
import 'reflect-metadata';
import App from './app';
@@ -12,6 +15,8 @@ function exitHandler(): void {
}
try {
dotenv.config({ path: path.resolve(__dirname, './', '.env') });
app.init();
// do something when app is closing

View File

@@ -1,10 +1,7 @@
{
"compilerOptions": {
"target": "es6",
"lib": [
"es6",
"dom"
],
"lib": ["es6", "dom"],
"module": "commonjs",
"declaration": true,
"noImplicitAny": false,
@@ -18,11 +15,7 @@
"baseUrl": "./",
"resolveJsonModule": true,
"esModuleInterop": true,
"types": [
"reflect-metadata"
]
"types": ["reflect-metadata", "node"]
},
"exclude": [
"node_modules"
]
}
"exclude": ["node_modules"]
}

1020
yarn.lock

File diff suppressed because it is too large Load Diff