feat: replace jest with vitest

This commit is contained in:
DIYgod 2024-03-03 18:57:07 +08:00
parent 3bfdf9427c
commit 38e42156a0
No known key found for this signature in database
38 changed files with 673 additions and 1621 deletions

View File

@ -18,7 +18,7 @@ permissions:
contents: read
jobs:
jest:
vitest:
runs-on: ubuntu-latest
timeout-minutes: 10
services:
@ -31,7 +31,7 @@ jobs:
fail-fast: false
matrix:
node-version: [ 18, 20, 21 ]
name: Jest on Node ${{ matrix.node-version }}
name: Vitest on Node ${{ matrix.node-version }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
@ -46,7 +46,7 @@ jobs:
- name: Run postinstall script for dependencies
run: pnpm rb
- name: Test all and generate coverage
run: pnpm run jest:coverage
run: pnpm run vitest:coverage
env:
REDIS_URL: redis://localhost:${{ job.services.redis.ports[6379] }}/
- name: Upload coverage to Codecov
@ -72,7 +72,7 @@ jobs:
- name: Chrome from Google
dependency: google-chrome-stable
environment: '{ "CHROMIUM_EXECUTABLE_PATH": "google-chrome-stable" }'
name: Jest puppeteer on Node ${{ matrix.node-version }} with ${{ matrix.chromium.name }}
name: Vitest puppeteer on Node ${{ matrix.node-version }} with ${{ matrix.chromium.name }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
@ -101,7 +101,7 @@ jobs:
sudo apt-get update
sudo apt-get install -yq --no-install-recommends ${{ matrix.chromium.dependency }}
- name: Test puppeteer
run: pnpm run jest puppeteer
run: pnpm run vitest puppeteer
env: ${{ fromJSON(matrix.chromium.environment) }}
docs:
@ -153,7 +153,7 @@ jobs:
automerge:
if: github.triggering_actor == 'dependabot[bot]' && github.event_name == 'pull_request'
needs: [ jest, puppeteer, docs, all ]
needs: [ vitest, puppeteer, docs, all ]
runs-on: ubuntu-latest
permissions:
pull-requests: write

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import app from '@/app';

View File

@ -1,8 +1,8 @@
import { describe, expect, it, afterEach, jest } from '@jest/globals';
import { describe, expect, it, afterEach, vi } from 'vitest';
import nock from 'nock';
afterEach(() => {
jest.resetModules();
vi.resetModules();
});
describe('config', () => {

View File

@ -633,16 +633,18 @@ calculateValue();
if (envs.REMOTE_CONFIG) {
got.get(envs.REMOTE_CONFIG)
.then((response) => {
.then(async (response) => {
const data = JSON.parse(response.body);
if (data) {
envs = Object.assign(envs, data);
calculateValue();
require('@/utils/logger').default.info('Remote config loaded.');
const { default: logger } = await import('@/utils/logger');
logger.info('Remote config loaded.');
}
})
.catch((error) => {
require('@/utils/logger').default.error('Remote config load failed.', error);
.catch(async (error) => {
const { default: logger } = await import('@/utils/logger');
logger.error('Remote config load failed.', error);
});
}

View File

@ -1,4 +1,4 @@
import { describe, expect, it, afterAll } from '@jest/globals';
import { describe, expect, it, afterAll } from 'vitest';
import supertest from 'supertest';
import server from '@/index';
import { load } from 'cheerio';

View File

@ -1,4 +1,4 @@
import { describe, expect, it, jest, afterEach } from '@jest/globals';
import { describe, expect, it, vi, afterEach } from 'vitest';
import md5 from '@/utils/md5';
process.env.NODE_NAME = 'mock';
@ -10,7 +10,7 @@ async function checkBlock(response) {
afterEach(() => {
delete process.env.ACCESS_KEY;
jest.resetModules();
vi.resetModules();
});
describe('access-control', () => {

View File

@ -1,8 +1,7 @@
import { describe, expect, it, jest, afterEach, afterAll } from '@jest/globals';
import { describe, expect, it, vi, afterEach, afterAll } from 'vitest';
import Parser from 'rss-parser';
const parser = new Parser();
jest.setTimeout(50000);
afterAll(() => {
delete process.env.HOTLINK_TEMPLATE;
@ -16,7 +15,7 @@ afterEach(() => {
delete process.env.HOTLINK_INCLUDE_PATHS;
delete process.env.HOTLINK_EXCLUDE_PATHS;
delete process.env.ALLOW_USER_HOTLINK_TEMPLATE;
jest.resetModules();
vi.resetModules();
});
const expects = {

View File

@ -1,4 +1,4 @@
import { describe, expect, it, jest, afterEach, afterAll, beforeAll } from '@jest/globals';
import { describe, expect, it, vi, afterEach, afterAll, beforeAll } from 'vitest';
import Parser from 'rss-parser';
import wait from '@/utils/wait';
@ -11,7 +11,7 @@ beforeAll(() => {
afterEach(() => {
delete process.env.CACHE_TYPE;
jest.resetModules();
vi.resetModules();
});
afterAll(() => {

View File

@ -1,16 +1,9 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import app from '@/app';
import { load } from 'cheerio';
process.env.NODE_NAME = 'mock';
let gitHash;
try {
gitHash = require('git-rev-sync').short();
} catch {
gitHash = (process.env.HEROKU_SLUG_COMMIT && process.env.HEROKU_SLUG_COMMIT.slice(0, 7)) || 'unknown';
}
describe('debug', () => {
it('debug', async () => {
const response1 = await app.request('/test/1');
@ -34,9 +27,6 @@ describe('debug', () => {
case 'Node Name:':
expect(value).toBe('mock');
break;
case 'Git Hash:':
expect(value).toBe(gitHash);
break;
case 'Request Amount:':
expect(value).toBe('6');
break;

View File

@ -1,6 +1,4 @@
import { describe, expect, it, afterAll, jest, afterEach } from '@jest/globals';
jest.setTimeout(50000);
import { describe, expect, it, afterAll, vi, afterEach } from 'vitest';
afterAll(() => {
delete process.env.FILTER_REGEX_ENGINE;
@ -8,7 +6,7 @@ afterAll(() => {
afterEach(() => {
delete process.env.FILTER_REGEX_ENGINE;
jest.resetModules();
vi.resetModules();
});
describe('filter-engine', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it, afterAll } from '@jest/globals';
import { describe, expect, it, afterAll } from 'vitest';
process.env.NODE_NAME = 'mock';
process.env.ALLOW_ORIGIN = 'rsshub.mock';

View File

@ -1,4 +1,4 @@
import { describe, expect, it, jest } from '@jest/globals';
import { describe, expect, it, vi } from 'vitest';
import app from '@/app';
import Parser from 'rss-parser';
import { config } from '@/config';
@ -423,7 +423,7 @@ describe('multi parameter', () => {
describe('openai', () => {
it(`chatgpt`, async () => {
jest.resetModules();
vi.resetModules();
process.env.OPENAI_API_KEY = 'sk-1234567890';
const app = (await import('@/app')).default;

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import app from '@/app';
import Parser from 'rss-parser';

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import { init, request } from './pkg';
describe('pkg', () => {
@ -10,64 +10,59 @@ describe('pkg', () => {
expect(config.ua).toBe('mock');
});
it('request', (done) => {
request('/test/1').then((data) => {
expect(data).toMatchObject({
atomlink: 'http://localhost/test/1',
title: 'Test 1',
itunes_author: null,
link: 'https://github.com/DIYgod/RSSHub',
item: [
{
title: 'Title1',
description: 'Description1',
pubDate: 'Mon, 31 Dec 2018 15:59:50 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/1',
author: 'DIYgod1',
},
{
title: 'Title2',
description: 'Description2',
pubDate: 'Mon, 31 Dec 2018 15:59:40 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/2',
author: 'DIYgod2',
},
{
title: 'Title3',
description: 'Description3',
pubDate: 'Mon, 31 Dec 2018 15:59:30 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/3',
author: 'DIYgod3',
},
{
title: 'Title4',
description: 'Description4',
pubDate: 'Mon, 31 Dec 2018 15:59:20 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/4',
author: 'DIYgod4',
},
{
title: 'Title5',
description: 'Description5',
pubDate: 'Mon, 31 Dec 2018 15:59:10 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/5',
author: 'DIYgod5',
},
],
allowEmpty: false,
});
done();
it('request', async () => {
const data = await request('/test/1');
expect(data).toMatchObject({
atomlink: 'http://localhost/test/1',
title: 'Test 1',
itunes_author: null,
link: 'https://github.com/DIYgod/RSSHub',
item: [
{
title: 'Title1',
description: 'Description1',
pubDate: 'Mon, 31 Dec 2018 15:59:50 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/1',
author: 'DIYgod1',
},
{
title: 'Title2',
description: 'Description2',
pubDate: 'Mon, 31 Dec 2018 15:59:40 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/2',
author: 'DIYgod2',
},
{
title: 'Title3',
description: 'Description3',
pubDate: 'Mon, 31 Dec 2018 15:59:30 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/3',
author: 'DIYgod3',
},
{
title: 'Title4',
description: 'Description4',
pubDate: 'Mon, 31 Dec 2018 15:59:20 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/4',
author: 'DIYgod4',
},
{
title: 'Title5',
description: 'Description5',
pubDate: 'Mon, 31 Dec 2018 15:59:10 GMT',
link: 'https://github.com/DIYgod/RSSHub/issues/5',
author: 'DIYgod5',
},
],
allowEmpty: false,
});
});
it('error', (done) => {
request('/test/error')
.then(() => {
done();
})
.catch((error) => {
expect(error).toBe('Error test');
done();
});
it('error', async () => {
try {
await request('/test/error');
} catch (error) {
expect(error).toBe('Error test');
}
});
});

View File

@ -1,4 +1,4 @@
import { describe, expect, it, afterAll } from '@jest/globals';
import { describe, expect, it, afterAll } from 'vitest';
process.env.SOCKET = 'socket';
import app from '@/app';

View File

@ -10,21 +10,25 @@ type Root = {
get: (routePath: string, filePath: string) => void;
};
const imports = directoryImport({
targetDirectoryPath: path.join(__dirname, './routes'),
importPattern: /router\.js$/,
});
const routes: Record<string, (root: Root) => void> = {};
for (const path in imports) {
const name = path.split('/').find(Boolean);
if (name) {
routes[name] = (
imports[path] as {
default: (root: Root) => void;
}
).default;
if (process.env.NODE_ENV === 'test') {
routes.test = (await import('./routes/test/router')).default;
} else {
const imports = directoryImport({
targetDirectoryPath: path.join(__dirname, './routes'),
importPattern: /router\.js$/,
});
for (const path in imports) {
const name = path.split('/').find(Boolean);
if (name) {
routes[name] = (
imports[path] as {
default: (root: Root) => void;
}
).default;
}
}
}

View File

@ -1,4 +1,4 @@
import { describe, expect, it, jest, afterEach, afterAll, beforeAll } from '@jest/globals';
import { describe, expect, it, vi, afterEach, afterAll, beforeAll } from 'vitest';
import wait from '@/utils/wait';
beforeAll(() => {
@ -8,7 +8,7 @@ beforeAll(() => {
afterEach(() => {
delete process.env.CACHE_TYPE;
jest.resetModules();
vi.resetModules();
});
afterAll(() => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import configUtils, { transElemText, replaceParams, getProp } from '@/utils/common-config';
import nock from 'nock';

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import { toTitleCase, collapseWhitespace, convertDateToISO8601 } from '@/utils/common-utils';
describe('common-utils', () => {

View File

@ -1,8 +1,8 @@
import { describe, expect, it, afterEach, jest } from '@jest/globals';
import { describe, expect, it, afterEach, vi } from 'vitest';
import nock from 'nock';
afterEach(() => {
jest.resetModules();
vi.resetModules();
});
describe('got', () => {
@ -22,7 +22,7 @@ describe('got', () => {
it('retry', async () => {
const { default: got } = await import('@/utils/got');
const { config } = await import('@/config');
const requestRun = jest.fn();
const requestRun = vi.fn();
nock('http://rsshub.test')
.get('/testRerty')
.times(config.requestRetry + 1)
@ -71,7 +71,7 @@ describe('got', () => {
const logger = (await import('@/utils/logger')).default;
// @ts-expect-error unused
const loggerSpy = jest.spyOn(logger, 'error').mockReturnValue({});
const loggerSpy = vi.spyOn(logger, 'error').mockReturnValue({});
try {
await got.get('http://rsshub.test/timeout');

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import { getRouteNameFromPath } from '@/utils/helpers';
describe('helpers', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import md5 from '@/utils/md5';
describe('md5', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import { parseRelativeDate } from '@/utils/parse-date';
import MockDate from 'mockdate';

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import pacProxy from '@/utils/proxy/pac-proxy';
const emptyProxyObj = {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import unifyProxy from '@/utils/proxy/unify-proxy';
const emptyProxyObj = {

View File

@ -1,4 +1,4 @@
import { describe, expect, it, jest, afterEach } from '@jest/globals';
import { describe, expect, it, vi, afterEach } from 'vitest';
import { parseCookieArray, constructCookieArray, setCookies, getCookies } from '@/utils/puppeteer-utils';
import puppeteer from '@/utils/puppeteer';
import type { Browser } from 'puppeteer';
@ -11,7 +11,7 @@ afterEach(() => {
browser = null;
}
jest.resetModules();
vi.resetModules();
});
describe('puppeteer-utils', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it, jest, afterEach } from '@jest/globals';
import { describe, expect, it, vi, afterEach } from 'vitest';
import wait from './wait';
import { type Browser } from 'puppeteer';
@ -8,7 +8,7 @@ afterEach(() => {
if (browser) {
// double insurance to close unclosed browser immediately after each test
// if a test closure fails before it can close the browser, the browser process will probably be unclosed,
// especially when the test unit is run through `npm run jest puppeteer`
// especially when the test unit is run through `npm run vitest puppeteer`
browser.close();
browser = null;
}
@ -18,7 +18,7 @@ afterEach(() => {
delete process.env.PROXY_PORT;
delete process.env.PROXY_AUTH;
jest.resetModules();
vi.resetModules();
});
describe('puppeteer', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import got from '@/utils/got';
import { config } from '@/config';
import nock from 'nock';

View File

@ -1,4 +1,4 @@
import { describe, expect, it, beforeEach, afterEach, jest } from '@jest/globals';
import { describe, expect, it, beforeEach, afterEach, vi } from 'vitest';
import got from '@/utils/got';
import parser from '@/utils/rss-parser';
@ -50,7 +50,7 @@ afterEach(() => {
http.get = httpWrap(http.get);
http.request = httpWrap(http.request);
jest.resetModules();
vi.resetModules();
});
describe('got', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import parser from '@/utils/rss-parser';
import { config } from '@/config';
import nock from 'nock';

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import timezone from '@/utils/timezone';
describe('timezone', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import { isValidHost } from '@/utils/valid-host';
describe('valid-host', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import wait from '@/utils/wait';
describe('wait', () => {

View File

@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it } from 'vitest';
import { load } from 'cheerio';
import nock from 'nock';
import { fixArticleContent, fetchArticle, finishArticleItem, normalizeUrl } from '@/utils/wechat-mp';

View File

@ -27,14 +27,14 @@
"format": "eslint --cache --fix \"**/*.{ts,js,yml}\" && node website/docs/.format/format.mjs && prettier \"**/*.{ts,js,json}\" --write",
"format:check": "eslint --cache \"**/*.{ts,js,yml}\" && prettier \"**/*.{ts,js,json}\" --check",
"format:staged": "lint-staged",
"jest": "cross-env NODE_ENV=test jest --runInBand --forceExit --detectOpenHandles",
"jest:coverage": "cross-env NODE_ENV=test jest --coverage --reporters=default --reporters=jest-junit --runInBand --forceExit --detectOpenHandles",
"jest:watch": "cross-env NODE_ENV=test jest --watch",
"vitest": "cross-env NODE_ENV=test vitest",
"vitest:coverage": "cross-env NODE_ENV=test vitest --coverage.enabled",
"vitest:watch": "cross-env NODE_ENV=test vitest --watch",
"lint": "eslint --cache .",
"prepare": "husky || true",
"profiling": "NODE_ENV=production tsx --prof lib/index.ts",
"start": "tsx lib/index.ts",
"test": "npm run format:check && npm run jest:coverage"
"test": "npm run format:check && npm run vitest:coverage"
},
"lint-staged": {
"*.js": [
@ -48,33 +48,6 @@
"*.md?(x)": "node website/docs/.format/format.mjs --staged",
"*.yml": "eslint --cache --fix"
},
"jest": {
"bail": true,
"collectCoverageFrom": [
"lib/**/*.js",
"lib/**/*.ts",
"!lib/routes/**",
"!lib/routes-deprecated/**",
"!lib/router.js"
],
"coverageReporters": [
"text-summary",
"lcov"
],
"moduleNameMapper": {
"@/(.*)": "<rootDir>/lib/$1"
},
"testEnvironment": "node",
"testMatch": [
"**/lib/**/*.test.ts"
],
"testPathIgnorePatterns": [
"/node_modules/"
]
},
"jest-junit": {
"outputDirectory": "coverage"
},
"dependencies": {
"@hono/node-server": "1.8.2",
"@notionhq/client": "2.2.14",
@ -150,7 +123,6 @@
"devDependencies": {
"@babel/preset-env": "7.24.0",
"@babel/preset-typescript": "7.23.3",
"@jest/globals": "29.7.0",
"@microsoft/eslint-formatter-sarif": "3.0.0",
"@stylistic/eslint-plugin-js": "1.6.3",
"@types/aes-js": "3.1.4",
@ -177,6 +149,7 @@
"@typescript-eslint/eslint-plugin": "7.1.0",
"@typescript-eslint/parser": "7.1.0",
"@vercel/nft": "0.26.4",
"@vitest/coverage-v8": "1.3.1",
"cross-env": "7.0.3",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
@ -187,8 +160,6 @@
"eslint-plugin-yml": "1.12.2",
"fs-extra": "11.2.0",
"husky": "9.0.11",
"jest": "29.7.0",
"jest-junit": "16.0.0",
"lint-staged": "15.2.2",
"mockdate": "3.0.5",
"nock": "13.5.4",
@ -207,17 +178,14 @@
"supertest": "6.3.4",
"to-vfile": "8.0.0",
"tosource": "2.0.0-alpha.3",
"ts-jest": "29.1.2",
"typescript": "5.3.3",
"unified": "9.2.2",
"unist-util-visit": "4.1.2",
"unist-util-visit-parents": "5.1.3"
"unist-util-visit-parents": "5.1.3",
"vite-tsconfig-paths": "4.3.1",
"vitest": "1.3.1"
},
"engines": {
"node": ">=16"
},
"reporters": [
"default",
"jest-junit"
]
"node": ">=18"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
env:
jest: true

15
vitest.config.ts Normal file
View File

@ -0,0 +1,15 @@
import { defineConfig } from 'vitest/config';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [tsconfigPaths()],
test: {
watch: false,
coverage: {
include: ['lib/**/*.ts'],
exclude: ['lib/routes/**', 'lib/routes-deprecated/**'],
reporter: ['junit'],
},
testTimeout: 10000,
},
});