mirror of
https://github.com/zombieFox/nightTab.git
synced 2024-11-21 23:53:10 +01:00
add feat/test runner
This commit is contained in:
parent
8e568c95c4
commit
d5ee42459e
21
.eslintrc.js
21
.eslintrc.js
@ -6,12 +6,12 @@ module.exports = {
|
||||
"webextensions": true
|
||||
},
|
||||
'extends': 'eslint:recommended',
|
||||
'ignorePatterns': ['webpack.*', 'dist/'],
|
||||
'ignorePatterns': ['webpack.*', 'dist/', 'babel.*'],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 12,
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'plugins': ['unused-imports'],
|
||||
'plugins': ['unused-imports', 'jest'],
|
||||
'rules': {
|
||||
'indent': ['error', 2, { 'SwitchCase': 1 }],
|
||||
'linebreak-style': [
|
||||
@ -32,5 +32,20 @@ module.exports = {
|
||||
'warn',
|
||||
{ 'vars': 'all', 'varsIgnorePattern': '^_', 'args': 'after-used', 'argsIgnorePattern': '^_' }
|
||||
]
|
||||
}
|
||||
},
|
||||
// This lints typescript files differently than js files.
|
||||
'overrides': [{
|
||||
'files': ["**/*.ts"],
|
||||
'extends': [
|
||||
'eslint:recommended', "plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
'parser': '@typescript-eslint/parser',
|
||||
'plugins': ['@typescript-eslint', 'unused-imports', 'jest'],
|
||||
'rules': {
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
// Good in theory, but seems to disables implict function return types
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
},
|
||||
}]
|
||||
}
|
||||
|
10
.github/workflows/gh-pages-deploy.yml
vendored
10
.github/workflows/gh-pages-deploy.yml
vendored
@ -13,10 +13,14 @@ jobs:
|
||||
uses: actions/checkout@v2.3.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install and Build
|
||||
run: |
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12' # TODO IDK what version nighttab is built with normally.
|
||||
cache: 'npm'
|
||||
- run: |
|
||||
npm install
|
||||
npm run lint
|
||||
npm test
|
||||
npm run build
|
||||
|
||||
- name: Deploy
|
||||
|
19
.github/workflows/pull-request-tests.yml
vendored
Normal file
19
.github/workflows/pull-request-tests.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Run tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependecies & Test
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12' # TODO IDK what version nighttab is built with normally.
|
||||
cache: 'npm'
|
||||
- run: |
|
||||
npm install
|
||||
npm run lint
|
||||
npm test
|
6
babel.config.js
Normal file
6
babel.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@babel/preset-env', { targets: { node: 'current' } }],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
};
|
8926
package-lock.json
generated
8926
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -6,8 +6,9 @@
|
||||
"scripts": {
|
||||
"start": "webpack serve --open --config webpack.dev.js",
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"lint": "eslint --fix --ext .js .",
|
||||
"prepare": "husky install"
|
||||
"lint": "eslint --fix --ext .js,.ts,.test.ts .",
|
||||
"prepare": "husky install",
|
||||
"test": "jest tests/*"
|
||||
},
|
||||
"keywords": [
|
||||
"startpage",
|
||||
@ -28,18 +29,27 @@
|
||||
},
|
||||
"homepage": "https://github.com/zombieFox/nightTab#readme",
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"css-loader": "^6.3.0",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-jest": "^24.5.2",
|
||||
"eslint-plugin-unused-imports": "^1.1.5",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"husky": "^7.0.2",
|
||||
"jest": "^27.2.4",
|
||||
"mini-css-extract-plugin": "^2.2.0",
|
||||
"moment": "^2.29.1",
|
||||
"sortablejs": "^1.13.0",
|
||||
"style-loader": "^3.3.0",
|
||||
"terser-webpack-plugin": "^5.1.4",
|
||||
"ts-loader": "^9.2.6",
|
||||
"typescript": "^4.4.3",
|
||||
"webfontloader": "^1.6.28",
|
||||
"webpack": "^5.37.1",
|
||||
"webpack-cli": "^4.7.0",
|
||||
@ -47,4 +57,4 @@
|
||||
"webpack-merge": "^5.8.0",
|
||||
"zip-webpack-plugin": "^4.0.1"
|
||||
}
|
||||
}
|
||||
}
|
1
tests/index.ts
Normal file
1
tests/index.ts
Normal file
@ -0,0 +1 @@
|
||||
// This file is strictly for utility functions used to bootstrap tests
|
30
tests/readme.md
Normal file
30
tests/readme.md
Normal file
@ -0,0 +1,30 @@
|
||||
# NightTab Testing Principles
|
||||
|
||||
Testing in NightTab consists primarily of unit tests covering functions that manipulate data in some way. Incorrect data transformations/corruption are the most likely thing to break nighttab and thus our focus is on that.
|
||||
|
||||
UI testing can get very tedious and so with nighttab we're manually testing visual aspects. Though we're open to discussing adding some regression testing.
|
||||
|
||||
All testing must meet the following criteria:
|
||||
- **Tests Should Be Fast**
|
||||
- **Tests Should Be Deterministic**
|
||||
- **Tests Should Be Simple**
|
||||
- Test Shouldn’t Duplicate Implementation Logic
|
||||
- Tests Should Be Readable
|
||||
- Tests should be integrated into the development and CI process
|
||||
- Tests should never be coupled with implementation details
|
||||
- Mocks should be avoided, when possible.
|
||||
|
||||
## Typescript
|
||||
We're using typescript for testing for two reasons:
|
||||
1. Benefit of types inside tests
|
||||
2. New Data Layer will be written in typescript for more robustness.
|
||||
|
||||
Linting for typescript is done via file overrides in the `.eslintrc.js` file, meaning js files are linted using their own configuration and then typescript files are linted with a few extra settings on top of those for js.
|
||||
|
||||
## Status of Testing
|
||||
Nighttab started without any automated testing. As the app grows in popularity with over 100k+ users and multiple contributors working on it, we've decided its time to integrate testing. We're slowly adding testing to make the app more robust and easier to improve and refactor.
|
||||
|
||||
## File Structure
|
||||
Tests file should have the `*.test.ts` extension and be placed under `/test/<path>` where path is a direct mirror of its location in `/src/<path>`.
|
||||
|
||||
The file `/test/index.ts` is not a test file and is instead meant to be used to keep utility functions used across tests.
|
49
tests/utility/get.test.ts
Normal file
49
tests/utility/get.test.ts
Normal file
@ -0,0 +1,49 @@
|
||||
//@ts-nocheck get has no types, for now
|
||||
import { get } from '../../src/utility/get';
|
||||
|
||||
let object;
|
||||
|
||||
beforeEach(() => {
|
||||
object = { theme: { accent: { rgb: { r: 'hello there' } } } };
|
||||
});
|
||||
|
||||
/**
|
||||
* For some reason get mutates the object its trying to access,
|
||||
* might be a good idea to remove this "feature".
|
||||
*/
|
||||
describe('get.js', () => {
|
||||
test('should get receive both props', () => {
|
||||
const path = 'theme.accent.rgb.r';
|
||||
const result = 'hello there';
|
||||
|
||||
expect(get({ object, path })).toBe(result);
|
||||
});
|
||||
|
||||
test('should get receive an incorrect path', () => {
|
||||
const path = 'theme.accent.rgb.d';
|
||||
|
||||
expect(get({ object, path })).toBe('');
|
||||
expect(object.theme.accent.rgb.d).toStrictEqual(undefined);
|
||||
});
|
||||
|
||||
test('should get receive an incorrect intermediate path', () => {
|
||||
const path = 'theme.accent.wrong.r';
|
||||
|
||||
expect(get({ object, path })).toBe('');
|
||||
expect(object.theme.accent.wrong).toStrictEqual({});
|
||||
});
|
||||
|
||||
test('should get receive an incorrect intermediate path as a number', () => {
|
||||
const path = 'theme.accent.1.r';
|
||||
|
||||
expect(get({ object, path })).toBe('');
|
||||
expect(object.theme.accent[1]).toStrictEqual([]);
|
||||
});
|
||||
|
||||
test('should get receive an null prop', () => {
|
||||
const path = 'theme.accent.rgb.r';
|
||||
|
||||
expect(get({ object, path: null })).toBe(false);
|
||||
expect(get({ object: null, path })).toBe(false);
|
||||
});
|
||||
});
|
7
tests/utility/makePath.test.ts
Normal file
7
tests/utility/makePath.test.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { makePath } from '../../src/utility/makePath';
|
||||
|
||||
test('makePath correctly parses the dot notation', () => {
|
||||
const input = 'theme.accent.rgb.r';
|
||||
const result = ['theme', 'accent', 'rgb', 'r'];
|
||||
expect(makePath(input)).toStrictEqual(result);
|
||||
});
|
50
tests/utility/set.test.ts
Normal file
50
tests/utility/set.test.ts
Normal file
@ -0,0 +1,50 @@
|
||||
//@ts-nocheck
|
||||
import { set } from '../../src/utility/set';
|
||||
|
||||
let object;
|
||||
|
||||
beforeEach(() => {
|
||||
object = { theme: { accent: { rgb: { r: 'Hello there!' } } } };
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* It seems, unlike get, set doesn't mutate objects when it doesn't find keys
|
||||
*/
|
||||
describe('set.js', () => {
|
||||
test('should set update an object as expected', () => {
|
||||
const path = 'theme.accent.rgb.r';
|
||||
const result = 'General Kenobi!';
|
||||
|
||||
set({ object, path, value: 'General Kenobi!' });
|
||||
expect(object.theme.accent.rgb.r).toBe(result);
|
||||
});
|
||||
|
||||
test('should get receive an incorrect path', () => {
|
||||
const path = 'theme.accent.rgb.d';
|
||||
|
||||
expect(set({ object, path })).toBe(false);
|
||||
expect(object.theme.accent.rgb.d).toBe(undefined);
|
||||
});
|
||||
|
||||
test('should get receive an incorrect intermediate path', () => {
|
||||
const path = 'theme.accent.wrong.r';
|
||||
|
||||
expect(set({ object, path })).toBe(false);
|
||||
expect(object.theme.accent.wrong).toStrictEqual(undefined);
|
||||
});
|
||||
|
||||
test('should get receive an incorrect path including a number', () => {
|
||||
const path = 'theme.accent.1.r';
|
||||
|
||||
expect(set({ object, path })).toBe(false);
|
||||
expect(object.theme.accent[1]).toBe(undefined);
|
||||
});
|
||||
|
||||
test('should get receive an null prop', () => {
|
||||
const path = 'theme.accent.rgb.r';
|
||||
|
||||
expect(set({ object, path: null })).toBe(false);
|
||||
expect(set({ object: null, path })).toBe(false);
|
||||
});
|
||||
});
|
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"target": "es5",
|
||||
/* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"lib": [
|
||||
"DOM"
|
||||
],
|
||||
/* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
"allowJs": true,
|
||||
/* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"allowSyntheticDefaultImports": true,
|
||||
/* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
||||
"esModuleInterop": true,
|
||||
/* Ensure that casing is correct in imports. */
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
/* Enable importing .json files */
|
||||
"resolveJsonModule": true,
|
||||
/* Skip type checking all .d.ts files. */
|
||||
"skipLibCheck": true,
|
||||
/* Enable all strict type-checking options. */
|
||||
"strict": true,
|
||||
/* Specify an output folder for all emitted files. */
|
||||
"outDir": "./dist/",
|
||||
/* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
"noImplicitAny": true,
|
||||
}
|
||||
}
|
@ -12,22 +12,34 @@ module.exports = {
|
||||
clean: true
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
}, {
|
||||
test: /\.(ttf|woff|woff2)$/,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'font/[name][ext]',
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|woff|woff2)$/,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'font/[name][ext]',
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'image/[name][ext]',
|
||||
}
|
||||
}
|
||||
}, {
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'image/[name][ext]',
|
||||
}
|
||||
}]
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js'],
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
|
Loading…
Reference in New Issue
Block a user