Skip to content

Commit

Permalink
feat: migrate from Tap to Jest (#220)
Browse files Browse the repository at this point in the history
* feat: use jest to test i18next-scanner

* chore: work in progress

* test: migrating to jest

* chore: update CLI and add missing dependencies

* chore: exclude ./bin from linting

* ci: remove redundant coverage script

Co-authored-by: Cheton Wu <[email protected]>
  • Loading branch information
cheton and Cheton Wu committed Jun 19, 2022
1 parent 905bd96 commit 1d8294d
Show file tree
Hide file tree
Showing 22 changed files with 3,909 additions and 3,213 deletions.
15 changes: 15 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const path = require('path');

module.exports = {
extends: 'trendmicro',
parser: '@babel/eslint-parser',
env: {
browser: true,
node: true,
'jest/globals': true,
},
plugins: [
'@babel',
'jest',
],
};
1 change: 0 additions & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ jobs:
- run: yarn eslint
- run: yarn build
- run: yarn test
- run: yarn coverage
- uses: codecov/codecov-action@v2
with:
name: node-${{ matrix.node-version }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules
package-lock.json
yarn.lock
/.nyc_output
/lib
/output
Expand Down
34 changes: 25 additions & 9 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
module.exports = {
module.exports = (api) => {
const { env } = { ...api };
const plugins = [
];

if (typeof env === 'function' && env('test')) {
// Enable async/await for jest
plugins.push('@babel/plugin-transform-runtime');
}

return {
extends: '@trendmicro/babel-config',
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'entry',
corejs: 3,
}
]
]
[
'@babel/preset-env',
{
useBuiltIns: 'entry',
corejs: 3,
}
],
[
'@babel/preset-react',
{},
],
],
plugins,
};
};
84 changes: 43 additions & 41 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,75 @@
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const program = require('commander');
const ensureArray = require('ensure-array');
const sort = require('gulp-sort');
const vfs = require('vinyl-fs');
const scanner = require('../lib');
const scanner = require('../lib').default;
const pkg = require('../package.json');

program
.version(pkg.version)
.usage('[options] <file ...>')
.option('--config <config>', 'Path to the config file (default: i18next-scanner.config.js)', 'i18next-scanner.config.js')
.option('--output <path>', 'Path to the output directory (default: .)');
.version(pkg.version)
.usage('[options] <file ...>')
.option('--config <config>', 'Path to the config file (default: i18next-scanner.config.js)', 'i18next-scanner.config.js')
.option('--output <path>', 'Path to the output directory (default: .)');

