- 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.tags/v1.15.0-rc1
- 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 |
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] |
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 | ||||
@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" | ||||
.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 | ||||
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"; \ |
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: | ||||
#!/usr/bin/env node | |||||
'use strict'; | |||||
const imageminZopfli = require('imagemin-zopfli'); | |||||
const {optimize, extendDefaultPlugins} = require('svgo'); | |||||
const {fabric} = require('fabric'); | |||||
const {readFile, writeFile} = require('fs').promises; | |||||
const {resolve} = require('path'); | |||||
import imageminZopfli from 'imagemin-zopfli'; | |||||
import {optimize, extendDefaultPlugins} from 'svgo'; | |||||
import {fabric} from 'fabric'; | |||||
import {readFile, writeFile} from 'fs/promises'; | |||||
import {resolve, dirname} from 'path'; | |||||
import {fileURLToPath} from 'url'; | |||||
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) { |
#!/usr/bin/env node | |||||
'use strict'; | |||||
const fastGlob = require('fast-glob'); | |||||
const {optimize, extendDefaultPlugins} = require('svgo'); | |||||
const {resolve, parse} = require('path'); | |||||
const {readFile, writeFile, mkdir} = require('fs').promises; | |||||
import fastGlob from 'fast-glob'; | |||||
import {optimize, extendDefaultPlugins} from 'svgo'; | |||||
import {resolve, parse, dirname} from 'path'; | |||||
import {readFile, writeFile, mkdir} from 'fs/promises'; | |||||
import {fileURLToPath} from 'url'; | |||||
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'); | ||||
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: |
export default { | |||||
setupFilesAfterEnv: ['jest-extended'], | |||||
testTimeout: 20000, | |||||
testMatch: [ | |||||
'**/web_src/**/*.test.js', | |||||
], | |||||
transform: {}, | |||||
verbose: false, | |||||
}; | |||||
{ | { | ||||
"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", | ||||
"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", |
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'); | |||||
}); |
const fastGlob = require('fast-glob'); | |||||
const wrapAnsi = require('wrap-ansi'); | |||||
const AddAssetPlugin = require('add-asset-webpack-plugin'); | |||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); | |||||
const LicenseCheckerWebpackPlugin = require('license-checker-webpack-plugin'); | |||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); | |||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); | |||||
const VueLoaderPlugin = require('vue-loader/lib/plugin'); | |||||
const {ESBuildMinifyPlugin} = require('esbuild-loader'); | |||||
const {resolve, parse} = require('path'); | |||||
const {SourceMapDevToolPlugin} = require('webpack'); | |||||
import fastGlob from 'fast-glob'; | |||||
import wrapAnsi from 'wrap-ansi'; | |||||
import AddAssetPlugin from 'add-asset-webpack-plugin'; | |||||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; | |||||
import LicenseCheckerWebpackPlugin from 'license-checker-webpack-plugin'; | |||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin'; | |||||
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'; | |||||
import {VueLoaderPlugin} from 'vue-loader'; | |||||
import {ESBuildMinifyPlugin} from 'esbuild-loader'; | |||||
import {resolve, parse, dirname} from 'path'; | |||||
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 = {}; | ||||
return true; | return true; | ||||
}; | }; | ||||
module.exports = { | |||||
export default { | |||||
mode: isProduction ? 'production' : 'development', | mode: isProduction ? 'production' : 'development', | ||||
entry: { | entry: { | ||||
index: [ | index: [ |