diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2024-02-08 06:52:06 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-08 11:52:06 +0000 | 
| commit | d4381831209dfbbbddd6d238c68461c9601573e2 (patch) | |
| tree | 87839d80f838464ff92ebc7fa652029b9329cc96 /test/eslint-config.test.js | |
| parent | 725a90dd6570044a3df6631051aaab8b026ca6c2 (diff) | |
Update eslint (#638)
* Add json test
* Update vscode settings to better handle json
* Collapse eslint rules for easier readability
* Reorganize
* Update no-multi-spaces rule for JSON
* Rules updates
* Switch to @stylistic/eslint-plugin
* Update deprecated stylistic rules
* Group stylistic rules
* Simplify rules
* Move eslint env overrides to end of file
* Add test
* Move promiseAnimationFrame to separate file
* Remove unneeded eslint disable
* Remove unneeded
Diffstat (limited to 'test/eslint-config.test.js')
| -rw-r--r-- | test/eslint-config.test.js | 172 | 
1 files changed, 172 insertions, 0 deletions
| diff --git a/test/eslint-config.test.js b/test/eslint-config.test.js new file mode 100644 index 00000000..bddde695 --- /dev/null +++ b/test/eslint-config.test.js @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2024  Yomitan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +import esbuild from 'esbuild'; +import {readFileSync} from 'fs'; +import {dirname, join} from 'path'; +import {fileURLToPath} from 'url'; +import {describe, test} from 'vitest'; +import {parseJson} from '../dev/json.js'; + +const rootDir = join(dirname(fileURLToPath(import.meta.url)), '..'); + +/** + * @param {string[]} scriptPaths + * @returns {Promise<string[]>} + */ +async function getDependencies(scriptPaths) { +    const v = await esbuild.build({ +        entryPoints: scriptPaths, +        bundle: true, +        minify: false, +        sourcemap: true, +        target: 'es2022', +        format: 'esm', +        write: false, +        metafile: true +    }); +    const dependencies = Object.keys(v.metafile.inputs); +    const stringComparer = new Intl.Collator('en-US'); // Invariant locale +    dependencies.sort((a, b) => stringComparer.compare(a, b)); +    return dependencies; +} + +/** + * @param {string[]} dependencies + * @returns {string[]} + */ +function removeLibraryDependencies(dependencies) { +    const pattern = /^ext\/lib\//; +    return dependencies.filter((v) => !pattern.test(v)); +} + +/** + * @param {{[key: string]: boolean}|undefined} env1 + * @param {{[key: string]: boolean}} env2 + * @returns {boolean} + */ +function envMatches(env1, env2) { +    if (typeof env1 !== 'object' || env1 === null) { return false; } +    const map1 = new Map(Object.entries(env1)); +    const map2 = new Map(Object.entries(env2)); +    if (map1.size !== map2.size) { return false; } +    for (const [k1, v1] of map1) { +        if (map2.get(k1) !== v1) { return false; } +    } +    return true; +} + +/** + * @param {string[]} files1 + * @param {string[]} files2 + * @returns {boolean} + */ +function filesMatch(files1, files2) { +    if (!Array.isArray(files1)) { return false; } +    const set1 = new Set(files1); +    const set2 = new Set(files2); +    if (set1.size !== set2.size) { return false; } +    for (const v of set1) { +        if (!set2.has(v)) { return false; } +    } +    return true; +} + +const targets = [ +    { +        name: 'sandbox', +        paths: [ +            'ext/js/templates/sandbox/template-renderer-frame-main.js' +        ], +        /** @type {{[key: string]: boolean}} */ +        env: { +            webextensions: false +        } +    }, +    { +        name: 'worker', +        paths: [ +            'ext/js/dictionary/dictionary-worker-main.js' +        ], +        /** @type {{[key: string]: boolean}} */ +        env: { +            browser: false, +            worker: true +        } +    }, +    { +        name: 'serviceworker', +        paths: [ +            'ext/js/background/background-main.js' +        ], +        /** @type {{[key: string]: boolean}} */ +        env: { +            browser: false, +            serviceworker: true +        } +    } +]; + +describe('Eslint configuration', () => { +    const eslintConfigPath = '.eslintrc.json'; +    /** @type {import('core').SafeAny} */ +    const eslintConfig = parseJson(readFileSync(join(rootDir, eslintConfigPath), 'utf8')); +    describe.each(targets)('Environment is $name', ({name, paths, env}) => { +        test('Entry exists', async ({expect}) => { +            const fullPaths = paths.map((v) => join(rootDir, v)); +            const dependencies = removeLibraryDependencies(await getDependencies(fullPaths)); + +            let okay = false; +            const candidates = []; +            const {overrides} = eslintConfig; +            for (let i = 0, ii = overrides.length; i < ii; ++i) { +                const override = overrides[i]; +                if (!envMatches(override.env, env)) { continue; } +                const {files} = override; +                if (!Array.isArray(files)) { continue; } +                candidates.push(i); +                if (filesMatch(files, dependencies)) { +                    okay = true; +                    break; +                } +            } + +            if (okay) { return; } +            switch (candidates.length) { +                case 0: +                    { +                        const message = `No override found with "${name}" environment: ${JSON.stringify(env)}`; +                        expect(false, message).toStrictEqual(true); +                    } +                    break; +                case 1: +                    { +                        const index = candidates[0]; +                        const message = `Override at index ${index} does not have the expected files for the "${name}" environment.`; +                        expect(overrides[index].files, message).toStrictEqual(dependencies); +                    } +                    break; +                default: +                    { +                        const message = `No override found with the correct file list for the "${name}" environment; candidate indices: [${candidates.join(', ')}].`; +                        expect([], message).toStrictEqual(dependencies); +                    } +                    break; +            } +        }); +    }); +}); |