program.on('--help', function() {
console.log('');
console.log(' Examples:');
console.log('');
console.log(' $ i18next-scanner --config i18next-scanner.config.js --output /path/to/output \'src/**/*.{js,jsx}\'');
console.log(' $ i18next-scanner --config i18next-scanner.config.js "src/**/*.{js,jsx}"');
console.log(' $ i18next-scanner "/path/to/src/app.js" "/path/to/assets/index.html"');
console.log('');
program.on('--help', () => {
console.log('');
console.log(' Examples:');
console.log('');
console.log(' $ i18next-scanner --config i18next-scanner.config.js --output /path/to/output \'src/**/*.{js,jsx}\'');
console.log(' $ i18next-scanner --config i18next-scanner.config.js "src/**/*.{js,jsx}"');
console.log(' $ i18next-scanner "/path/to/src/app.js" "/path/to/assets/index.html"');
console.log('');
});

program.parse(process.argv);

if (!program.config) {
program.help();
return;
const options = program.opts();

if (!options.config) {
program.help();
process.exit(1);
}

let config = {};
try {
config = require(path.resolve(program.config));
// eslint-disable-next-line import/no-dynamic-require
config = require(path.resolve(options.config));
} catch (err) {
console.error('i18next-scanner:', err);
return;
console.error('i18next-scanner:', err);
process.exit(1);
}

{ // Input
config.input = (program.args.length > 0) ? program.args : ensureArray(config.input);
config.input = config.input.map(function(s) {
s = s.trim();

// On Windows, arguments contain spaces must be enclosed with double quotes, not single quotes.
if (s.match(/(^'.*'$|^".*"$)/)) {
// Remove first and last character
s = s.slice(1, -1);
}
return s;
});
config.input = (program.args.length > 0) ? program.args : ensureArray(config.input);
config.input = config.input.map((s) => {
s = s.trim();

if (config.input.length === 0) {
program.help();
return;
// On Windows, arguments contain spaces must be enclosed with double quotes, not single quotes.
if (s.match(/(^'.*'$|^".*"$)/)) {
// Remove first and last character
s = s.slice(1, -1);
}
return s;
});

if (config.input.length === 0) {
program.help();
process.exit(1);
}
}

{ // Output
config.output = program.output || config.output;
config.output = options.output || config.output;

if (!config.output) {
config.output = '.';
}
if (!config.output) {
config.output = '.';
}
}

vfs.src(config.input)
.pipe(sort()) // Sort files in stream by path
.pipe(scanner(config.options, config.transform, config.flush))
.pipe(vfs.dest(config.output))
.pipe(sort()) // Sort files in stream by path
.pipe(scanner(config.options, config.transform, config.flush))
.pipe(vfs.dest(config.output));
1 change: 1 addition & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@testing-library/jest-dom';
72 changes: 54 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
"scripts": {
"prepublishOnly": "npm run eslint && npm run build && npm test",
"build": "babel ./src --out-dir ./lib",
"clean": "del lib .nyc_output",
"eslint": "eslint ./src",
"coverage": "nyc --reporter=lcov --reporter=text yarn test",
"test": "tap test/*.js --no-timeout --node-arg=--require --node-arg=@babel/register --node-arg=--require --node-arg=core-js/stable --node-arg=--require --node-arg=regenerator-runtime/runtime"
"eslint-fix": "eslint --fix ./src ./test/*.js",
"test": "jest --no-cache"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -54,7 +55,7 @@
"acorn-walk": "^8.0.0",
"chalk": "^4.1.0",
"clone-deep": "^4.0.0",
"commander": "^6.2.0",
"commander": "^9.0.0",
"deepmerge": "^4.0.0",
"ensure-array": "^1.0.0",
"eol": "^0.9.1",
Expand All @@ -69,27 +70,62 @@
"vinyl-fs": "^3.0.1"
},
"devDependencies": {
"@babel/cli": "~7.12.1",
"@babel/core": "~7.12.3",
"@babel/preset-env": "~7.12.1",
"@babel/register": "~7.12.1",
"@trendmicro/babel-config": "~1.0.0-alpha",
"babel-eslint": "~10.1.0",
"@babel/cli": "^7.17.10",
"@babel/core": "^7.18.5",
"@babel/eslint-parser": "^7.18.2",
"@babel/eslint-plugin": "^7.17.7",
"@babel/plugin-transform-runtime": "^7.18.5",
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"@babel/register": "^7.17.7",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@trendmicro/babel-config": "^1.0.2",
"codecov": "^3.8.3",
"core-js": "^3.7.0",
"eslint": "^7.13.0",
"eslint-config-trendmicro": "^1.4.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.21.5",
"core-js": "^3.23.1",
"del-cli": "^4.0.1",
"eslint": "^8.18.0",
"eslint-config-trendmicro": "^3.1.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "~26.5.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.30.0",
"gulp": "^4.0.2",
"gulp-tap": "^2.0.0",
"gulp-util": "^3.0.8",
"jest": "^28.1.1",
"jest-environment-jsdom": "^28.1.1",
"jest-extended": "^2.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-i18next": "^11.12.0",
"sha1": "^1.1.1",
"tap": "^15.0.10",
"text-table": "^0.2.0"
},
"tap": {
"check-coverage": false
"jest": {
"setupFiles": [],
"setupFilesAfterEnv": [
"<rootDir>/jest.setup.js"
],
"unmockedModulePathPatterns": [
"react"
],
"testMatch": [
"<rootDir>/test/**/?(*.)(spec|test).js?(x)"
],
"collectCoverage": true,
"collectCoverageFrom": [
"<rootDir>/src/**/*.js",
"!**/node_modules/**",
"!**/test/**"
],
"coverageReporters": [
"lcov",
"text",
"html"
],
"testPathIgnorePatterns": [],
"testTimeout": 30000,
"testEnvironment": "node"
}
}
88 changes: 44 additions & 44 deletions src/acorn-jsx-walk.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,56 @@ import { simple as walk, base } from 'acorn-walk';
import { DynamicImportKey } from 'acorn-dynamic-import';

Object.assign(base, {
FieldDefinition(node, state, callback) {
if (node.value !== null) {
callback(node.value, state);
}
},

// Workaround with `acorn` and `acorn-dynamic-import`
// https://github.com/kristoferbaxter/dynamic-walk/blob/master/workaround.js
[DynamicImportKey]: () => {},
FieldDefinition(node, state, callback) {
if (node.value !== null) {
callback(node.value, state);
}
},

// Workaround with `acorn` and `acorn-dynamic-import`
// https://github.com/kristoferbaxter/dynamic-walk/blob/master/workaround.js
[DynamicImportKey]: () => {},
});

// Extends acorn walk with JSX elements
// https://github.com/RReverser/acorn-jsx/issues/23#issuecomment-403753801
Object.assign(base, {
JSXAttribute(node, state, callback) {
if (node.value !== null) {
callback(node.value, state);
}
},

JSXElement(node, state, callback) {
node.openingElement.attributes.forEach(attribute => {
callback(attribute, state);
});
node.children.forEach(node => {
callback(node, state);
});
},

JSXEmptyExpression(node, state, callback) {
// Comments. Just ignore.
},

JSXExpressionContainer(node, state, callback) {
callback(node.expression, state);
},

JSXFragment(node, state, callback) {
node.children.forEach(node => {
callback(node, state);
});
},

JSXSpreadAttribute(node, state, callback) {
callback(node.argument, state);
},

JSXText() {}
JSXAttribute(node, state, callback) {
if (node.value !== null) {
callback(node.value, state);
}
},

JSXElement(node, state, callback) {
node.openingElement.attributes.forEach(attribute => {
callback(attribute, state);
});
node.children.forEach(node => {
callback(node, state);
});
},

JSXEmptyExpression(node, state, callback) {
// Comments. Just ignore.
},

JSXExpressionContainer(node, state, callback) {
callback(node.expression, state);
},

JSXFragment(node, state, callback) {
node.children.forEach(node => {
callback(node, state);
});
},

JSXSpreadAttribute(node, state, callback) {
callback(node.argument, state);
},

JSXText() {}
});

export default (ast, options) => {
walk(ast, { ...options });
walk(ast, { ...options });
};
Loading

0 comments on commit 1d8294d

Please sign in to comment.