refactor: convert Jest infrastructure to TS (#6910)

This commit is contained in:
Joshua Chen 2022-03-14 08:43:51 +08:00 committed by GitHub
parent 1305977098
commit dc975fecbf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 86 deletions

View file

@ -35,13 +35,13 @@ export default {
errorOnDeprecated: true, errorOnDeprecated: true,
moduleNameMapper: { moduleNameMapper: {
// Jest can't resolve CSS or asset imports // Jest can't resolve CSS or asset imports
'^.+\\.(css|jpe?g|png|svg)$': '<rootDir>/jest/emptyModule.js', '^.+\\.(css|jpe?g|png|svg|webp)$': '<rootDir>/jest/emptyModule.ts',
// Using src instead of lib, so we always get fresh source // Using src instead of lib, so we always get fresh source
'@docusaurus/(browserContext|BrowserOnly|ComponentCreator|constants|docusaurusContext|ExecutionEnvironment|Head|Interpolate|isInternalUrl|Link|Noop|renderRoutes|router|Translate|use.*)': '@docusaurus/(browserContext|BrowserOnly|ComponentCreator|constants|docusaurusContext|ExecutionEnvironment|Head|Interpolate|isInternalUrl|Link|Noop|renderRoutes|router|Translate|use.*)':
'@docusaurus/core/src/client/exports/$1', '@docusaurus/core/src/client/exports/$1',
// Maybe point to a fixture? // Maybe point to a fixture?
'@generated/.*': '<rootDir>/jest/emptyModule.js', '@generated/.*': '<rootDir>/jest/emptyModule.ts',
// TODO use "projects" + multiple configs if we work on another theme? // TODO use "projects" + multiple configs if we work on another theme?
'@theme/(.*)': '@docusaurus/theme-classic/src/theme/$1', '@theme/(.*)': '@docusaurus/theme-classic/src/theme/$1',
'@site/(.*)': 'website/$1', '@site/(.*)': 'website/$1',
@ -50,7 +50,7 @@ export default {
'@docusaurus/plugin-content-docs/client': '@docusaurus/plugin-content-docs/client':
'@docusaurus/plugin-content-docs/src/client/index.ts', '@docusaurus/plugin-content-docs/src/client/index.ts',
}, },
snapshotSerializers: ['<rootDir>/jest/snapshotPathNormalizer.js'], snapshotSerializers: ['<rootDir>/jest/snapshotPathNormalizer.ts'],
snapshotFormat: { snapshotFormat: {
printBasicPrototype: false, printBasicPrototype: false,
}, },

View file

@ -5,4 +5,4 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
module.exports = {}; export default {};

View file

@ -9,82 +9,57 @@
// Forked from https://github.com/tribou/jest-serializer-path/blob/master/lib/index.js // Forked from https://github.com/tribou/jest-serializer-path/blob/master/lib/index.js
// Added some project-specific handlers // Added some project-specific handlers
const _ = require('lodash'); import _ from 'lodash';
const {escapePath} = require('@docusaurus/utils'); import {escapePath} from '@docusaurus/utils';
const {version} = require('@docusaurus/core/package.json'); import {version} from '@docusaurus/core/package.json';
const os = require('os'); import os from 'os';
const path = require('path'); import path from 'path';
const fs = require('fs'); import fs from 'fs';
module.exports = { export function print(
print(val, serialize) { val: unknown,
let normalizedValue = val; serialize: (val: unknown) => string,
): string {
if (_.isError(normalizedValue)) { if (val instanceof Error) {
const message = normalizePaths(normalizedValue.message); const message = normalizePaths(val.message);
const error = new Error(message); const error = new Error(message);
const allKeys = [
// Clone hidden props ...Object.getOwnPropertyNames(error),
const ownProps = Object.getOwnPropertyNames(error); ...Object.keys(val),
// eslint-disable-next-line no-restricted-syntax ] as (keyof Error)[];
for (const index in ownProps) { allKeys.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(ownProps, index)) { error[key] = normalizePaths(val[key]) as never;
const key = ownProps[index]; });
return serialize(error);
error[key] = normalizePaths(normalizedValue[key]); } else if (val && typeof val === 'object') {
} const normalizedValue = _.cloneDeep(val) as Record<string, unknown>;
}
// Clone normal props
// eslint-disable-next-line no-restricted-syntax
for (const index in normalizedValue) {
if (Object.prototype.hasOwnProperty.call(normalizedValue, index)) {
error[index] = normalizePaths(normalizedValue[index]);
}
}
normalizedValue = error;
} else if (typeof normalizedValue === 'object') {
normalizedValue = _.cloneDeep(normalizedValue);
Object.keys(normalizedValue).forEach((key) => { Object.keys(normalizedValue).forEach((key) => {
normalizedValue[key] = normalizePaths(normalizedValue[key]); normalizedValue[key] = normalizePaths(normalizedValue[key]);
}); });
} else {
normalizedValue = normalizePaths(normalizedValue);
}
return serialize(normalizedValue); return serialize(normalizedValue);
}, }
test(val) { return serialize(normalizePaths(val));
let has = false; }
if (val && typeof val === 'object') { export function test(val: unknown): boolean {
return (
(typeof val === 'object' &&
val &&
Object.keys(val).some((key) =>
shouldUpdate((val as Record<string, unknown>)[key]),
)) ||
// val.message is non-enumerable in an error // val.message is non-enumerable in an error
if (val.message && shouldUpdate(val.message)) { (val instanceof Error && shouldUpdate(val.message)) ||
has = true; shouldUpdate(val)
} );
}
Object.keys(val).forEach((key) => {
if (shouldUpdate(val[key])) {
has = true;
}
});
} else if (shouldUpdate(val)) {
has = true;
}
return has;
},
normalizePaths,
getRealPath,
};
/** /**
* Normalize paths across platforms. * Normalize paths across platforms.
* Filters must be ran on all platforms to guard against false positives * Filters must be ran on all platforms to guard against false positives
*/ */
function normalizePaths(value) { function normalizePaths<T>(value: T): T {
if (typeof value !== 'string') { if (typeof value !== 'string') {
return value; return value;
} }
@ -101,7 +76,7 @@ function normalizePaths(value) {
const homeRealRelativeToTempReal = path.relative(tempDirReal, homeDirReal); const homeRealRelativeToTempReal = path.relative(tempDirReal, homeDirReal);
const homeRealRelativeToTemp = path.relative(tempDir, homeDirReal); const homeRealRelativeToTemp = path.relative(tempDir, homeDirReal);
const runner = [ const runner: ((val: string) => string)[] = [
// Replace process.cwd with <PROJECT_ROOT> // Replace process.cwd with <PROJECT_ROOT>
(val) => val.split(cwdReal).join('<PROJECT_ROOT>'), (val) => val.split(cwdReal).join('<PROJECT_ROOT>'),
(val) => val.split(cwd).join('<PROJECT_ROOT>'), (val) => val.split(cwd).join('<PROJECT_ROOT>'),
@ -149,28 +124,23 @@ function normalizePaths(value) {
(val) => val.replace(/\\(?!")/g, '/'), (val) => val.replace(/\\(?!")/g, '/'),
]; ];
let result = value; let result = value as string;
runner.forEach((current) => { runner.forEach((current) => {
result = current(result); result = current(result);
}); });
return result; return result as T & string;
} }
function shouldUpdate(value) { function shouldUpdate(value: unknown) {
if (typeof value !== 'string') {
return false;
}
// return true if value is different from normalized value // return true if value is different from normalized value
return normalizePaths(value) !== value; return typeof value === 'string' && normalizePaths(value) !== value;
} }
function getRealPath(pathname) { function getRealPath(pathname: string) {
try { try {
// eslint-disable-next-line no-restricted-properties // eslint-disable-next-line no-restricted-properties
const realPath = fs.realpathSync(pathname); const realPath = fs.realpathSync(pathname);
return realPath; return realPath;
} catch (error) { } catch (error) {
return pathname; return pathname;

View file

@ -57,7 +57,10 @@ describe('users data', () => {
.message('') .message('')
.required(), .required(),
// The preview should be jest/emptyModule // The preview should be jest/emptyModule
preview: Joi.object({}).unknown(false).required().messages({ preview: Joi.object({default: Joi.any()})
.unknown(false)
.required()
.messages({
'object.base': 'object.base':
'The image should be hosted on Docusaurus site, and not use remote HTTP or HTTPS URLs. It must be imported with require().', 'The image should be hosted on Docusaurus site, and not use remote HTTP or HTTPS URLs. It must be imported with require().',
}), }),