Add frontend testing, require node 12 (#15315)

- Add basic frontend unit testing infrastructure using jest in ESM mode
- Rename 'make test' to 'make test-backend'
- Introduce 'make test-frontend' and 'make test' that runs both
- Bump Node.js requirement to v12. v10 will be EOL in less than a month.
- Convert all build-related JS files to ESM.

I opted to run frontend tests run as part of the compliance pipeline because
they complete fast and are not platform-specific like the golang tests.
release/v1.15
silverwind 2021-04-08 12:41:57 +02:00 committed by GitHub
parent 4eea819b24
commit 0d1a5e0ffc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 5151 additions and 35 deletions

View File

@ -70,11 +70,17 @@ steps:
- make checks-backend - make checks-backend
depends_on: [lint-backend] depends_on: [lint-backend]
- name: test-frontend
image: node:14
commands:
- make test-frontend
depends_on: [lint-frontend]
- name: build-frontend - name: build-frontend
image: node:14 image: node:14
commands: commands:
- make frontend - make frontend
depends_on: [lint-frontend] depends_on: [test-frontend]
- name: build-backend-no-gcc - name: build-backend-no-gcc
pull: always pull: always

View File

@ -52,6 +52,12 @@ overrides:
rules: rules:
import/no-unresolved: [0] import/no-unresolved: [0]
import/no-extraneous-dependencies: [0] import/no-extraneous-dependencies: [0]
- files: ["*.test.js"]
env:
jest: true
- files: ["*.config.js"]
rules:
import/no-unused-modules: [0]
rules: rules:
accessor-pairs: [2] accessor-pairs: [2]

View File

@ -28,7 +28,7 @@ COMMA := ,
XGO_VERSION := go-1.16.x XGO_VERSION := go-1.16.x
MIN_GO_VERSION := 001014000 MIN_GO_VERSION := 001014000
MIN_NODE_VERSION := 010013000 MIN_NODE_VERSION := 012017000
DOCKER_IMAGE ?= gitea/gitea DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest DOCKER_TAG ?= latest
@ -173,6 +173,9 @@ help:
@echo " - checks run various consistency checks" @echo " - checks run various consistency checks"
@echo " - checks-frontend check frontend files" @echo " - checks-frontend check frontend files"
@echo " - checks-backend check backend files" @echo " - checks-backend check backend files"
@echo " - test test everything"
@echo " - test-frontend test frontend files"
@echo " - test-backend test backend files"
@echo " - webpack build webpack files" @echo " - webpack build webpack files"
@echo " - svg build svg files" @echo " - svg build svg files"
@echo " - fomantic build fomantic files" @echo " - fomantic build fomantic files"
@ -322,7 +325,7 @@ lint: lint-frontend lint-backend
.PHONY: lint-frontend .PHONY: lint-frontend
lint-frontend: node_modules lint-frontend: node_modules
npx eslint --color --max-warnings=0 web_src/js build templates webpack.config.js npx eslint --color --max-warnings=0 web_src/js build templates *.config.js
npx stylelint --color --max-warnings=0 web_src/less npx stylelint --color --max-warnings=0 web_src/less
.PHONY: lint-backend .PHONY: lint-backend
@ -345,16 +348,23 @@ watch-backend: go-check
air -c .air.conf air -c .air.conf
.PHONY: test .PHONY: test
test: test: test-frontend test-backend
.PHONY: test-backend
test-backend:
@echo "Running go test with -tags '$(TEST_TAGS)'..." @echo "Running go test with -tags '$(TEST_TAGS)'..."
@$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' $(GO_PACKAGES) @$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' $(GO_PACKAGES)
.PHONY: test-frontend
test-frontend:
@NODE_OPTIONS="--experimental-vm-modules --no-warnings" npx jest --color
.PHONY: test-check .PHONY: test-check
test-check: test-check:
@echo "Running test-check..."; @echo "Running test-check...";
@diff=$$(git status -s); \ @diff=$$(git status -s); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "make test has changed files in the source tree:"; \ echo "make test-backend has changed files in the source tree:"; \
echo "$${diff}"; \ echo "$${diff}"; \
echo "You should change the tests to create these files in a temporary directory."; \ echo "You should change the tests to create these files in a temporary directory."; \
echo "Do not simply add these files to .gitignore"; \ echo "Do not simply add these files to .gitignore"; \

View File

@ -77,7 +77,7 @@ or if sqlite support is required:
The `build` target is split into two sub-targets: The `build` target is split into two sub-targets:
- `make backend` which requires [Go 1.13](https://golang.org/dl/) or greater. - `make backend` which requires [Go 1.13](https://golang.org/dl/) or greater.
- `make frontend` which requires [Node.js 10.13](https://nodejs.org/en/download/) or greater. - `make frontend` which requires [Node.js 12.17](https://nodejs.org/en/download/) or greater.
If pre-built frontend files are present it is possible to only build the backend: If pre-built frontend files are present it is possible to only build the backend:

View File

@ -1,12 +1,11 @@
#!/usr/bin/env node import imageminZopfli from 'imagemin-zopfli';
'use strict'; import {optimize, extendDefaultPlugins} from 'svgo';
import {fabric} from 'fabric';
const imageminZopfli = require('imagemin-zopfli'); import {readFile, writeFile} from 'fs/promises';
const {optimize, extendDefaultPlugins} = require('svgo'); import {resolve, dirname} from 'path';
const {fabric} = require('fabric'); import {fileURLToPath} from 'url';
const {readFile, writeFile} = require('fs').promises;
const {resolve} = require('path');
const __dirname = dirname(fileURLToPath(import.meta.url));
const logoFile = resolve(__dirname, '../assets/logo.svg'); const logoFile = resolve(__dirname, '../assets/logo.svg');
function exit(err) { function exit(err) {

View File

@ -1,11 +1,10 @@
#!/usr/bin/env node import fastGlob from 'fast-glob';
'use strict'; import {optimize, extendDefaultPlugins} from 'svgo';
import {resolve, parse, dirname} from 'path';
const fastGlob = require('fast-glob'); import {readFile, writeFile, mkdir} from 'fs/promises';
const {optimize, extendDefaultPlugins} = require('svgo'); import {fileURLToPath} from 'url';
const {resolve, parse} = require('path');
const {readFile, writeFile, mkdir} = require('fs').promises;
const __dirname = dirname(fileURLToPath(import.meta.url));
const glob = (pattern) => fastGlob.sync(pattern, {cwd: resolve(__dirname), absolute: true}); const glob = (pattern) => fastGlob.sync(pattern, {cwd: resolve(__dirname), absolute: true});
const outputDir = resolve(__dirname, '../public/img/svg'); const outputDir = resolve(__dirname, '../public/img/svg');

View File

@ -21,7 +21,7 @@ params:
version: 1.13.7 version: 1.13.7
minGoVersion: 1.14 minGoVersion: 1.14
goVersion: 1.16 goVersion: 1.16
minNodeVersion: 10.13 minNodeVersion: 12.17
outputs: outputs:
home: home:

10
jest.config.js Normal file
View File

@ -0,0 +1,10 @@
export default {
setupFilesAfterEnv: ['jest-extended'],
testTimeout: 20000,
testMatch: [
'**/web_src/**/*.test.js',
],
transform: {},
verbose: false,
};

5051
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
{ {
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"type": "module",
"engines": { "engines": {
"node": ">= 10.13.0" "node": ">= 12.17.0"
}, },
"dependencies": { "dependencies": {
"@claviska/jquery-minicolors": "2.3.5", "@claviska/jquery-minicolors": "2.3.5",
@ -50,6 +51,8 @@
"eslint-plugin-import": "2.22.1", "eslint-plugin-import": "2.22.1",
"eslint-plugin-unicorn": "29.0.0", "eslint-plugin-unicorn": "29.0.0",
"eslint-plugin-vue": "7.8.0", "eslint-plugin-vue": "7.8.0",
"jest": "26.6.3",
"jest-extended": "0.11.5",
"stylelint": "13.12.0", "stylelint": "13.12.0",
"stylelint-config-standard": "21.0.0", "stylelint-config-standard": "21.0.0",
"svgo": "2.3.0", "svgo": "2.3.0",

29
web_src/js/utils.test.js Normal file
View File

@ -0,0 +1,29 @@
import {
basename, extname, isObject, uniq, stripTags,
} from './utils.js';
test('basename', () => {
expect(basename('/path/to/file.js')).toEqual('file.js');
expect(basename('/path/to/file')).toEqual('file');
expect(basename('file.js')).toEqual('file.js');
});
test('extname', () => {
expect(extname('/path/to/file.js')).toEqual('.js');
expect(extname('/path/')).toEqual('');
expect(extname('/path')).toEqual('');
expect(extname('file.js')).toEqual('.js');
});
test('isObject', () => {
expect(isObject({})).toBeTrue();
expect(isObject([])).toBeFalse();
});
test('uniq', () => {
expect(uniq([1, 1, 1, 2])).toEqual([1, 2]);
});
test('stripTags', () => {
expect(stripTags('<a>test</a>')).toEqual('test');
});

View File

@ -1,15 +1,18 @@
const fastGlob = require('fast-glob'); import fastGlob from 'fast-glob';
const wrapAnsi = require('wrap-ansi'); import wrapAnsi from 'wrap-ansi';
const AddAssetPlugin = require('add-asset-webpack-plugin'); import AddAssetPlugin from 'add-asset-webpack-plugin';
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
const LicenseCheckerWebpackPlugin = require('license-checker-webpack-plugin'); import LicenseCheckerWebpackPlugin from 'license-checker-webpack-plugin';
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); import MiniCssExtractPlugin from 'mini-css-extract-plugin';
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
const VueLoaderPlugin = require('vue-loader/lib/plugin'); import {VueLoaderPlugin} from 'vue-loader';
const {ESBuildMinifyPlugin} = require('esbuild-loader'); import {ESBuildMinifyPlugin} from 'esbuild-loader';
const {resolve, parse} = require('path'); import {resolve, parse, dirname} from 'path';
const {SourceMapDevToolPlugin} = require('webpack'); import webpack from 'webpack';
import {fileURLToPath} from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const {SourceMapDevToolPlugin} = webpack;
const glob = (pattern) => fastGlob.sync(pattern, {cwd: __dirname, absolute: true}); const glob = (pattern) => fastGlob.sync(pattern, {cwd: __dirname, absolute: true});
const themes = {}; const themes = {};
@ -35,7 +38,7 @@ const filterCssImport = (url, ...args) => {
return true; return true;
}; };
module.exports = { export default {
mode: isProduction ? 'production' : 'development', mode: isProduction ? 'production' : 'development',
entry: { entry: {
index: [ index: [