diff --git a/.editorconfig b/.editorconfig index 33c5fb6..5f3846b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,18 +5,17 @@ root = true # Unix-style newlines with a newline ending every file [*] +charset = utf-8 +indent_style = space +indent_size = 2 end_of_line = lf insert_final_newline = true +trim_trailing_whitespace = false +# max_line_length = 80 [**/*.js] -charset = utf-8 +# indent_style = tab trim_trailing_whitespace = true -indent_style = tab -indent_size = 2 - -[**/*.{json,yml}] -indent_style = space -indent_size = 2 # Leave PO/MO/POT files alone [**/*.{po,mo,pot}] diff --git a/.eslintrc.js b/.eslintrc.js index 6d77097..564f84a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,38 +1,22 @@ module.exports = { - 'env': { - 'commonjs': true, - 'es2021': true, - 'node': true - }, - 'extends': 'eslint:recommended', - 'overrides': [ - { - 'env': { 'jest': true }, - 'files': ['test/**'], - 'plugins': ['jest'], - 'extends': ['plugin:jest/recommended'], - 'rules': {} - } - ], - 'parserOptions': { - 'ecmaVersion': 'latest' - }, - 'rules': { - 'indent': [ - 'error', - 'tab' - ], - 'linebreak-style': [ - 'error', - 'unix' - ], - 'quotes': [ - 'error', - 'single' - ], - 'semi': [ - 'error', - 'always' - ], - } + env: { + commonjs: true, + es2021: true, + node: true, + }, + extends: ['eslint:recommended', 'plugin:prettier/recommended'], + overrides: [ + { + env: { + jest: true, + }, + files: ['test/**'], + plugins: ['jest'], + extends: ['plugin:jest/recommended'], + rules: {}, + }, + ], + parserOptions: { + ecmaVersion: 'latest', + }, }; diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..0c03e77 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,8 @@ +module.exports = { + trailingComma: 'es5', + tabWidth: 2, + semi: true, + singleQuote: true, + jsxSingleQuote: true, + useTabs: false, +}; diff --git a/jest.config.js b/jest.config.js index 661ae8b..54cab55 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,198 +4,196 @@ */ module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, + // All imported modules in your tests should be mocked automatically + // automock: false, - // Stop running tests after `n` failures - // bail: 0, + // Stop running tests after `n` failures + // bail: 0, - // The directory where Jest should store its cached dependency information - // cacheDirectory: "C:\\Users\\phili\\AppData\\Local\\Temp\\jest", + // The directory where Jest should store its cached dependency information + // cacheDirectory: 'C:\\Users\\phili\\AppData\\Local\\Temp\\jest', - // Automatically clear mock calls, instances, contexts and results before every test - // clearMocks: false, + // Automatically clear mock calls, instances, contexts and results before every test + // clearMocks: false, - // Indicates whether the coverage information should be collected while executing the test - collectCoverage: false, + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: false, - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: undefined, + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, - // The directory where Jest should output its coverage files - // coverageDirectory: "coverage", + // The directory where Jest should output its coverage files + // coverageDirectory: 'coverage', - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "\\\\node_modules\\\\" - // ], + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // '\\\\node_modules\\\\' + // ], - // Indicates which provider should be used to instrument code for coverage - // coverageProvider: "v8", + // Indicates which provider should be used to instrument code for coverage + // coverageProvider: 'v8', - // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], - coverageReporters: [ - "lcov", - "text", - // "text-summary", - ], + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // 'json', + // 'text', + // 'lcov', + // 'clover' + // ], + coverageReporters: [ + 'lcov', + 'text', + // 'text-summary', + ], - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: undefined, + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, - // A path to a custom dependency extractor - // dependencyExtractor: undefined, + // A path to a custom dependency extractor + // dependencyExtractor: undefined, - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, - // The default configuration for fake timers - // fakeTimers: { - // "enableGlobally": false - // }, + // The default configuration for fake timers + // fakeTimers: { + // 'enableGlobally': false + // }, - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: undefined, + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: undefined, + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, - // A set of global variables that need to be available in all test environments - // globals: {}, + // A set of global variables that need to be available in all test environments + // globals: {}, - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - // maxWorkers: "50%", + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: '50%', - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // 'node_modules' + // ], - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "mjs", - // "cjs", - // "jsx", - // "ts", - // "tsx", - // "json", - // "node" - // ], + // An array of file extensions your modules use + // moduleFileExtensions: [ + // 'js', + // 'mjs', + // 'cjs', + // 'jsx', + // 'ts', + // 'tsx', + // 'json', + // 'node' + // ], - // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module - // moduleNameMapper: {}, + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], - // Activates notifications for test results - // notify: false, + // Activates notifications for test results + // notify: false, - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: 'failure-change', - // A preset that is used as a base for Jest's configuration - // preset: undefined, + // A preset that is used as a base for Jest's configuration + // preset: undefined, - // Run tests from one or more projects - // projects: undefined, + // Run tests from one or more projects + // projects: undefined, - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - reporters: ['default', 'github-actions'], + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + reporters: ['default', 'github-actions'], - // Automatically reset mock state before every test - // resetMocks: false, + // Automatically reset mock state before every test + // resetMocks: false, - // Reset the module registry before running each individual test - // resetModules: false, + // Reset the module registry before running each individual test + // resetModules: false, - // A path to a custom resolver - // resolver: undefined, + // A path to a custom resolver + // resolver: undefined, - // Automatically restore mock state and implementation before every test - // restoreMocks: false, + // Automatically restore mock state and implementation before every test + // restoreMocks: false, - // The root directory that Jest should scan for tests and modules within - rootDir: './test/', + // The root directory that Jest should scan for tests and modules within + rootDir: './test/', - // A list of paths to directories that Jest should use to search for files in - roots: [ - "" - ], + // A list of paths to directories that Jest should use to search for files in + roots: [''], - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", + // Allows you to use a custom runner instead of Jest's default test runner + // runner: 'jest-runner', - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], - // The number of seconds after which a test is considered as slow and reported as such in the results. - // slowTestThreshold: 5, + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], - // The test environment that will be used for testing - // testEnvironment: "jest-environment-node", + // The test environment that will be used for testing + // testEnvironment: 'jest-environment-node', - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, - // Adds a location field to test results - // testLocationInResults: false, + // Adds a location field to test results + // testLocationInResults: false, - // The glob patterns Jest uses to detect test files - // testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], + // The glob patterns Jest uses to detect test files + // testMatch: [ + // '**/__tests__/**/*.[jt]s?(x)', + // '**/?(*.)+(spec|test).[tj]s?(x)' + // ], - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "\\\\node_modules\\\\" - // ], + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // '\\\\node_modules\\\\' + // ], - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], - // This option allows the use of a custom results processor - // testResultsProcessor: undefined, + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, - // This option allows use of a custom test runner - // testRunner: "jest-circus/runner", + // This option allows use of a custom test runner + // testRunner: 'jest-circus/runner', - // A map from regular expressions to paths to transformers - // transform: undefined, + // A map from regular expressions to paths to transformers + // transform: undefined, - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "\\\\node_modules\\\\", - // "\\.pnp\\.[^\\\\]+$" - // ], + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // '\\\\node_modules\\\\', + // '\\.pnp\\.[^\\\\]+$' + // ], - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, - // Indicates whether each individual test should be reported during the run - verbose: false, + // Indicates whether each individual test should be reported during the run + verbose: false, - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], - // Whether to use watchman for file crawling - // watchman: true, + // Whether to use watchman for file crawling + // watchman: true, }; diff --git a/package-lock.json b/package-lock.json index a69bb27..5d19a9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,9 @@ }, "devDependencies": { "eslint": "^8.19.0", + "eslint-config-prettier": "^8.5.0", "eslint-plugin-jest": "^26.6.0", + "eslint-plugin-prettier": "^4.2.1", "jest": "^28.1.3" }, "engines": { @@ -2015,6 +2017,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-plugin-jest": { "version": "26.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.6.0.tgz", @@ -2039,6 +2053,27 @@ } } }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -2268,6 +2303,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -3846,6 +3887,34 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-format": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", @@ -6204,6 +6273,13 @@ } } }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, "eslint-plugin-jest": { "version": "26.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.6.0.tgz", @@ -6213,6 +6289,15 @@ "@typescript-eslint/utils": "^5.10.0" } }, + "eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -6335,6 +6420,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -7530,6 +7621,22 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "peer": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-format": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", diff --git a/package.json b/package.json index 6e233d1..d61525f 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,9 @@ }, "devDependencies": { "eslint": "^8.19.0", + "eslint-config-prettier": "^8.5.0", "eslint-plugin-jest": "^26.6.0", + "eslint-plugin-prettier": "^4.2.1", "jest": "^28.1.3" } } diff --git a/src/async.js b/src/async.js index b60d3a9..7d616eb 100644 --- a/src/async.js +++ b/src/async.js @@ -9,7 +9,12 @@ const fs = require('fs'); const gettextParser = require('gettext-parser'); const prepareOptions = require('./options'); -const { resolvePOTFilepaths, getPOFilepaths, generatePO, logResults } = require('./shared'); +const { + resolvePOTFilepaths, + getPOFilepaths, + generatePO, + logResults, +} = require('./shared'); let pot_input_files = []; let po_input_files = []; @@ -27,12 +32,12 @@ let po_input_files = []; * @return {void} */ function parsePO(po_filepath, resolve, reject) { - // Async - Read, parse and process PO file - fs.readFile(po_filepath, (err, file_content) => { - if (err) reject(err); - const po_object = gettextParser.po.parse(file_content); - resolve(po_object); - }); + // Async - Read, parse and process PO file + fs.readFile(po_filepath, (err, file_content) => { + if (err) reject(err); + const po_object = gettextParser.po.parse(file_content); + resolve(po_object); + }); } /** @@ -53,117 +58,134 @@ function parsePO(po_filepath, resolve, reject) { * @return {void} */ function processPOT(pot_file, options, resolve, reject) { - const isVinyl = Vinyl.isVinyl(pot_file); - const pot_filepath = isVinyl ? pot_file.path : pot_file; - - // Get filepaths of POs - const po_filepaths = getPOFilepaths(pot_filepath, options); - - if (po_filepaths.length) { - if (isVinyl) { - if (options.returnPOT) { - pot_input_files.push(pot_file); - } - - const pot_object = gettextParser.po.parse(pot_file.contents); - resolve([pot_object, po_filepaths]); - } else { - // Async - Read and parse POT file - fs.readFile(pot_filepath, (err, pot_content) => { - if (err) reject(err); - - if (options.returnPOT) { - pot_input_files.push(new Vinyl({ - contents: Buffer.from(pot_content), - path: pot_filepath - })); - } - - const pot_object = gettextParser.po.parse(pot_content); - resolve([pot_object, po_filepaths]); - }); - } - } else { - resolve(null); - } + const isVinyl = Vinyl.isVinyl(pot_file); + const pot_filepath = isVinyl ? pot_file.path : pot_file; + + // Get filepaths of POs + const po_filepaths = getPOFilepaths(pot_filepath, options); + + if (po_filepaths.length) { + if (isVinyl) { + if (options.returnPOT) { + pot_input_files.push(pot_file); + } + + const pot_object = gettextParser.po.parse(pot_file.contents); + resolve([pot_object, po_filepaths]); + } else { + // Async - Read and parse POT file + fs.readFile(pot_filepath, (err, pot_content) => { + if (err) reject(err); + + if (options.returnPOT) { + pot_input_files.push( + new Vinyl({ + contents: Buffer.from(pot_content), + path: pot_filepath, + }) + ); + } + + const pot_object = gettextParser.po.parse(pot_content); + resolve([pot_object, po_filepaths]); + }); + } + } else { + resolve(null); + } } function fillPotPo(cb, options) { - if (typeof cb !== 'function') { - throw new PluginError('fillPotPo() requires a callback function as first parameter'); - } - - // Set options - try { - options = prepareOptions(options); - options = resolvePOTFilepaths(options); - } catch (error) { - cb([false, error.toString()]); - return; - } - - // Reset - pot_input_files = []; - po_input_files = []; - - // Process all POT files - Promise.all( options.potSources.map(pot_file => { - const pot_filepath = Vinyl.isVinyl(pot_file) ? pot_file.relative : pot_file; - - return new Promise((resolve, reject) => { - - processPOT(pot_file, options, resolve, reject); - - }).then(async (value) => { - - if (!value) { - po_input_files.push([]); - return []; - } - - // Process all PO files - let pot_object = value[0]; - let po_files = value[1]; - po_input_files.push(po_files); - const po_results = await Promise.all( po_files.map(po_file => { - return new Promise((resolve, reject) => { - - parsePO(po_file, resolve, reject); - - }).then(po_object => { - - // Generate PO and add to collection - return generatePO(pot_object, po_object, po_file, options); - - }).catch(error => { - throw new PluginError(`${c.bold(error.message)} ${c.gray(`(PO ${c.white(po_file)})`)}`); - }); - - }) ); - return po_results; - - }).catch(error => { - throw new PluginError(`${error.message} ${c.gray(`(POT ${c.white(pot_filepath)})`)}`); - }); - - }) ).then(po_output_files => { - if (options.logResults) { - logResults(options._potFilenames, po_input_files, po_output_files, options.destDir); - } - - if (options.returnPOT) { - cb([true, pot_input_files]); - return; - } - - // Flatten into array with all PO files - po_output_files = [].concat(...po_output_files); - cb([true, po_output_files]); - }).catch(error => { - cb([false, error.toString()]); - }); - - return; + if (typeof cb !== 'function') { + throw new PluginError( + 'fillPotPo() requires a callback function as first parameter' + ); + } + + // Set options + try { + options = prepareOptions(options); + options = resolvePOTFilepaths(options); + } catch (error) { + cb([false, error.toString()]); + return; + } + + // Reset + pot_input_files = []; + po_input_files = []; + + // Process all POT files + Promise.all( + options.potSources.map((pot_file) => { + const pot_filepath = Vinyl.isVinyl(pot_file) + ? pot_file.relative + : pot_file; + + return new Promise((resolve, reject) => { + processPOT(pot_file, options, resolve, reject); + }) + .then(async (value) => { + if (!value) { + po_input_files.push([]); + return []; + } + + // Process all PO files + let pot_object = value[0]; + let po_files = value[1]; + po_input_files.push(po_files); + const po_results = await Promise.all( + po_files.map((po_file) => { + return new Promise((resolve, reject) => { + parsePO(po_file, resolve, reject); + }) + .then((po_object) => { + // Generate PO and add to collection + return generatePO(pot_object, po_object, po_file, options); + }) + .catch((error) => { + throw new PluginError( + `${c.bold(error.message)} ${c.gray( + `(PO ${c.white(po_file)})` + )}` + ); + }); + }) + ); + return po_results; + }) + .catch((error) => { + throw new PluginError( + `${error.message} ${c.gray(`(POT ${c.white(pot_filepath)})`)}` + ); + }); + }) + ) + .then((po_output_files) => { + if (options.logResults) { + logResults( + options._potFilenames, + po_input_files, + po_output_files, + options.destDir + ); + } + + if (options.returnPOT) { + cb([true, pot_input_files]); + return; + } + + // Flatten into array with all PO files + po_output_files = [].concat(...po_output_files); + cb([true, po_output_files]); + }) + .catch((error) => { + cb([false, error.toString()]); + }); + + return; } module.exports = fillPotPo; diff --git a/src/index.js b/src/index.js index b1ca5d9..1dd3276 100644 --- a/src/index.js +++ b/src/index.js @@ -3,18 +3,18 @@ const fillPotPo = require('./async'); const fillPotPoSync = require('./sync'); -const the_module = module.exports = fillPotPo; +const the_module = (module.exports = fillPotPo); the_module.sync = fillPotPoSync; /* * Default content-related options for generating PO files. * Use these when generating and testing PO files to ensure a proper comparison. -*/ + */ the_module.testOptions = { - wrapLength: 77, - defaultContextAsFallback: true, - appendNonIncludedFromPO: true, - includePORevisionDate: false, - includeGenerator: false, + wrapLength: 77, + defaultContextAsFallback: true, + appendNonIncludedFromPO: true, + includePORevisionDate: false, + includeGenerator: false, }; diff --git a/src/options.js b/src/options.js index 5c9fe75..06356ff 100644 --- a/src/options.js +++ b/src/options.js @@ -1,8 +1,15 @@ 'use strict'; const PluginError = require('./plugin-error'); -const { isArray, isObject, isString, isBool, isArrayOfStrings, isArrayOfVinyls } = require('./utils'); -const Vinyl = require('vinyl'); +const { + isArray, + isObject, + isString, + isBool, + isArrayOfStrings, + isArrayOfVinyls, +} = require('./utils'); +const { isVinyl } = require('vinyl'); // const { sync: matchedSync } = require('matched'); // const { pathLineSort } = require('./utils'); @@ -12,9 +19,9 @@ const { resolve, relative } = require('path'); * Wrapper for PluginError for all errors with options. */ class OptionsError extends PluginError { - constructor( message ) { - super( message, 'options' ); - } + constructor(message) { + super(message, 'options'); + } } let cwd = './'; @@ -29,74 +36,82 @@ let cwd = './'; * @return {object} */ function validateOptionsInput(options) { - if (isObject(options)) { - if ( - typeof options.potSources !== 'undefined' - && !isString(options.potSources) - && !isArrayOfStrings(options.potSources) - && !Vinyl.isVinyl(options.potSources) - && !isArrayOfVinyls(options.potSources) - ) { - throw new OptionsError('Option potSources should be a string or Vinyl object, or an array of those.'); - } - - if ( - typeof options.poSources !== 'undefined' - && options.poSources - && !isString(options.poSources) - && !isArrayOfStrings(options.poSources) - ) { - throw new OptionsError('Option poSources should be a glob string or glob array.'); - } - - if (typeof options.wrapLength !== 'undefined' - && (typeof options.wrapLength !== 'number' || 0 >= options.wrapLength) - ) { - throw new OptionsError('If set, option wrapLength should be a number higher than 0.'); - } - - const if_set_string = [ 'srcDir', 'destDir', 'domain', - ]; - if_set_string.forEach(k => { - if (typeof options[k] !== 'undefined' && ! isString(options[k])) { - throw new OptionsError(`Option ${k} should be a string.`); - } - }); - - const no_newlines = [ 'srcDir', 'destDir', 'domain' ]; - no_newlines.forEach(k => { - if (typeof options[k] !== 'undefined' && options[k].match(/\n/)) { - throw new OptionsError(`Option ${k} can't contain newline characters.`); - } - }); - - const if_set_bool = [ - 'writeFiles', - 'returnPOT', - 'domainFromPOTPath', - 'domainInPOPath', - 'defaultContextAsFallback', - 'appendNonIncludedFromPO', - 'includePORevisionDate', - 'includeGenerator', - 'logResults', - ]; - if_set_bool.forEach(k => { - if (typeof options[k] !== 'undefined' && ! isBool(options[k])) { - throw new OptionsError(`Option ${k} should be a boolean.`); - } - }); - } else if (isString(options) || isArrayOfStrings(options)) { - options = { - poSources: options - }; - } else if (typeof options !== 'undefined') { - throw new OptionsError('Options should be an object of options, glob string or glob array.'); - } else { - options = {}; - } - - return options; + if (isObject(options)) { + if ( + typeof options.potSources !== 'undefined' && + !isString(options.potSources) && + !isArrayOfStrings(options.potSources) && + !isVinyl(options.potSources) && + !isArrayOfVinyls(options.potSources) + ) { + throw new OptionsError( + 'Option potSources should be a string or Vinyl object, or an array of those.' + ); + } + + if ( + typeof options.poSources !== 'undefined' && + options.poSources && + !isString(options.poSources) && + !isArrayOfStrings(options.poSources) + ) { + throw new OptionsError( + 'Option poSources should be a glob string or glob array.' + ); + } + + if ( + typeof options.wrapLength !== 'undefined' && + (typeof options.wrapLength !== 'number' || 0 >= options.wrapLength) + ) { + throw new OptionsError( + 'If set, option wrapLength should be a number higher than 0.' + ); + } + + const if_set_string = ['srcDir', 'destDir', 'domain']; + if_set_string.forEach((k) => { + if (typeof options[k] !== 'undefined' && !isString(options[k])) { + throw new OptionsError(`Option ${k} should be a string.`); + } + }); + + const no_newlines = ['srcDir', 'destDir', 'domain']; + no_newlines.forEach((k) => { + if (typeof options[k] !== 'undefined' && options[k].match(/\n/)) { + throw new OptionsError(`Option ${k} can't contain newline characters.`); + } + }); + + const if_set_bool = [ + 'writeFiles', + 'returnPOT', + 'domainFromPOTPath', + 'domainInPOPath', + 'defaultContextAsFallback', + 'appendNonIncludedFromPO', + 'includePORevisionDate', + 'includeGenerator', + 'logResults', + ]; + if_set_bool.forEach((k) => { + if (typeof options[k] !== 'undefined' && !isBool(options[k])) { + throw new OptionsError(`Option ${k} should be a boolean.`); + } + }); + } else if (isString(options) || isArrayOfStrings(options)) { + options = { + poSources: options, + }; + } else if (typeof options !== 'undefined') { + throw new OptionsError( + 'Options should be an object of options, glob string or glob array.' + ); + } else { + options = {}; + } + + return options; } /** @@ -107,55 +122,51 @@ function validateOptionsInput(options) { * @return {object} */ function sanitizeAndStandardizeOptionsInput(options) { - if (typeof options.potSources !== 'undefined' && options.potSources) { - if (!isArray(options.potSources)) { - options.potSources = [options.potSources]; - } - options.potSources = options.potSources - .map(v => (typeof v === 'string' ? v.trim() : v)) - .filter(v => (typeof v !== 'string' || v.length > 0)) - ; - } - - if (typeof options.poSources !== 'undefined' && options.poSources) { - // Make array with one or more non-empty strings - if (!isArray(options.poSources)) { - options.poSources = [options.poSources]; - } - options.poSources = options.poSources - .map(v => v.trim()) - .filter(v => (v.length > 0)) - ; - } - - if (options.srcDir) { - // NOTE: all paths starting with a slash are considered absolute paths - options.srcDir = resolve(options.srcDir.trim()); - options.srcDir = `${relative(cwd, options.srcDir)}/` // add trailing slash - .replaceAll(/\\/g, '/') // only forward slashes - .replaceAll(/\/+/g, '/') // remove duplicate slashes - .replaceAll(/(? (typeof v === 'string' ? v.trim() : v)) + .filter((v) => typeof v !== 'string' || v.length > 0); + } + + if (typeof options.poSources !== 'undefined' && options.poSources) { + // Make array with one or more non-empty strings + if (!isArray(options.poSources)) { + options.poSources = [options.poSources]; + } + options.poSources = options.poSources + .map((v) => v.trim()) + .filter((v) => v.length > 0); + } + + if (options.srcDir) { + // NOTE: all paths starting with a slash are considered absolute paths + options.srcDir = resolve(options.srcDir.trim()); + options.srcDir = `${relative(cwd, options.srcDir)}/` // add trailing slash + .replaceAll(/\\/g, '/') // only forward slashes + .replaceAll(/\/+/g, '/') // remove duplicate slashes + .replaceAll(/(?= options.domain.length - ) { - throw new OptionsError('Option domain should be a non-empty string when domainFromPOTPath is false and domainInPOPath is true.'); - } - - if (options.returnPOT && !options.writeFiles) { - throw new OptionsError('If option returnPOT is true, option writeFiles must be true or no PO files will be generated.'); - } - - return options; + cwd = resolve(); + + // Validate/check options + options = validateOptionsInput(options); + + // Sanitize/clean and standardize options + options = sanitizeAndStandardizeOptionsInput(options); + + const defaultOptions = { + // Input-related + potSources: ['**/*.pot', '!node_modules/**'], + poSources: null, + srcDir: '', + domainInPOPath: true, + domainFromPOTPath: true, + domain: '', + srcGlobOptions: {}, + + // Content-related + wrapLength: 77, + defaultContextAsFallback: false, + appendNonIncludedFromPO: false, + includePORevisionDate: false, + includeGenerator: true, + + // Output-related + returnPOT: false, + writeFiles: typeof writeFiles !== 'undefined' ? writeFiles : true, + destDir: '', + logResults: false, + }; + + // Merge with defaults + options = Object.assign({}, defaultOptions, options); + + /** + * Check for logical errors + */ + + if ( + options.domainFromPOTPath === false && + options.domainInPOPath === true && + 0 >= options.domain.length + ) { + throw new OptionsError( + 'Option domain should be a non-empty string when domainFromPOTPath is false and domainInPOPath is true.' + ); + } + + if (options.returnPOT && !options.writeFiles) { + throw new OptionsError( + 'If option returnPOT is true, option writeFiles must be true or no PO files will be generated.' + ); + } + + return options; } module.exports = prepareOptions; diff --git a/src/plugin-error.js b/src/plugin-error.js index 6af1ec5..0e8ef8a 100644 --- a/src/plugin-error.js +++ b/src/plugin-error.js @@ -6,24 +6,27 @@ c.enabled = require('color-support').hasBasic; const pluginname = require('../package.json').name; - // class PluginError extends Error { class PluginError { - constructor( message, category = '' ) { - // super( message ); - // this.name = 'PluginError'; - this.message = message; - this.category = category.slice(0, 1).toUpperCase() + category.slice(1).toLowerCase(); - } - - toString() { - return `${c.cyan(pluginname)} ${c.bold.red(`${this.category}Error`)} ${this.message}`; - } - - // See: https://nodejs.org/api/util.html#custom-inspection-functions-on-objects - [util.inspect.custom]() { // (depth, options, inspect) { - return this.toString(); - } + constructor(message, category = '') { + // super( message ); + // this.name = 'PluginError'; + this.message = message; + this.category = + category.slice(0, 1).toUpperCase() + category.slice(1).toLowerCase(); + } + + toString() { + return `${c.cyan(pluginname)} ${c.bold.red(`${this.category}Error`)} ${ + this.message + }`; + } + + // See: https://nodejs.org/api/util.html#custom-inspection-functions-on-objects + [util.inspect.custom]() { + // (depth, options, inspect) { + return this.toString(); + } } module.exports = PluginError; @@ -38,4 +41,4 @@ module.exports = PluginError; // ${item_type}${item_name}${belonging_to}${error} // // (Option )(lineWrap) () ( should be an integer). -// // () (First argument)( of mergePotPO())( should be a callback function). \ No newline at end of file +// // () (First argument)( of mergePotPO())( should be a callback function). diff --git a/src/shared.js b/src/shared.js index 3d2b415..21b6557 100644 --- a/src/shared.js +++ b/src/shared.js @@ -11,7 +11,6 @@ const PluginError = require('./plugin-error'); const c = require('ansi-colors'); c.enabled = require('color-support').hasBasic; - /** * Resolve POT sources globs to filepaths. * If options.potSources is array of Vinyl objects, leave them as-is. @@ -20,23 +19,28 @@ c.enabled = require('color-support').hasBasic; * @return {object} options */ function resolvePOTFilepaths(options) { - // Resolve POT filepaths to process, if array of strings - if (options.potSources.length && isString(options.potSources[0])) { - options.potSources = matchedSync(options.potSources); - } - - // Store POT filepaths for logging - options._potFilenames = options.potSources.map(f => (isString(f) ? f : f.path)); - - if (0 >= options.potSources.length) { - throw new PluginError('No POT files found to process.'); - } - - if (1 < options.potSources.length && options.poSources) { - throw new PluginError('When processing multiple POT files, leave option poSources empty.\nElse, the same generated PO files will be overwritten for each POT file.', 'options'); - } - - return options; + // Resolve POT filepaths to process, if array of strings + if (options.potSources.length && isString(options.potSources[0])) { + options.potSources = matchedSync(options.potSources); + } + + // Store POT filepaths for logging + options._potFilenames = options.potSources.map((f) => + isString(f) ? f : f.path + ); + + if (0 >= options.potSources.length) { + throw new PluginError('No POT files found to process.'); + } + + if (1 < options.potSources.length && options.poSources) { + throw new PluginError( + 'When processing multiple POT files, leave option poSources empty.\nElse, the same generated PO files will be overwritten for each POT file.', + 'options' + ); + } + + return options; } /** @@ -48,37 +52,39 @@ function resolvePOTFilepaths(options) { * @return {array} PO filepaths */ function getPOFilepaths(pot_filepath, options) { - const pot_name = basename(pot_filepath, '.pot'); - const domain = options.domainFromPOTPath ? pot_name: options.domain; - const po_dir = options.srcDir ? options.srcDir: `${dirname(pot_filepath)}/`; - - // TODO? also search subdirectories? - // preserving glob base for re-use at write and/or return paths - // use: https://github.com/gulpjs/glob-parent - // options.srcSearchRecursive = true/false - - // TODO? - // const srcDirs = matchedSync(`${po_dir}`); // Always has trailing separator - - // Auto-compile PO files glob - const po_files_glob = []; - if (options.poSources) { - // TODO? prefix all with po_dir ? - po_files_glob.push(...options.poSources); - } else { - const locale_glob = '[a-z][a-z]?([a-z])?(_[A-Z][A-Z]?([A-Z]))?(_formal)'; - const domain_glob = options.domainInPOPath ? `${domain}-`: ''; - po_files_glob.push(`${po_dir}${domain_glob}${locale_glob}.po`); - // TODO? - // const sub_dirs = options.srcSearchRecursive ? '**/': ''; - // po_files_glob.push(`${po_dir}${sub_dirs}${domain_glob}${locale_glob}.po`); - } - - // Find and sort file paths - const po_filepaths = pathLineSort(matchedSync(po_files_glob, options.srcGlobOptions)); - // TODO? - // store or return srcDirs (for subtracting from PO paths later on) - return po_filepaths; + const pot_name = basename(pot_filepath, '.pot'); + const domain = options.domainFromPOTPath ? pot_name : options.domain; + const po_dir = options.srcDir ? options.srcDir : `${dirname(pot_filepath)}/`; + + // TODO? also search subdirectories? + // preserving glob base for re-use at write and/or return paths + // use: https://github.com/gulpjs/glob-parent + // options.srcSearchRecursive = true/false + + // TODO? + // const srcDirs = matchedSync(`${po_dir}`); // Always has trailing separator + + // Auto-compile PO files glob + const po_files_glob = []; + if (options.poSources) { + // TODO? prefix all with po_dir ? + po_files_glob.push(...options.poSources); + } else { + const locale_glob = '[a-z][a-z]?([a-z])?(_[A-Z][A-Z]?([A-Z]))?(_formal)'; + const domain_glob = options.domainInPOPath ? `${domain}-` : ''; + po_files_glob.push(`${po_dir}${domain_glob}${locale_glob}.po`); + // TODO? + // const sub_dirs = options.srcSearchRecursive ? '**/': ''; + // po_files_glob.push(`${po_dir}${sub_dirs}${domain_glob}${locale_glob}.po`); + } + + // Find and sort file paths + const po_filepaths = pathLineSort( + matchedSync(po_files_glob, options.srcGlobOptions) + ); + // TODO? + // store or return srcDirs (for subtracting from PO paths later on) + return po_filepaths; } /** @@ -98,27 +104,27 @@ function getPOFilepaths(pot_filepath, options) { * @return {object} Vinyl file object */ function generatePO(pot_object, po_object, po_filepath, options) { - // Deep clone POT as base for the new PO - let new_po_object = JSON.parse(JSON.stringify(pot_object)); - - // Pre-fill template with PO strings - new_po_object = fillPO(new_po_object, po_object, options); - - // Compile object to PO - const new_po_output = compilePO(new_po_object, options); - - // Optionally, write to file - // TODO? preserve subdirectories from search glob? - const new_po_filepath = basename(po_filepath); - if (options.writeFiles) { - writePO(`${options.destDir}${new_po_filepath}`, new_po_output); - } - - // Add Buffer to collection - return new Vinyl({ - contents: Buffer.from(new_po_output), - path: new_po_filepath, - }); + // Deep clone POT as base for the new PO + let new_po_object = JSON.parse(JSON.stringify(pot_object)); + + // Pre-fill template with PO strings + new_po_object = fillPO(new_po_object, po_object, options); + + // Compile object to PO + const new_po_output = compilePO(new_po_object, options); + + // Optionally, write to file + // TODO? preserve subdirectories from search glob? + const new_po_filepath = basename(po_filepath); + if (options.writeFiles) { + writePO(`${options.destDir}${new_po_filepath}`, new_po_output); + } + + // Add Buffer to collection + return new Vinyl({ + contents: Buffer.from(new_po_output), + path: new_po_filepath, + }); } /** @@ -138,104 +144,116 @@ function generatePO(pot_object, po_object, po_filepath, options) { * @return {object} Prefilled PO object */ function fillPO(new_po_object, po_object, options) { - // Traverse template contexts - for (const [ctxt, entries] of Object.entries(new_po_object.translations)) { - // Traverse template entries - for (const [msgid, entry] of Object.entries(entries)) { - // If the PO has a translation for this - // with equal number of strings, use to pre-fill it. - if ( - po_object.translations[ ctxt ] && - po_object.translations[ ctxt ][ msgid ] && - po_object.translations[ ctxt ][ msgid ]['msgstr'].length === entry['msgstr'].length - ) { - new_po_object.translations[ ctxt ][ msgid ]['msgstr'] = [ ...po_object.translations[ ctxt ][ msgid ]['msgstr'] ]; - } else if ( - options.defaultContextAsFallback && - po_object.translations[''] && - po_object.translations[''][ msgid ] && - po_object.translations[''][ msgid ]['msgstr'].length === entry['msgstr'].length - ) { - // Optionally, fallback to default context - new_po_object.translations[ ctxt ][ msgid ]['msgstr'] = [ ...po_object.translations[''][ msgid ]['msgstr'] ]; - - // Set/add fuzzy flag comment - const fuzzy_comment = 'fuzzy'; - if (!new_po_object.translations[ ctxt ][ msgid ].comments) { - new_po_object.translations[ ctxt ][ msgid ].comments = {}; - } - if (!new_po_object.translations[ ctxt ][ msgid ].comments.flag) { - new_po_object.translations[ ctxt ][ msgid ].comments.flag = fuzzy_comment; - } else { - new_po_object.translations[ ctxt ][ msgid ].comments.flag = ( - fuzzy_comment + ', ' + new_po_object.translations[ ctxt ][ msgid ].comments.flag - ); - } - - // Set translator comment to flag re-usage in case of deprecation - // NOTE: comment set on PO object, so it's only included if appended as deprecated. - const reusage_comment = `NOTE: re-used for same message, but with context '${ctxt}'`; - if (!po_object.translations[''][ msgid ].comments) { - po_object.translations[''][ msgid ].comments = {}; - } - if (!po_object.translations[''][ msgid ].comments.translator) { - po_object.translations[''][ msgid ].comments.translator = reusage_comment; - } else { - po_object.translations[''][ msgid ].comments.translator = ( - reusage_comment + '\n' + po_object.translations[''][ msgid ].comments.translator - ); - } - } - } - } - - if (options.appendNonIncludedFromPO) { - // Append all strings from PO that are not present in POT - for (const [ctxt, entries] of Object.entries(po_object.translations)) { - // Add context - if (!new_po_object.translations[ ctxt ]) { - new_po_object.translations[ ctxt ] = {}; - } - - for (const [msgid, entry] of Object.entries(entries)) { - // Add entry - if (!new_po_object.translations[ ctxt ][ msgid ]) { - new_po_object.translations[ ctxt ][ msgid ] = entry; - - // Add translator comment "DEPRECATED" - if (!new_po_object.translations[ ctxt ][ msgid ].comments) { - new_po_object.translations[ ctxt ][ msgid ].comments = {}; - } - if (!new_po_object.translations[ ctxt ][ msgid ].comments.translator) { - new_po_object.translations[ ctxt ][ msgid ].comments.translator = 'DEPRECATED'; - } else if (!entry.comments.translator.match(/^DEPRECATED$/gm)) { - new_po_object.translations[ ctxt ][ msgid ].comments.translator = ( - 'DEPRECATED\n' + new_po_object.translations[ ctxt ][ msgid ].comments.translator - ); - } - } - } - } - } - - if (options.includePORevisionDate) { - const d = new Date(); - const po_rev_date_string = [ - `${d.getUTCFullYear()}`, - `-${String(d.getUTCMonth() + 1).padStart(2, '0')}`, - `-${String(d.getUTCDate()).padStart(2, '0')}`, - ` ${String(d.getUTCHours()).padStart(2, '0')}`, - `:${String(d.getUTCMinutes()).padStart(2, '0')}`, - '+0000' - ].join(''); - new_po_object.headers['po-revision-date'] = po_rev_date_string; - } - - if (options.includeGenerator) { - new_po_object.headers['X-Generator'] = `${packageJSON.name}/${packageJSON.version}`; - } - - return new_po_object; + // Traverse template contexts + for (const [ctxt, entries] of Object.entries(new_po_object.translations)) { + // Traverse template entries + for (const [msgid, entry] of Object.entries(entries)) { + // If the PO has a translation for this + // with equal number of strings, use to pre-fill it. + if ( + po_object.translations[ctxt] && + po_object.translations[ctxt][msgid] && + po_object.translations[ctxt][msgid]['msgstr'].length === + entry['msgstr'].length + ) { + new_po_object.translations[ctxt][msgid]['msgstr'] = [ + ...po_object.translations[ctxt][msgid]['msgstr'], + ]; + } else if ( + options.defaultContextAsFallback && + po_object.translations[''] && + po_object.translations[''][msgid] && + po_object.translations[''][msgid]['msgstr'].length === + entry['msgstr'].length + ) { + // Optionally, fallback to default context + new_po_object.translations[ctxt][msgid]['msgstr'] = [ + ...po_object.translations[''][msgid]['msgstr'], + ]; + + // Set/add fuzzy flag comment + const fuzzy_comment = 'fuzzy'; + if (!new_po_object.translations[ctxt][msgid].comments) { + new_po_object.translations[ctxt][msgid].comments = {}; + } + if (!new_po_object.translations[ctxt][msgid].comments.flag) { + new_po_object.translations[ctxt][msgid].comments.flag = fuzzy_comment; + } else { + new_po_object.translations[ctxt][msgid].comments.flag = + fuzzy_comment + + ', ' + + new_po_object.translations[ctxt][msgid].comments.flag; + } + + // Set translator comment to flag re-usage in case of deprecation + // NOTE: comment set on PO object, so it's only included if appended as deprecated. + const reusage_comment = `NOTE: re-used for same message, but with context '${ctxt}'`; + if (!po_object.translations[''][msgid].comments) { + po_object.translations[''][msgid].comments = {}; + } + if (!po_object.translations[''][msgid].comments.translator) { + po_object.translations[''][msgid].comments.translator = + reusage_comment; + } else { + po_object.translations[''][msgid].comments.translator = + reusage_comment + + '\n' + + po_object.translations[''][msgid].comments.translator; + } + } + } + } + + if (options.appendNonIncludedFromPO) { + // Append all strings from PO that are not present in POT + for (const [ctxt, entries] of Object.entries(po_object.translations)) { + // Add context + if (!new_po_object.translations[ctxt]) { + new_po_object.translations[ctxt] = {}; + } + + for (const [msgid, entry] of Object.entries(entries)) { + // Add entry + if (!new_po_object.translations[ctxt][msgid]) { + new_po_object.translations[ctxt][msgid] = entry; + + // Add translator comment "DEPRECATED" + if (!new_po_object.translations[ctxt][msgid].comments) { + new_po_object.translations[ctxt][msgid].comments = {}; + } + if (!new_po_object.translations[ctxt][msgid].comments.translator) { + new_po_object.translations[ctxt][msgid].comments.translator = + 'DEPRECATED'; + } else if (!entry.comments.translator.match(/^DEPRECATED$/gm)) { + new_po_object.translations[ctxt][msgid].comments.translator = + 'DEPRECATED\n' + + new_po_object.translations[ctxt][msgid].comments.translator; + } + } + } + } + } + + if (options.includePORevisionDate) { + const d = new Date(); + const po_rev_date_string = [ + `${d.getUTCFullYear()}`, + `-${String(d.getUTCMonth() + 1).padStart(2, '0')}`, + `-${String(d.getUTCDate()).padStart(2, '0')}`, + ` ${String(d.getUTCHours()).padStart(2, '0')}`, + `:${String(d.getUTCMinutes()).padStart(2, '0')}`, + '+0000', + ].join(''); + new_po_object.headers['po-revision-date'] = po_rev_date_string; + } + + if (options.includeGenerator) { + new_po_object.headers[ + 'X-Generator' + ] = `${packageJSON.name}/${packageJSON.version}`; + } + + return new_po_object; } /** @@ -250,25 +268,25 @@ function fillPO(new_po_object, po_object, options) { * @return {string} Compiled PO file content */ function compilePO(new_po_object, options) { - return gettextParser.po.compile(new_po_object, { - foldLength: options.wrapLength, - // Sort entries by first reference filepath and line number. - sort: (a, b) => { - // Entries with DEPRECATED translator comment are put last (but sorted as usual there). - const b_deprecated = b.comments?.translator?.match(/^DEPRECATED$/gm); - const a_deprecated = a.comments?.translator?.match(/^DEPRECATED$/gm); - if (!a_deprecated && b_deprecated) return -1; - if (a_deprecated && !b_deprecated) return 1; - - // Entries without reference(s) are put last. - if (!b.comments?.reference) return -1; - if (!a.comments?.reference) return 1; - - a = a.comments.reference.trim().split(/\s+/)[0]; - b = b.comments.reference.trim().split(/\s+/)[0]; - return pathLineSort(a, b); - }, - }); + return gettextParser.po.compile(new_po_object, { + foldLength: options.wrapLength, + // Sort entries by first reference filepath and line number. + sort: (a, b) => { + // Entries with DEPRECATED translator comment are put last (but sorted as usual there). + const b_deprecated = b.comments?.translator?.match(/^DEPRECATED$/gm); + const a_deprecated = a.comments?.translator?.match(/^DEPRECATED$/gm); + if (!a_deprecated && b_deprecated) return -1; + if (a_deprecated && !b_deprecated) return 1; + + // Entries without reference(s) are put last. + if (!b.comments?.reference) return -1; + if (!a.comments?.reference) return 1; + + a = a.comments.reference.trim().split(/\s+/)[0]; + b = b.comments.reference.trim().split(/\s+/)[0]; + return pathLineSort(a, b); + }, + }); } /** @@ -282,11 +300,11 @@ function compilePO(new_po_object, options) { * @return {void} */ function writePO(new_po_filepath, new_po_output) { - const new_po_dir = dirname(new_po_filepath); - if (!existsSync(new_po_dir)) { - mkdirSync(new_po_dir, {recursive: true}); - } - writeFileSync(new_po_filepath, new_po_output); + const new_po_dir = dirname(new_po_filepath); + if (!existsSync(new_po_dir)) { + mkdirSync(new_po_dir, { recursive: true }); + } + writeFileSync(new_po_filepath, new_po_output); } /** @@ -302,36 +320,41 @@ function writePO(new_po_filepath, new_po_output) { * @return {void} */ function logResults(pots, pos_in, pos_out, dest) { - pots.forEach((pot, i) => { - const pot_filepath = basename(pot); - const po_filepaths_in = pos_in[i]?.map(po => po); - const po_filepaths_out = pos_out[i]?.map(po => po.path); - const max_length_in = po_filepaths_in.reduce((p, c) => (Math.max(c.length, p)), 0); - - console.log(''); - - if (po_filepaths_out && po_filepaths_out.length) { - console.log(` ${c.bold.green('■')} ${c.white(pot_filepath)}`); - - po_filepaths_out.forEach((po_filepath_out, pi) => { - console.log([ - ' ', - `${c.cyan(po_filepaths_in[pi].padEnd(max_length_in, ' '))}`, - ` ${c.gray('—►')} `, - `${c.yellow(dest)}${c.yellow(po_filepath_out)}` - ].join('')); - }); - } else { - console.log(` ${c.gray('■')} ${c.white(pot_filepath)}`); - console.log(` ${c.gray('No PO files found.')}`); - } - }); - console.log(''); + pots.forEach((pot, i) => { + const pot_filepath = basename(pot); + const po_filepaths_in = pos_in[i]?.map((po) => po); + const po_filepaths_out = pos_out[i]?.map((po) => po.path); + const max_length_in = po_filepaths_in.reduce( + (p, c) => Math.max(c.length, p), + 0 + ); + + console.log(''); + + if (po_filepaths_out && po_filepaths_out.length) { + console.log(` ${c.bold.green('■')} ${c.white(pot_filepath)}`); + + po_filepaths_out.forEach((po_filepath_out, pi) => { + console.log( + [ + ' ', + `${c.cyan(po_filepaths_in[pi].padEnd(max_length_in, ' '))}`, + ` ${c.gray('—►')} `, + `${c.yellow(dest)}${c.yellow(po_filepath_out)}`, + ].join('') + ); + }); + } else { + console.log(` ${c.gray('■')} ${c.white(pot_filepath)}`); + console.log(` ${c.gray('No PO files found.')}`); + } + }); + console.log(''); } module.exports = { - resolvePOTFilepaths, - getPOFilepaths, - generatePO, - logResults + resolvePOTFilepaths, + getPOFilepaths, + generatePO, + logResults, }; diff --git a/src/sync.js b/src/sync.js index ee66d66..9938109 100644 --- a/src/sync.js +++ b/src/sync.js @@ -5,7 +5,12 @@ const fs = require('fs'); const gettextParser = require('gettext-parser'); const prepareOptions = require('./options'); -const { resolvePOTFilepaths, getPOFilepaths, generatePO, logResults } = require('./shared'); +const { + resolvePOTFilepaths, + getPOFilepaths, + generatePO, + logResults, +} = require('./shared'); let pot_input_files = []; let po_input_files = []; @@ -25,17 +30,17 @@ let po_output_files = []; * @return {void} */ function processPOs(po_filepaths, pot_object, options) { - const pos = []; - // Parse PO files - for (const po_filepath of po_filepaths) { - // Sync - Read and parse PO file - const po_content = fs.readFileSync(po_filepath).toString(); - const po_object = gettextParser.po.parse(po_content); - - // Generate PO and add to collection - pos.push( generatePO(pot_object, po_object, po_filepath, options) ); - } - po_output_files.push(pos); + const pos = []; + // Parse PO files + for (const po_filepath of po_filepaths) { + // Sync - Read and parse PO file + const po_content = fs.readFileSync(po_filepath).toString(); + const po_object = gettextParser.po.parse(po_content); + + // Generate PO and add to collection + pos.push(generatePO(pot_object, po_object, po_filepath, options)); + } + po_output_files.push(pos); } /** @@ -51,67 +56,74 @@ function processPOs(po_filepaths, pot_object, options) { * @return {void} */ function processPOT(pot_file, options) { - const isVinyl = Vinyl.isVinyl(pot_file); - const pot_filepath = isVinyl ? pot_file.path : pot_file; - - // Get filepaths of POs - const po_filepaths = getPOFilepaths(pot_filepath, options); - po_input_files.push(po_filepaths); - - if (po_filepaths.length) { - let pot_content = ''; - if (isVinyl) { - if (options.returnPOT) { - pot_input_files.push(pot_file); - } - - pot_content = pot_file.contents; - } else { - // Sync - Read POT file - pot_content = fs.readFileSync(pot_filepath); - - if (options.returnPOT) { - pot_input_files.push(new Vinyl({ - contents: Buffer.from(pot_content), - path: pot_filepath - })); - } - - pot_content = pot_content.toString(); - } - const pot_object = gettextParser.po.parse(pot_content); - processPOs(po_filepaths, pot_object, options); - } else { - po_output_files.push([]); - } + const isVinyl = Vinyl.isVinyl(pot_file); + const pot_filepath = isVinyl ? pot_file.path : pot_file; + + // Get filepaths of POs + const po_filepaths = getPOFilepaths(pot_filepath, options); + po_input_files.push(po_filepaths); + + if (po_filepaths.length) { + let pot_content = ''; + if (isVinyl) { + if (options.returnPOT) { + pot_input_files.push(pot_file); + } + + pot_content = pot_file.contents; + } else { + // Sync - Read POT file + pot_content = fs.readFileSync(pot_filepath); + + if (options.returnPOT) { + pot_input_files.push( + new Vinyl({ + contents: Buffer.from(pot_content), + path: pot_filepath, + }) + ); + } + + pot_content = pot_content.toString(); + } + const pot_object = gettextParser.po.parse(pot_content); + processPOs(po_filepaths, pot_object, options); + } else { + po_output_files.push([]); + } } function fillPotPoSync(options) { - // Reset - pot_input_files = []; - po_input_files = []; - po_output_files = []; - - // Set options - options = prepareOptions(options); - options = resolvePOTFilepaths(options); - - // Process all POT files - options.potSources.forEach(pot_file => { - processPOT(pot_file, options); - }); - - if (options.logResults) { - logResults(options._potFilenames, po_input_files, po_output_files, options.destDir); - } - - if (options.returnPOT) { - return pot_input_files; - } - - // Flatten into array with all PO files - po_output_files = [].concat(...po_output_files); - return po_output_files; + // Reset + pot_input_files = []; + po_input_files = []; + po_output_files = []; + + // Set options + options = prepareOptions(options); + options = resolvePOTFilepaths(options); + + // Process all POT files + options.potSources.forEach((pot_file) => { + processPOT(pot_file, options); + }); + + if (options.logResults) { + logResults( + options._potFilenames, + po_input_files, + po_output_files, + options.destDir + ); + } + + if (options.returnPOT) { + return pot_input_files; + } + + // Flatten into array with all PO files + po_output_files = [].concat(...po_output_files); + return po_output_files; } module.exports = fillPotPoSync; diff --git a/src/utils.js b/src/utils.js index 9e2e04b..0385a53 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,6 @@ 'use strict'; -const Vinyl = require('vinyl'); +const { isVinyl } = require('vinyl'); /** * Escape string for literal match in regex. @@ -12,7 +12,7 @@ const Vinyl = require('vinyl'); * @return {string} */ function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } /** @@ -23,7 +23,7 @@ function escapeRegExp(string) { * @return {boolean} */ function isArray(ar) { - return Object.prototype.toString.call(ar) === '[object Array]'; + return Object.prototype.toString.call(ar) === '[object Array]'; } /** @@ -34,7 +34,7 @@ function isArray(ar) { * @return {boolean} */ function isObject(obj) { - return Object.prototype.toString.call(obj) === '[object Object]'; + return Object.prototype.toString.call(obj) === '[object Object]'; } /** @@ -45,7 +45,7 @@ function isObject(obj) { * @return {boolean} */ function isString(str) { - return Object.prototype.toString.call(str) === '[object String]'; + return Object.prototype.toString.call(str) === '[object String]'; } /** @@ -56,7 +56,7 @@ function isString(str) { * @return {boolean} */ function isBool(bl) { - return Object.prototype.toString.call(bl) === '[object Boolean]'; + return Object.prototype.toString.call(bl) === '[object Boolean]'; } /** @@ -67,8 +67,8 @@ function isBool(bl) { * @return {boolean} */ function isArrayOfStrings(ar) { - if (!isArray(ar)) return false; - return ar.reduce((r, v) => (isString(v) && r), true); + if (!isArray(ar)) return false; + return ar.reduce((r, v) => isString(v) && r, true); } /** @@ -79,8 +79,8 @@ function isArrayOfStrings(ar) { * @return {boolean} */ function isArrayOfVinyls(ar) { - if (!isArray(ar)) return false; - return ar.reduce((r, v) => (Vinyl.isVinyl(v) && r), true); + if (!isArray(ar)) return false; + return ar.reduce((r, v) => isVinyl(v) && r, true); } /** @@ -93,82 +93,82 @@ function isArrayOfVinyls(ar) { * @return {number} -1, 0 or 1 */ function pathLineSort(a, b) { - // Wrapper mode - if (isArray(a) && typeof b === 'undefined') { - return a.sort(pathLineSort); - } - - if (!isString(a) || !isString(b)) { - throw new Error('pathLineSort: a or b not a string'); - } - - // split line numbers - let a_line, b_line; - [a, a_line] = a.split(':'); - [b, b_line] = b.split(':'); - - // trim leading/trailing slashes - a = a.replaceAll(/(^\/|\/$)/g, ''); - b = b.replaceAll(/(^\/|\/$)/g, ''); - - // line numbers ascending for same paths - if (a == b) { - // no line number first - if ((!a_line || '' === a_line) && b_line && b_line.length) return -1; - if ((!b_line || '' === b_line) && a_line && a_line.length) return +1; - - if (a_line && a_line.length && b_line && b_line.length) { - return (parseInt(a_line) - parseInt(b_line)); - } - } - - // split by directory - a = a.split('/'); - b = b.split('/'); - - const a_len = a.length; - const b_len = b.length; - - // Based on: path-sort package - const l = Math.max(a_len, b_len); - for (let i = 0; i < l; i++) { - // less deep paths at the end - if (i >= b_len || '' === b[i]) return -1; - if (i >= a_len || '' === a[i]) return +1; - - // split extension - let a_part, b_part; - let a_ext, b_ext; - [a_part, a_ext] = a[i].split(/\.(?=[^.]*$)/g); - [b_part, b_ext] = b[i].split(/\.(?=[^.]*$)/g); - const a_is_file = (a_ext && '' !== a_ext); - const b_is_file = (b_ext && '' !== b_ext); - - // files after folders - if (!a_is_file && b_is_file) return -1; - if (a_is_file && !b_is_file) return +1; - - // file/folder name - alphabetical ascending - if (a_part.toUpperCase() > b_part.toUpperCase()) return +1; - if (a_part.toUpperCase() < b_part.toUpperCase()) return -1; - - // file extension - alphabetical ascending - if (a_ext && b_ext) { - if (a_ext.toUpperCase() > b_ext.toUpperCase()) return +1; - if (a_ext.toUpperCase() < b_ext.toUpperCase()) return -1; - } - } - - return 0; + // Wrapper mode + if (isArray(a) && typeof b === 'undefined') { + return a.sort(pathLineSort); + } + + if (!isString(a) || !isString(b)) { + throw new Error('pathLineSort: a or b not a string'); + } + + // split line numbers + let a_line, b_line; + [a, a_line] = a.split(':'); + [b, b_line] = b.split(':'); + + // trim leading/trailing slashes + a = a.replaceAll(/(^\/|\/$)/g, ''); + b = b.replaceAll(/(^\/|\/$)/g, ''); + + // line numbers ascending for same paths + if (a == b) { + // no line number first + if ((!a_line || '' === a_line) && b_line && b_line.length) return -1; + if ((!b_line || '' === b_line) && a_line && a_line.length) return +1; + + if (a_line && a_line.length && b_line && b_line.length) { + return parseInt(a_line) - parseInt(b_line); + } + } + + // split by directory + a = a.split('/'); + b = b.split('/'); + + const a_len = a.length; + const b_len = b.length; + + // Based on: path-sort package + const l = Math.max(a_len, b_len); + for (let i = 0; i < l; i++) { + // less deep paths at the end + if (i >= b_len || '' === b[i]) return -1; + if (i >= a_len || '' === a[i]) return +1; + + // split extension + let a_part, b_part; + let a_ext, b_ext; + [a_part, a_ext] = a[i].split(/\.(?=[^.]*$)/g); + [b_part, b_ext] = b[i].split(/\.(?=[^.]*$)/g); + const a_is_file = a_ext && '' !== a_ext; + const b_is_file = b_ext && '' !== b_ext; + + // files after folders + if (!a_is_file && b_is_file) return -1; + if (a_is_file && !b_is_file) return +1; + + // file/folder name - alphabetical ascending + if (a_part.toUpperCase() > b_part.toUpperCase()) return +1; + if (a_part.toUpperCase() < b_part.toUpperCase()) return -1; + + // file extension - alphabetical ascending + if (a_ext && b_ext) { + if (a_ext.toUpperCase() > b_ext.toUpperCase()) return +1; + if (a_ext.toUpperCase() < b_ext.toUpperCase()) return -1; + } + } + + return 0; } module.exports = { - escapeRegExp, - isArray, - isObject, - isString, - isBool, - isArrayOfStrings, - isArrayOfVinyls, - pathLineSort + escapeRegExp, + isArray, + isObject, + isString, + isBool, + isArrayOfStrings, + isArrayOfVinyls, + pathLineSort, }; diff --git a/test/async.test.js b/test/async.test.js index afb5465..327ba61 100644 --- a/test/async.test.js +++ b/test/async.test.js @@ -17,7 +17,9 @@ const expected_dir = 'test/examples/output_correct/'; const test_dir = 'test/examples/output/fa'; const pot_source_buffer = fs.readFileSync(potSource); -const expected_po_domain = fs.readFileSync(`${expected_dir}text-domain-nl_NL.po`); +const expected_po_domain = fs.readFileSync( + `${expected_dir}text-domain-nl_NL.po` +); const expected_po_no_domain = fs.readFileSync(`${expected_dir}nl_NL.po`); /** @@ -26,743 +28,733 @@ const expected_po_no_domain = fs.readFileSync(`${expected_dir}nl_NL.po`); * @return {void} */ function clearOutputFolder() { - let files = matchedSync([ - `${test_dir}*`, - ]); - files.sort((a, b) => { - const a_len = a.split(/\//); - const b_len = b.split(/\//); - return b_len - a_len; - }); - for (const file of files) { - fs.rmSync(file, {recursive: true}); - } + let files = matchedSync([`${test_dir}*`]); + files.sort((a, b) => { + const a_len = a.split(/\//); + const b_len = b.split(/\//); + return b_len - a_len; + }); + for (const file of files) { + fs.rmSync(file, { recursive: true }); + } } beforeAll(() => { - clearOutputFolder(); + clearOutputFolder(); }); afterAll(() => { - clearOutputFolder(); + clearOutputFolder(); }); describe('async.js - single POT', () => { - - let folder_i = 0; - - /* eslint-disable jest/no-done-callback */ - - test('auto domain PO - no write', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: false, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - - // Check contents - expect( - result[0].contents - .equals(expected_po_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('auto no-domain PO - no write', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - domainInPOPath: false, - writeFiles: false, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - - // Check contents - expect( - result[0].contents - .equals(expected_po_no_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('manual multiple PO - no write', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - poSources: [ poSources ], - srcDir: input_dir, - writeFiles: false, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(2); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - expect(result[1]).toBeInstanceOf(Vinyl); - expect(result[1].isBuffer()).toBe(true); - expect(result[1].path).toEqual('text-domain-nl_NL.po'); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - - // Check contents - expect( - result[0].contents - .equals(expected_po_no_domain) - ).toBe(true); - expect( - result[1].contents - .equals(expected_po_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('auto domain PO - write', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: true, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents of file - expect( - fs.readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('auto no-domain PO - write', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - domainInPOPath: false, - writeFiles: true, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/nl_NL.po` - ]); - - // Check contents of file - expect( - fs.readFileSync(`${folder_path}/nl_NL.po`) - .equals(expected_po_no_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('manual multiple PO - write', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - poSources: [ poSources ], - srcDir: input_dir, - writeFiles: true, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(2); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - expect(result[1]).toBeInstanceOf(Vinyl); - expect(result[1].isBuffer()).toBe(true); - expect(result[1].path).toEqual('text-domain-nl_NL.po'); - - // Check if files exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toEqual([ - `${folder_path}/nl_NL.po`, - `${folder_path}/text-domain-nl_NL.po`, - ]); - - // Check contents of files - expect( - fs.readFileSync(`${folder_path}/nl_NL.po`) - .equals(expected_po_no_domain) - ).toBe(true); - expect( - fs.readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('manual empty PO array', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - // Mock the console.log function - const consoleSpy = jest.spyOn(console, 'log') - .mockName('console.log') - .mockImplementation((...args) => { - return args.map(v => String(v).trim().replaceAll(/\s+/g, ' ')).join(' '); - }); - - const options = { - ...testOptions, - potSources: [ potSource ], - poSources: [], - srcDir: input_dir, - writeFiles: false, - logResults: true, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(0); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - - // Check if results were logged - expect(consoleSpy).toHaveBeenCalledTimes(4); - const logs = consoleSpy.mock.results.slice(); - expect(logs[0].value + logs[3].value).toEqual(''); - expect(logs[1].value).toMatch(new RegExp('■ ' + potSource.split('/').slice(-1)[0])); - expect(logs[2].value).toMatch(new RegExp('No PO files found.')); - - // Restore the console.log function - consoleSpy.mockRestore(); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('auto domain PO - write - return POT', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - returnPOT: true, - writeFiles: true, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(relative(result[0].path, potSource)).toEqual(''); - - // Check contents - expect( - result[0].contents - .equals(pot_source_buffer) - ).toBe(true); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents of file - expect( - fs.readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('auto domain PO - no write - input POT Vinyl', done => { - const options = { - ...testOptions, - potSources: new Vinyl({ - contents: pot_source_buffer, - path: potSource, - }), - srcDir: input_dir, - writeFiles: false, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check contents - expect( - result[0].contents - .equals(expected_po_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('auto domain PO - write - return POT - input POT Vinyl', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!fs.existsSync(folder_path)) { - fs.mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: new Vinyl({ - contents: pot_source_buffer, - path: potSource, - }), - srcDir: input_dir, - returnPOT: true, - writeFiles: true, - destDir: folder_path, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(relative(result[0].path, potSource)).toEqual(''); - - // Check contents - expect( - result[0].contents - .equals(pot_source_buffer) - ).toBe(true); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents of file - expect( - fs.readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('extras - auto domain PO - write - content optionals', done => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - - // Mock the console.log function - const consoleSpy = jest.spyOn(console, 'log') - .mockName('console.log') - .mockImplementation((...args) => { - return args.map(v => String(v).trim().replaceAll(/\s+/g, ' ')).join(' '); - }); - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: true, - destDir: folder_path, - logResults: true, - appendNonIncludedFromPO: false, - includePORevisionDate: true, - includeGenerator: true, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(true); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check if folder and file exist - expect(fs.existsSync(folder_path)).toBe(true); - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents - const result_string_content = result[0].contents.toString(); - expect(result_string_content) - .not.toMatch(/^# DEPRECATED$/m); - expect(result_string_content) - .toMatch(/^"PO-Revision-Date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}\+0000\\n"$/m); - expect(result_string_content) - .toMatch(/^"X-Generator: fill-pot-po\/\d+\.\d+\.\d+\\n"$/m); - - // Check if results were logged - expect(consoleSpy).toHaveBeenCalledTimes(4); - const logs = consoleSpy.mock.results.slice(); - expect(logs[0].value + logs[3].value).toEqual(''); - expect(logs[1].value).toMatch(new RegExp('■ ' + potSource.split('/').slice(-1)[0])); - expect(logs[2].value).toMatch(new RegExp(`${input_dir}text-domain-nl_NL.po —► ${folder_path}/text-domain-nl_NL.po`)); - - // Restore the console.log function - consoleSpy.mockRestore(); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('return options error', done => { - const options = { - ...testOptions, - potSources: true, - writeFiles: false, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(false); - - // Check returned array - expect(result).toMatch(new RegExp('Option potSources should be a string')); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('return POT fs.readFile error', done => { - // Mock the fs.readFile function - const readFileSpy = jest.spyOn(fs, 'readFile') - .mockName('fs.readFile') - .mockImplementation((path, cb) => { - // first: *.pot - // then: *.po - cb({message: 'POT_MOCK_FS_READFILE_ERROR'}, ''); - }); - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: false, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(false); - - // Check returned array - expect(result).toMatch(new RegExp('POT_MOCK_FS_READFILE_ERROR')); - - // Restore the fs.readFile function - readFileSpy.mockRestore(); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - test('return PO fs.readFile error', done => { - // Mock the fs.readFile function - const origReadFile = fs.readFile; - const readFileSpy = jest.spyOn(fs, 'readFile') - .mockName('fs.readFile') - .mockImplementation((path, cb) => { - // first *.pot - if (path.match(/\.pot$/i)) { - origReadFile(path, cb); - return; - } - // then *.po - cb({message: 'PO_MOCK_FS_READFILE_ERROR'}, ''); - }); - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: false, - }; - - function cb(result_array) { - try { - // Errorless execution - expect(result_array).toHaveLength(2); - const [was_success, result] = result_array; - expect(was_success).toBe(false); - - // Check returned array - expect(result).toMatch(new RegExp('PO_MOCK_FS_READFILE_ERROR')); - - // Restore the fs.readFile function - readFileSpy.mockRestore(); - - done(); - } catch (error) { - done(error); - } - } - - fillPotPo(cb, options); - }); - - /* eslint-enable jest/no-done-callback */ - - test('fillPotPo requires a callback', () => { - expect(() => { - fillPotPo(); - }).toThrow(/fillPotPo\(\) requires a callback function as first parameter/); - }); - + let folder_i = 0; + + /* eslint-disable jest/no-done-callback */ + + test('auto domain PO - no write', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: false, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + + // Check contents + expect(result[0].contents.equals(expected_po_domain)).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('auto no-domain PO - no write', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + domainInPOPath: false, + writeFiles: false, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + + // Check contents + expect(result[0].contents.equals(expected_po_no_domain)).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('manual multiple PO - no write', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + poSources: [poSources], + srcDir: input_dir, + writeFiles: false, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(2); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + expect(result[1]).toBeInstanceOf(Vinyl); + expect(result[1].isBuffer()).toBe(true); + expect(result[1].path).toEqual('text-domain-nl_NL.po'); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + + // Check contents + expect(result[0].contents.equals(expected_po_no_domain)).toBe(true); + expect(result[1].contents.equals(expected_po_domain)).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('auto domain PO - write', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: true, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents of file + expect( + fs + .readFileSync(`${folder_path}/text-domain-nl_NL.po`) + .equals(expected_po_domain) + ).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('auto no-domain PO - write', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + domainInPOPath: false, + writeFiles: true, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/nl_NL.po`]); + + // Check contents of file + expect( + fs + .readFileSync(`${folder_path}/nl_NL.po`) + .equals(expected_po_no_domain) + ).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('manual multiple PO - write', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + poSources: [poSources], + srcDir: input_dir, + writeFiles: true, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(2); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + expect(result[1]).toBeInstanceOf(Vinyl); + expect(result[1].isBuffer()).toBe(true); + expect(result[1].path).toEqual('text-domain-nl_NL.po'); + + // Check if files exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toEqual([ + `${folder_path}/nl_NL.po`, + `${folder_path}/text-domain-nl_NL.po`, + ]); + + // Check contents of files + expect( + fs + .readFileSync(`${folder_path}/nl_NL.po`) + .equals(expected_po_no_domain) + ).toBe(true); + expect( + fs + .readFileSync(`${folder_path}/text-domain-nl_NL.po`) + .equals(expected_po_domain) + ).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('manual empty PO array', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + // Mock the console.log function + const consoleSpy = jest + .spyOn(console, 'log') + .mockName('console.log') + .mockImplementation((...args) => { + return args + .map((v) => String(v).trim().replaceAll(/\s+/g, ' ')) + .join(' '); + }); + + const options = { + ...testOptions, + potSources: [potSource], + poSources: [], + srcDir: input_dir, + writeFiles: false, + logResults: true, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(0); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + + // Check if results were logged + expect(consoleSpy).toHaveBeenCalledTimes(4); + const logs = consoleSpy.mock.results.slice(); + expect(logs[0].value + logs[3].value).toEqual(''); + expect(logs[1].value).toMatch( + new RegExp('■ ' + potSource.split('/').slice(-1)[0]) + ); + expect(logs[2].value).toMatch(new RegExp('No PO files found.')); + + // Restore the console.log function + consoleSpy.mockRestore(); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('auto domain PO - write - return POT', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + returnPOT: true, + writeFiles: true, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(relative(result[0].path, potSource)).toEqual(''); + + // Check contents + expect(result[0].contents.equals(pot_source_buffer)).toBe(true); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents of file + expect( + fs + .readFileSync(`${folder_path}/text-domain-nl_NL.po`) + .equals(expected_po_domain) + ).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('auto domain PO - no write - input POT Vinyl', (done) => { + const options = { + ...testOptions, + potSources: new Vinyl({ + contents: pot_source_buffer, + path: potSource, + }), + srcDir: input_dir, + writeFiles: false, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check contents + expect(result[0].contents.equals(expected_po_domain)).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('auto domain PO - write - return POT - input POT Vinyl', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!fs.existsSync(folder_path)) { + fs.mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: new Vinyl({ + contents: pot_source_buffer, + path: potSource, + }), + srcDir: input_dir, + returnPOT: true, + writeFiles: true, + destDir: folder_path, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(relative(result[0].path, potSource)).toEqual(''); + + // Check contents + expect(result[0].contents.equals(pot_source_buffer)).toBe(true); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents of file + expect( + fs + .readFileSync(`${folder_path}/text-domain-nl_NL.po`) + .equals(expected_po_domain) + ).toBe(true); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('extras - auto domain PO - write - content optionals', (done) => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + + // Mock the console.log function + const consoleSpy = jest + .spyOn(console, 'log') + .mockName('console.log') + .mockImplementation((...args) => { + return args + .map((v) => String(v).trim().replaceAll(/\s+/g, ' ')) + .join(' '); + }); + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: true, + destDir: folder_path, + logResults: true, + appendNonIncludedFromPO: false, + includePORevisionDate: true, + includeGenerator: true, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(true); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check if folder and file exist + expect(fs.existsSync(folder_path)).toBe(true); + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents + const result_string_content = result[0].contents.toString(); + expect(result_string_content).not.toMatch(/^# DEPRECATED$/m); + expect(result_string_content).toMatch( + /^"PO-Revision-Date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}\+0000\\n"$/m + ); + expect(result_string_content).toMatch( + /^"X-Generator: fill-pot-po\/\d+\.\d+\.\d+\\n"$/m + ); + + // Check if results were logged + expect(consoleSpy).toHaveBeenCalledTimes(4); + const logs = consoleSpy.mock.results.slice(); + expect(logs[0].value + logs[3].value).toEqual(''); + expect(logs[1].value).toMatch( + new RegExp('■ ' + potSource.split('/').slice(-1)[0]) + ); + expect(logs[2].value).toMatch( + new RegExp( + `${input_dir}text-domain-nl_NL.po —► ${folder_path}/text-domain-nl_NL.po` + ) + ); + + // Restore the console.log function + consoleSpy.mockRestore(); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('return options error', (done) => { + const options = { + ...testOptions, + potSources: true, + writeFiles: false, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(false); + + // Check returned array + expect(result).toMatch( + new RegExp('Option potSources should be a string') + ); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('return POT fs.readFile error', (done) => { + // Mock the fs.readFile function + const readFileSpy = jest + .spyOn(fs, 'readFile') + .mockName('fs.readFile') + .mockImplementation((path, cb) => { + // first: *.pot + // then: *.po + cb({ message: 'POT_MOCK_FS_READFILE_ERROR' }, ''); + }); + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: false, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(false); + + // Check returned array + expect(result).toMatch(new RegExp('POT_MOCK_FS_READFILE_ERROR')); + + // Restore the fs.readFile function + readFileSpy.mockRestore(); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + test('return PO fs.readFile error', (done) => { + // Mock the fs.readFile function + const origReadFile = fs.readFile; + const readFileSpy = jest + .spyOn(fs, 'readFile') + .mockName('fs.readFile') + .mockImplementation((path, cb) => { + // first *.pot + if (path.match(/\.pot$/i)) { + origReadFile(path, cb); + return; + } + // then *.po + cb({ message: 'PO_MOCK_FS_READFILE_ERROR' }, ''); + }); + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: false, + }; + + function cb(result_array) { + try { + // Errorless execution + expect(result_array).toHaveLength(2); + const [was_success, result] = result_array; + expect(was_success).toBe(false); + + // Check returned array + expect(result).toMatch(new RegExp('PO_MOCK_FS_READFILE_ERROR')); + + // Restore the fs.readFile function + readFileSpy.mockRestore(); + + done(); + } catch (error) { + done(error); + } + } + + fillPotPo(cb, options); + }); + + /* eslint-enable jest/no-done-callback */ + + test('fillPotPo requires a callback', () => { + expect(() => { + fillPotPo(); + }).toThrow(/fillPotPo\(\) requires a callback function as first parameter/); + }); }); // TODO? potSources: Vinyl or Array-of diff --git a/test/options.test.js b/test/options.test.js index e408d89..e937b3f 100644 --- a/test/options.test.js +++ b/test/options.test.js @@ -6,421 +6,454 @@ const { escapeRegExp } = require('../src/utils'); const Vinyl = require('vinyl'); function reOptionError(k) { - return new RegExp(`option ${escapeRegExp(k)}`, 'i'); + return new RegExp(`option ${escapeRegExp(k)}`, 'i'); } const vinyl_file = new Vinyl({ - contents: Buffer.from('some contents'), - path: 'filename.pot', + contents: Buffer.from('some contents'), + path: 'filename.pot', }); const potSource = ['./test/examples/text-domain.pot']; describe('options.js - validate', () => { - // String only - const string_only = [ 'srcDir', 'destDir', 'domain' ]; - string_only.forEach(k => { - test(`${k} - only string`, () => { - const re = reOptionError(k); - expect(() => { - prepareOptions({ [k]: null }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: false }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: true }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: 1 }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: '' }); - }).not.toThrow(re); - expect(() => { - prepareOptions({ [k]: [] }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: {} }); - }).toThrow(re); - }); - }); - - // Boolean only - const bool_only = [ - 'writeFiles', - 'returnPOT', - 'domainFromPOTPath', - 'domainInPOPath', - 'defaultContextAsFallback', - 'appendNonIncludedFromPO', - 'includePORevisionDate', - 'includeGenerator', - 'logResults', - ]; - bool_only.forEach(k => { - test(`${k} - only boolean`, () => { - const re = reOptionError(k); - expect(() => { - prepareOptions({ [k]: null }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: false }); - }).not.toThrow(re); - expect(() => { - prepareOptions({ [k]: true }); - }).not.toThrow(re); - expect(() => { - prepareOptions({ [k]: 1 }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: '' }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: [] }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: {} }); - }).toThrow(re); - }); - }); - - // Number only - const number_only = [ 'wrapLength' ]; - number_only.forEach(k => { - test(`${k} - only number`, () => { - const re = reOptionError(k); - expect(() => { - prepareOptions({ [k]: null }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: false }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: true }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: 1 }); - }).not.toThrow(re); - expect(() => { - prepareOptions({ [k]: '' }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: [] }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: {} }); - }).toThrow(re); - }); - }); - - test('potSources - only string, Vinyl or array of those', () => { - const k = 'potSources'; - const re = reOptionError(k); - expect(() => { - prepareOptions({ [k]: null }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: false }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: true }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: 1 }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: '' }); - }).not.toThrow(re); - expect(() => { - prepareOptions({ [k]: [] }); - }).not.toThrow(re); - expect(() => { - prepareOptions({ [k]: {} }); - }).toThrow(re); - expect(() => { - prepareOptions({ [k]: vinyl_file }); - }).not.toThrow(re); - expect(() => { - prepareOptions({ [k]: [vinyl_file] }); - }).not.toThrow(re); - }); - - test('options - only absent, object, glob string or glob array', () => { - const re = /Options should be an object/; - - expect(() => { - prepareOptions(); - }).not.toThrow(re); - - expect(() => { - prepareOptions(undefined); - }).not.toThrow(re); - - expect(() => { - prepareOptions(null); - }).toThrow(re); - - expect(() => { - prepareOptions(false); - }).toThrow(re); - - expect(() => { - prepareOptions(true); - }).toThrow(re); - - expect(() => { - prepareOptions(1); - }).toThrow(re); - - expect(() => { - prepareOptions('**/*.po'); - }).not.toThrow(re); - - expect(() => { - prepareOptions([]); - }).not.toThrow(re); - - expect(() => { - prepareOptions({}); - }).not.toThrow(re); - - expect(() => { - prepareOptions([1]); - }).toThrow(re); - - expect(() => { - prepareOptions([true]); - }).toThrow(re); - - expect(() => { - prepareOptions(['**/*.po']); - }).not.toThrow(re); - - expect(() => { - prepareOptions([[]]); - }).toThrow(re); - - expect(() => { - prepareOptions([{}]); - }).toThrow(re); - }); - - test('poSources - if truthy, only string or array of strings', () => { - const re = reOptionError('poSources'); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: null }); - }).not.toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: false }); - }).not.toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: true }); - }).toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: 1 }); - }).toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: '**/*.po' }); - }).not.toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: ['**/*.po'] }); - }).not.toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: {} }); - }).toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: [1] }); - }).toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: [true] }); - }).toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: [[]] }); - }).toThrow(re); - - expect(() => { - prepareOptions({ potSources: potSource, poSources: [{}] }); - }).toThrow(re); - }); - - test('writeFiles - default overwrite for gulp plugin', () => { - expect( prepareOptions({ potSources: '**/*.pot' }, false) ).toHaveProperty('writeFiles', false); - }); - - test('srcDir - no newlines', () => { - expect(() => { - prepareOptions({ srcDir: 'some\npath' }); - }).toThrow(reOptionError('srcDir')); - }); - - test('destDir - no newlines', () => { - expect(() => { - prepareOptions({ destDir: 'some\npath' }); - }).toThrow(reOptionError('destDir')); - }); - - test('wrapLength - only positive integer', () => { - const re = reOptionError('wrapLength'); - - expect(() => { - prepareOptions({ wrapLength: -1 }); - }).toThrow(re); - - expect(() => { - prepareOptions({ wrapLength: 0 }); - }).toThrow(re); - }); + // String only + const string_only = ['srcDir', 'destDir', 'domain']; + string_only.forEach((k) => { + test(`${k} - only string`, () => { + const re = reOptionError(k); + expect(() => { + prepareOptions({ [k]: null }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: false }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: true }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: 1 }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: '' }); + }).not.toThrow(re); + expect(() => { + prepareOptions({ [k]: [] }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: {} }); + }).toThrow(re); + }); + }); + + // Boolean only + const bool_only = [ + 'writeFiles', + 'returnPOT', + 'domainFromPOTPath', + 'domainInPOPath', + 'defaultContextAsFallback', + 'appendNonIncludedFromPO', + 'includePORevisionDate', + 'includeGenerator', + 'logResults', + ]; + bool_only.forEach((k) => { + test(`${k} - only boolean`, () => { + const re = reOptionError(k); + expect(() => { + prepareOptions({ [k]: null }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: false }); + }).not.toThrow(re); + expect(() => { + prepareOptions({ [k]: true }); + }).not.toThrow(re); + expect(() => { + prepareOptions({ [k]: 1 }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: '' }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: [] }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: {} }); + }).toThrow(re); + }); + }); + + // Number only + const number_only = ['wrapLength']; + number_only.forEach((k) => { + test(`${k} - only number`, () => { + const re = reOptionError(k); + expect(() => { + prepareOptions({ [k]: null }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: false }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: true }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: 1 }); + }).not.toThrow(re); + expect(() => { + prepareOptions({ [k]: '' }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: [] }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: {} }); + }).toThrow(re); + }); + }); + + test('potSources - only string, Vinyl or array of those', () => { + const k = 'potSources'; + const re = reOptionError(k); + expect(() => { + prepareOptions({ [k]: null }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: false }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: true }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: 1 }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: '' }); + }).not.toThrow(re); + expect(() => { + prepareOptions({ [k]: [] }); + }).not.toThrow(re); + expect(() => { + prepareOptions({ [k]: {} }); + }).toThrow(re); + expect(() => { + prepareOptions({ [k]: vinyl_file }); + }).not.toThrow(re); + expect(() => { + prepareOptions({ [k]: [vinyl_file] }); + }).not.toThrow(re); + }); + + test('options - only absent, object, glob string or glob array', () => { + const re = /Options should be an object/; + + expect(() => { + prepareOptions(); + }).not.toThrow(re); + + expect(() => { + prepareOptions(undefined); + }).not.toThrow(re); + + expect(() => { + prepareOptions(null); + }).toThrow(re); + + expect(() => { + prepareOptions(false); + }).toThrow(re); + + expect(() => { + prepareOptions(true); + }).toThrow(re); + + expect(() => { + prepareOptions(1); + }).toThrow(re); + + expect(() => { + prepareOptions('**/*.po'); + }).not.toThrow(re); + + expect(() => { + prepareOptions([]); + }).not.toThrow(re); + + expect(() => { + prepareOptions({}); + }).not.toThrow(re); + + expect(() => { + prepareOptions([1]); + }).toThrow(re); + + expect(() => { + prepareOptions([true]); + }).toThrow(re); + + expect(() => { + prepareOptions(['**/*.po']); + }).not.toThrow(re); + + expect(() => { + prepareOptions([[]]); + }).toThrow(re); + + expect(() => { + prepareOptions([{}]); + }).toThrow(re); + }); + + test('poSources - if truthy, only string or array of strings', () => { + const re = reOptionError('poSources'); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: null }); + }).not.toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: false }); + }).not.toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: true }); + }).toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: 1 }); + }).toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: '**/*.po' }); + }).not.toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: ['**/*.po'] }); + }).not.toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: {} }); + }).toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: [1] }); + }).toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: [true] }); + }).toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: [[]] }); + }).toThrow(re); + + expect(() => { + prepareOptions({ potSources: potSource, poSources: [{}] }); + }).toThrow(re); + }); + + test('writeFiles - default overwrite for gulp plugin', () => { + expect(prepareOptions({ potSources: '**/*.pot' }, false)).toHaveProperty( + 'writeFiles', + false + ); + }); + + test('srcDir - no newlines', () => { + expect(() => { + prepareOptions({ srcDir: 'some\npath' }); + }).toThrow(reOptionError('srcDir')); + }); + + test('destDir - no newlines', () => { + expect(() => { + prepareOptions({ destDir: 'some\npath' }); + }).toThrow(reOptionError('destDir')); + }); + + test('wrapLength - only positive integer', () => { + const re = reOptionError('wrapLength'); + + expect(() => { + prepareOptions({ wrapLength: -1 }); + }).toThrow(re); + + expect(() => { + prepareOptions({ wrapLength: 0 }); + }).toThrow(re); + }); }); describe('options.js - clean & standardize', () => { - test('potSources - is array of only non-empty strings or Vinyl objects', () => { - // wraps in array - expect( prepareOptions({ potSources: '**/*.pot' }) ).toHaveProperty('potSources', ['**/*.pot']); - expect( prepareOptions({ potSources: vinyl_file }) ).toHaveProperty('potSources', [vinyl_file]); - - // trims strings and removes empty strings - const all = [ - 'first', - ' ', - 'second', - '', - ' \t third\t ', - ]; - const expected = [ - 'first', - 'second', - 'third', - ]; - expect( prepareOptions({ potSources: all }) ).toHaveProperty('potSources', expected); - }); - - test('poSources - is array of only non-empty strings', () => { - // wrap string - expect( prepareOptions({ potSources: potSource, poSources: '**/*.po' }) ).toHaveProperty('poSources', ['**/*.po']); - - // filter non-strings and (trimmed) empty strings - const all = [ - 'first', - ' ', - 'second', - '', - ' \t third\t ', - ]; - const expected = [ - 'first', - 'second', - 'third', - ]; - expect( prepareOptions({ potSources: potSource, poSources: all }) ).toHaveProperty('poSources', expected); - }); - - const directories = [ 'srcDir', 'destDir' ]; - directories.forEach(k => { - test(`${k} - trimmed whitespace`, () => { - expect( prepareOptions({ [k]: ' ' }) ).toHaveProperty(k, ''); - expect( prepareOptions({ [k]: ' some ' }) ).toHaveProperty(k, 'some/'); - }); - - test(`${k} - only forward slashes`, () => { - expect( prepareOptions({ [k]: 'some\\' }) ).toHaveProperty(k, 'some/'); - expect( prepareOptions({ [k]: 'some\\path\\' }) ).toHaveProperty(k, 'some/path/'); - }); - - test(`${k} - only single slashes`, () => { - expect( prepareOptions({ [k]: 'some\\' }) ).toHaveProperty(k, 'some/'); - expect( prepareOptions({ [k]: 'some\\\\' }) ).toHaveProperty(k, 'some/'); - expect( prepareOptions({ [k]: 'some\\/\\/' }) ).toHaveProperty(k, 'some/'); - expect( prepareOptions({ [k]: 'some\\//\\\\' }) ).toHaveProperty(k, 'some/'); - }); - - test(`${k} - without current directory parts`, () => { - expect( prepareOptions({ [k]: './some/' }) ).toHaveProperty(k, 'some/'); - expect( prepareOptions({ [k]: 'some/././.\\path' }) ).toHaveProperty(k, 'some/path/'); - expect( prepareOptions({ [k]: './../some/.\\path' }) ).toHaveProperty(k, '../some/path/'); - }); - - test(`${k} - trailing slash`, () => { - expect( prepareOptions({ [k]: 'some' }) ).toHaveProperty(k, 'some/'); - expect( prepareOptions({ [k]: 'some/' }) ).toHaveProperty(k, 'some/'); - expect( prepareOptions({ [k]: 'some/path' }) ).toHaveProperty(k, 'some/path/'); - }); - - test(`${k} - resolve to minimal path`, () => { - // basic - expect( prepareOptions({ [k]: 'some/../path' }) ).toHaveProperty(k, 'path/'); - // nested - expect( prepareOptions({ [k]: 'some/other/../../path' }) ).toHaveProperty(k, 'path/'); - // concurrent - expect( prepareOptions({ [k]: 'some/other/../path/../' }) ).toHaveProperty(k, 'some/'); - // nested and concurrent - expect( prepareOptions({ [k]: 'some/other/../path/../../' }) ).toHaveProperty(k, ''); - // leave non-solvables - expect( prepareOptions({ [k]: './../some/../path' }) ).toHaveProperty(k, '../path/'); - // empty if result would be '/' - expect( prepareOptions({ [k]: ' ./\\//.\\/.\\ ' }) ).toHaveProperty(k, ''); - // combination - expect( prepareOptions({ [k]: ' ./some\\other/.\\path ' }) ).toHaveProperty(k, 'some/other/path/'); - }); - }); - - test('wrapLength - ceiled integer', () => { - expect( prepareOptions({ wrapLength: 0.001 }) ).toHaveProperty('wrapLength', 1); - expect( prepareOptions({ wrapLength: 1.2 }) ).toHaveProperty('wrapLength', 2); - }); + test('potSources - is array of only non-empty strings or Vinyl objects', () => { + // wraps in array + expect(prepareOptions({ potSources: '**/*.pot' })).toHaveProperty( + 'potSources', + ['**/*.pot'] + ); + expect(prepareOptions({ potSources: vinyl_file })).toHaveProperty( + 'potSources', + [vinyl_file] + ); + + // trims strings and removes empty strings + const all = ['first', ' ', 'second', '', ' \t third\t ']; + const expected = ['first', 'second', 'third']; + expect(prepareOptions({ potSources: all })).toHaveProperty( + 'potSources', + expected + ); + }); + + test('poSources - is array of only non-empty strings', () => { + // wrap string + expect( + prepareOptions({ potSources: potSource, poSources: '**/*.po' }) + ).toHaveProperty('poSources', ['**/*.po']); + + // filter non-strings and (trimmed) empty strings + const all = ['first', ' ', 'second', '', ' \t third\t ']; + const expected = ['first', 'second', 'third']; + expect( + prepareOptions({ potSources: potSource, poSources: all }) + ).toHaveProperty('poSources', expected); + }); + + const directories = ['srcDir', 'destDir']; + directories.forEach((k) => { + test(`${k} - trimmed whitespace`, () => { + expect(prepareOptions({ [k]: ' ' })).toHaveProperty(k, ''); + expect(prepareOptions({ [k]: ' some ' })).toHaveProperty(k, 'some/'); + }); + + test(`${k} - only forward slashes`, () => { + expect(prepareOptions({ [k]: 'some\\' })).toHaveProperty(k, 'some/'); + expect(prepareOptions({ [k]: 'some\\path\\' })).toHaveProperty( + k, + 'some/path/' + ); + }); + + test(`${k} - only single slashes`, () => { + expect(prepareOptions({ [k]: 'some\\' })).toHaveProperty(k, 'some/'); + expect(prepareOptions({ [k]: 'some\\\\' })).toHaveProperty(k, 'some/'); + expect(prepareOptions({ [k]: 'some\\/\\/' })).toHaveProperty(k, 'some/'); + expect(prepareOptions({ [k]: 'some\\//\\\\' })).toHaveProperty( + k, + 'some/' + ); + }); + + test(`${k} - without current directory parts`, () => { + expect(prepareOptions({ [k]: './some/' })).toHaveProperty(k, 'some/'); + expect(prepareOptions({ [k]: 'some/././.\\path' })).toHaveProperty( + k, + 'some/path/' + ); + expect(prepareOptions({ [k]: './../some/.\\path' })).toHaveProperty( + k, + '../some/path/' + ); + }); + + test(`${k} - trailing slash`, () => { + expect(prepareOptions({ [k]: 'some' })).toHaveProperty(k, 'some/'); + expect(prepareOptions({ [k]: 'some/' })).toHaveProperty(k, 'some/'); + expect(prepareOptions({ [k]: 'some/path' })).toHaveProperty( + k, + 'some/path/' + ); + }); + + test(`${k} - resolve to minimal path`, () => { + // basic + expect(prepareOptions({ [k]: 'some/../path' })).toHaveProperty( + k, + 'path/' + ); + // nested + expect(prepareOptions({ [k]: 'some/other/../../path' })).toHaveProperty( + k, + 'path/' + ); + // concurrent + expect(prepareOptions({ [k]: 'some/other/../path/../' })).toHaveProperty( + k, + 'some/' + ); + // nested and concurrent + expect( + prepareOptions({ [k]: 'some/other/../path/../../' }) + ).toHaveProperty(k, ''); + // leave non-solvables + expect(prepareOptions({ [k]: './../some/../path' })).toHaveProperty( + k, + '../path/' + ); + // empty if result would be '/' + expect(prepareOptions({ [k]: ' ./\\//.\\/.\\ ' })).toHaveProperty(k, ''); + // combination + expect(prepareOptions({ [k]: ' ./some\\other/.\\path ' })).toHaveProperty( + k, + 'some/other/path/' + ); + }); + }); + + test('wrapLength - ceiled integer', () => { + expect(prepareOptions({ wrapLength: 0.001 })).toHaveProperty( + 'wrapLength', + 1 + ); + expect(prepareOptions({ wrapLength: 1.2 })).toHaveProperty('wrapLength', 2); + }); }); describe('options.js - logic', () => { - test('provide domain if domainInPOPath and not domainFromPOTPath', () => { - const re = new RegExp('domain should be a non-empty string'); - - expect(() => { - prepareOptions({ domainFromPOTPath: false }); - }).toThrow(re); - - expect(() => { - prepareOptions({ domainFromPOTPath: false, domain: '' }); - }).toThrow(re); - - expect(() => { - prepareOptions({ domainFromPOTPath: false, domain: 'some' }); - }).not.toThrow(re); - - expect(() => { - prepareOptions({ domainFromPOTPath: false, domainInPOPath: true }); - }).toThrow(re); - - expect(() => { - prepareOptions({ domainFromPOTPath: false, domainInPOPath: false }); - }).not.toThrow(re); - }); - - test('writeFiles must be true if returnPOT is false', () => { - const re = new RegExp('If option returnPOT is true, option writeFiles must be true'); - - expect(() => { - prepareOptions({ returnPOT: true, writeFiles: false }); - }).toThrow(re); - - expect(() => { - prepareOptions({ returnPOT: true, writeFiles: true }); - }).not.toThrow(re); - - expect(() => { - prepareOptions({ returnPOT: false, writeFiles: false }); - }).not.toThrow(re); - - expect(() => { - prepareOptions({ returnPOT: false, writeFiles: true }); - }).not.toThrow(re); - }); + test('provide domain if domainInPOPath and not domainFromPOTPath', () => { + const re = new RegExp('domain should be a non-empty string'); + + expect(() => { + prepareOptions({ domainFromPOTPath: false }); + }).toThrow(re); + + expect(() => { + prepareOptions({ domainFromPOTPath: false, domain: '' }); + }).toThrow(re); + + expect(() => { + prepareOptions({ domainFromPOTPath: false, domain: 'some' }); + }).not.toThrow(re); + + expect(() => { + prepareOptions({ domainFromPOTPath: false, domainInPOPath: true }); + }).toThrow(re); + + expect(() => { + prepareOptions({ domainFromPOTPath: false, domainInPOPath: false }); + }).not.toThrow(re); + }); + + test('writeFiles must be true if returnPOT is false', () => { + const re = new RegExp( + 'If option returnPOT is true, option writeFiles must be true' + ); + + expect(() => { + prepareOptions({ returnPOT: true, writeFiles: false }); + }).toThrow(re); + + expect(() => { + prepareOptions({ returnPOT: true, writeFiles: true }); + }).not.toThrow(re); + + expect(() => { + prepareOptions({ returnPOT: false, writeFiles: false }); + }).not.toThrow(re); + + expect(() => { + prepareOptions({ returnPOT: false, writeFiles: true }); + }).not.toThrow(re); + }); }); diff --git a/test/plugin-error.test.js b/test/plugin-error.test.js index 11768da..e18b473 100644 --- a/test/plugin-error.test.js +++ b/test/plugin-error.test.js @@ -4,18 +4,17 @@ const PluginError = require('../src/plugin-error'); const util = require('util'); describe('plugin-error.js', () => { - const error = new PluginError('SOMEERROR', 'AaAaA'); + const error = new PluginError('SOMEERROR', 'AaAaA'); - test('includes message', () => { - expect( error.toString() ).toMatch(/AaaaaError/); - }); + test('includes message', () => { + expect(error.toString()).toMatch(/AaaaaError/); + }); - test('includes category', () => { - expect( error.toString() ).toMatch(/SOMEERROR/); - }); - - test('method used when console.log() calls .toString()', () => { - expect( error.toString() ).toEqual( error[util.inspect.custom]() ); - }); + test('includes category', () => { + expect(error.toString()).toMatch(/SOMEERROR/); + }); + test('method used when console.log() calls .toString()', () => { + expect(error.toString()).toEqual(error[util.inspect.custom]()); + }); }); diff --git a/test/shared.test.js b/test/shared.test.js index 4c82ba2..d08079f 100644 --- a/test/shared.test.js +++ b/test/shared.test.js @@ -7,79 +7,73 @@ const Vinyl = require('vinyl'); const potSource = ['./test/examples/text-domain.pot']; const vinyl_file = new Vinyl({ - contents: Buffer.from('some contents'), - path: 'filename.pot', + contents: Buffer.from('some contents'), + path: 'filename.pot', }); describe('shared.js - resolvePOTFilepaths()', () => { - test('leave poSources empty if multiple potSources', () => { - const re = new RegExp('leave option poSources empty'); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ poSources: '' }) - ); - }).not.toThrow(re); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ poSources: '*.po' }) - ); - }).toThrow(re); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ poSources: ['*.po'] }) - ); - }).toThrow(re); + test('leave poSources empty if multiple potSources', () => { + const re = new RegExp('leave option poSources empty'); + expect(() => { + resolvePOTFilepaths(prepareOptions({ poSources: '' })); + }).not.toThrow(re); + expect(() => { + resolvePOTFilepaths(prepareOptions({ poSources: '*.po' })); + }).toThrow(re); + expect(() => { + resolvePOTFilepaths(prepareOptions({ poSources: ['*.po'] })); + }).toThrow(re); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ potSources: potSource, poSources: '' }) - ); - }).not.toThrow(re); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ potSources: potSource, poSources: '*.po' }) - ); - }).not.toThrow(re); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ potSources: potSource, poSources: ['*.po'] }) - ); - }).not.toThrow(re); - }); + expect(() => { + resolvePOTFilepaths( + prepareOptions({ potSources: potSource, poSources: '' }) + ); + }).not.toThrow(re); + expect(() => { + resolvePOTFilepaths( + prepareOptions({ potSources: potSource, poSources: '*.po' }) + ); + }).not.toThrow(re); + expect(() => { + resolvePOTFilepaths( + prepareOptions({ potSources: potSource, poSources: ['*.po'] }) + ); + }).not.toThrow(re); + }); - test('potSources has files to process', () => { - const re = new RegExp('No POT files found'); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ potSources: [] }) - ); - }).toThrow(re); - expect(() => { - resolvePOTFilepaths( - prepareOptions({ potSources: ['NON_EXISTING.pot'] }) - ); - }).toThrow(re); - }); + test('potSources has files to process', () => { + const re = new RegExp('No POT files found'); + expect(() => { + resolvePOTFilepaths(prepareOptions({ potSources: [] })); + }).toThrow(re); + expect(() => { + resolvePOTFilepaths(prepareOptions({ potSources: ['NON_EXISTING.pot'] })); + }).toThrow(re); + }); - test('store POT filenames', () => { - let options = null; - expect(() => { - options = resolvePOTFilepaths({ potSources: [vinyl_file] }); - }).not.toThrow(); - expect(options).not.toBeNull(); - expect(options).toHaveProperty('_potFilenames', [vinyl_file.path]); + test('store POT filenames', () => { + let options = null; + expect(() => { + options = resolvePOTFilepaths({ potSources: [vinyl_file] }); + }).not.toThrow(); + expect(options).not.toBeNull(); + expect(options).toHaveProperty('_potFilenames', [vinyl_file.path]); - options = null; - expect(() => { - options = resolvePOTFilepaths({ potSources: ['test/examples/text-domain.pot'] }); - }).not.toThrow(); - expect(options).not.toBeNull(); - expect(options).toHaveProperty('_potFilenames', ['test/examples/text-domain.pot']); - }); + options = null; + expect(() => { + options = resolvePOTFilepaths({ + potSources: ['test/examples/text-domain.pot'], + }); + }).not.toThrow(); + expect(options).not.toBeNull(); + expect(options).toHaveProperty('_potFilenames', [ + 'test/examples/text-domain.pot', + ]); + }); - /** - * logResults() is tested in sync.test.js, inside tests: - * - 'manual empty PO array' - * - 'extras - auto domain PO - write - content optionals' - */ + /** + * logResults() is tested in sync.test.js, inside tests: + * - 'manual empty PO array' + * - 'extras - auto domain PO - write - content optionals' + */ }); diff --git a/test/sync.test.js b/test/sync.test.js index 4dcc3bd..9b305fa 100644 --- a/test/sync.test.js +++ b/test/sync.test.js @@ -26,546 +26,527 @@ const expected_po_no_domain = readFileSync(`${expected_dir}nl_NL.po`); * @return {void} */ function clearOutputFolder() { - let files = matchedSync([ - `${test_dir}*`, - ]); - files.sort((a, b) => { - const a_len = a.split(/\//); - const b_len = b.split(/\//); - return b_len - a_len; - }); - for (const file of files) { - rmSync(file, {recursive: true}); - } + let files = matchedSync([`${test_dir}*`]); + files.sort((a, b) => { + const a_len = a.split(/\//); + const b_len = b.split(/\//); + return b_len - a_len; + }); + for (const file of files) { + rmSync(file, { recursive: true }); + } } beforeAll(() => { - clearOutputFolder(); + clearOutputFolder(); }); afterAll(() => { - clearOutputFolder(); + clearOutputFolder(); }); describe('sync.js - single POT', () => { - - let folder_i = 0; - - test('auto domain PO - no write', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: false, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - - // Check contents - expect( - result[0].contents - .equals(expected_po_domain) - ).toBe(true); - }); - - test('auto no-domain PO - no write', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - domainInPOPath: false, - writeFiles: false, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - - // Check contents - expect( - result[0].contents - .equals(expected_po_no_domain) - ).toBe(true); - }); - - test('manual multiple PO - no write', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - poSources: [ poSources ], - srcDir: input_dir, - writeFiles: false, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(2); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - expect(result[1]).toBeInstanceOf(Vinyl); - expect(result[1].isBuffer()).toBe(true); - expect(result[1].path).toEqual('text-domain-nl_NL.po'); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - - // Check contents - expect( - result[0].contents - .equals(expected_po_no_domain) - ).toBe(true); - expect( - result[1].contents - .equals(expected_po_domain) - ).toBe(true); - }); - - test('auto domain PO - write', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: true, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents of file - expect( - readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - }); - - test('auto no-domain PO - write', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - domainInPOPath: false, - writeFiles: true, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/nl_NL.po` - ]); - - // Check contents of file - expect( - readFileSync(`${folder_path}/nl_NL.po`) - .equals(expected_po_no_domain) - ).toBe(true); - }); - - test('manual multiple PO - write', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - poSources: [ poSources ], - srcDir: input_dir, - writeFiles: true, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(2); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('nl_NL.po'); - expect(result[1]).toBeInstanceOf(Vinyl); - expect(result[1].isBuffer()).toBe(true); - expect(result[1].path).toEqual('text-domain-nl_NL.po'); - - // Check if files exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toEqual([ - `${folder_path}/nl_NL.po`, - `${folder_path}/text-domain-nl_NL.po`, - ]); - - // Check contents of files - expect( - readFileSync(`${folder_path}/nl_NL.po`) - .equals(expected_po_no_domain) - ).toBe(true); - expect( - readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - }); - - test('manual empty PO array', () => { - // Mock the console.log function - const consoleSpy = jest.spyOn(console, 'log') - .mockName('console.log') - .mockImplementation((...args) => { - return args.map(v => String(v).trim().replaceAll(/\s+/g, ' ')).join(' '); - }); - - const options = { - ...testOptions, - potSources: [ potSource ], - poSources: [], - srcDir: input_dir, - writeFiles: false, - logResults: true, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(0); - - // Check if results were logged - expect(consoleSpy).toHaveBeenCalledTimes(4); - const logs = consoleSpy.mock.results.slice(); - expect(logs[0].value + logs[3].value).toEqual(''); - expect(logs[1].value).toMatch(new RegExp('■ ' + potSource.split('/').slice(-1)[0])); - expect(logs[2].value).toMatch(new RegExp('No PO files found.')); - - // Restore the console.log function - consoleSpy.mockRestore(); - }); - - test('auto domain PO - write - return POT', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - returnPOT: true, - writeFiles: true, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(relative(result[0].path, potSource)).toEqual(''); - - // Check contents - expect( - result[0].contents - .equals(pot_source_buffer) - ).toBe(true); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents of file - expect( - readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - }); - - test('auto domain PO - write - input POT Vinyl', () => { - const options = { - ...testOptions, - potSources: new Vinyl({ - contents: pot_source_buffer, - path: potSource, - }), - srcDir: input_dir, - writeFiles: false, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check contents - expect( - result[0].contents - .equals(expected_po_domain) - ).toBe(true); - }); - - test('auto domain PO - write - return POT - input POT Vinyl', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: new Vinyl({ - contents: pot_source_buffer, - path: potSource, - }), - srcDir: input_dir, - returnPOT: true, - writeFiles: true, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(relative(result[0].path, potSource)).toEqual(''); - - // Check contents - expect( - result[0].contents - .equals(pot_source_buffer) - ).toBe(true); - - // Check if file exist - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents of file - expect( - readFileSync(`${folder_path}/text-domain-nl_NL.po`) - .equals(expected_po_domain) - ).toBe(true); - }); - - test('extras - auto domain PO - write - content optionals', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - - // Mock the console.log function - const consoleSpy = jest.spyOn(console, 'log') - .mockName('console.log') - .mockImplementation((...args) => { - return args.map(v => String(v).trim().replaceAll(/\s+/g, ' ')).join(' '); - }); - - const options = { - ...testOptions, - potSources: [ potSource ], - srcDir: input_dir, - writeFiles: true, - destDir: folder_path, - logResults: true, - appendNonIncludedFromPO: false, - includePORevisionDate: true, - includeGenerator: true, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(1); - expect(result[0]).toBeInstanceOf(Vinyl); - expect(result[0].isBuffer()).toBe(true); - expect(result[0].path).toEqual('text-domain-nl_NL.po'); - - // Check if folder and file exist - expect(existsSync(folder_path)).toBe(true); - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(1); - expect(files).toEqual([ - `${folder_path}/text-domain-nl_NL.po` - ]); - - // Check contents - const result_string_content = result[0].contents.toString(); - expect(result_string_content) - .not.toMatch(/^# DEPRECATED$/m); - expect(result_string_content) - .toMatch(/^"PO-Revision-Date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}\+0000\\n"$/m); - expect(result_string_content) - .toMatch(/^"X-Generator: fill-pot-po\/\d+\.\d+\.\d+\\n"$/m); - - // Check if results were logged - expect(consoleSpy).toHaveBeenCalledTimes(4); - const logs = consoleSpy.mock.results.slice(); - expect(logs[0].value + logs[3].value).toEqual(''); - expect(logs[1].value) - .toMatch(new RegExp('■ ' + potSource.split('/').slice(-1)[0])); - expect(logs[2].value) - .toMatch(new RegExp(`${input_dir}text-domain-nl_NL.po —► ${folder_path}/text-domain-nl_NL.po`)); - - // Restore the console.log function - consoleSpy.mockRestore(); - }); - - test('extras - manual domain - no srcDir - no write', () => { - folder_i++; - let folder_path = `${test_dir}${folder_i}`; - if (!existsSync(folder_path)) { - mkdirSync(folder_path, {recursive: true}); - } - - const options = { - ...testOptions, - potSources: [ potSource ], - domainFromPOTPath: false, - domain: 'text-domain', - writeFiles: false, - destDir: folder_path, - }; - - // Errorless execution - let result; - expect(() => { - result = fillPotPo(options); - }).not.toThrow(); - - // Check returned array - expect(result).toHaveLength(0); - - // Check that no files were created - const files = matchedSync([`${folder_path}/*`]); - expect(files).toHaveLength(0); - }); + let folder_i = 0; + + test('auto domain PO - no write', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: false, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + + // Check contents + expect(result[0].contents.equals(expected_po_domain)).toBe(true); + }); + + test('auto no-domain PO - no write', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + domainInPOPath: false, + writeFiles: false, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + + // Check contents + expect(result[0].contents.equals(expected_po_no_domain)).toBe(true); + }); + + test('manual multiple PO - no write', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + poSources: [poSources], + srcDir: input_dir, + writeFiles: false, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(2); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + expect(result[1]).toBeInstanceOf(Vinyl); + expect(result[1].isBuffer()).toBe(true); + expect(result[1].path).toEqual('text-domain-nl_NL.po'); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + + // Check contents + expect(result[0].contents.equals(expected_po_no_domain)).toBe(true); + expect(result[1].contents.equals(expected_po_domain)).toBe(true); + }); + + test('auto domain PO - write', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: true, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents of file + expect( + readFileSync(`${folder_path}/text-domain-nl_NL.po`).equals( + expected_po_domain + ) + ).toBe(true); + }); + + test('auto no-domain PO - write', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + domainInPOPath: false, + writeFiles: true, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/nl_NL.po`]); + + // Check contents of file + expect( + readFileSync(`${folder_path}/nl_NL.po`).equals(expected_po_no_domain) + ).toBe(true); + }); + + test('manual multiple PO - write', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + poSources: [poSources], + srcDir: input_dir, + writeFiles: true, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(2); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('nl_NL.po'); + expect(result[1]).toBeInstanceOf(Vinyl); + expect(result[1].isBuffer()).toBe(true); + expect(result[1].path).toEqual('text-domain-nl_NL.po'); + + // Check if files exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toEqual([ + `${folder_path}/nl_NL.po`, + `${folder_path}/text-domain-nl_NL.po`, + ]); + + // Check contents of files + expect( + readFileSync(`${folder_path}/nl_NL.po`).equals(expected_po_no_domain) + ).toBe(true); + expect( + readFileSync(`${folder_path}/text-domain-nl_NL.po`).equals( + expected_po_domain + ) + ).toBe(true); + }); + + test('manual empty PO array', () => { + // Mock the console.log function + const consoleSpy = jest + .spyOn(console, 'log') + .mockName('console.log') + .mockImplementation((...args) => { + return args + .map((v) => String(v).trim().replaceAll(/\s+/g, ' ')) + .join(' '); + }); + + const options = { + ...testOptions, + potSources: [potSource], + poSources: [], + srcDir: input_dir, + writeFiles: false, + logResults: true, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(0); + + // Check if results were logged + expect(consoleSpy).toHaveBeenCalledTimes(4); + const logs = consoleSpy.mock.results.slice(); + expect(logs[0].value + logs[3].value).toEqual(''); + expect(logs[1].value).toMatch( + new RegExp('■ ' + potSource.split('/').slice(-1)[0]) + ); + expect(logs[2].value).toMatch(new RegExp('No PO files found.')); + + // Restore the console.log function + consoleSpy.mockRestore(); + }); + + test('auto domain PO - write - return POT', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + returnPOT: true, + writeFiles: true, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(relative(result[0].path, potSource)).toEqual(''); + + // Check contents + expect(result[0].contents.equals(pot_source_buffer)).toBe(true); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents of file + expect( + readFileSync(`${folder_path}/text-domain-nl_NL.po`).equals( + expected_po_domain + ) + ).toBe(true); + }); + + test('auto domain PO - write - input POT Vinyl', () => { + const options = { + ...testOptions, + potSources: new Vinyl({ + contents: pot_source_buffer, + path: potSource, + }), + srcDir: input_dir, + writeFiles: false, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check contents + expect(result[0].contents.equals(expected_po_domain)).toBe(true); + }); + + test('auto domain PO - write - return POT - input POT Vinyl', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: new Vinyl({ + contents: pot_source_buffer, + path: potSource, + }), + srcDir: input_dir, + returnPOT: true, + writeFiles: true, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(relative(result[0].path, potSource)).toEqual(''); + + // Check contents + expect(result[0].contents.equals(pot_source_buffer)).toBe(true); + + // Check if file exist + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents of file + expect( + readFileSync(`${folder_path}/text-domain-nl_NL.po`).equals( + expected_po_domain + ) + ).toBe(true); + }); + + test('extras - auto domain PO - write - content optionals', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + + // Mock the console.log function + const consoleSpy = jest + .spyOn(console, 'log') + .mockName('console.log') + .mockImplementation((...args) => { + return args + .map((v) => String(v).trim().replaceAll(/\s+/g, ' ')) + .join(' '); + }); + + const options = { + ...testOptions, + potSources: [potSource], + srcDir: input_dir, + writeFiles: true, + destDir: folder_path, + logResults: true, + appendNonIncludedFromPO: false, + includePORevisionDate: true, + includeGenerator: true, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Vinyl); + expect(result[0].isBuffer()).toBe(true); + expect(result[0].path).toEqual('text-domain-nl_NL.po'); + + // Check if folder and file exist + expect(existsSync(folder_path)).toBe(true); + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(1); + expect(files).toEqual([`${folder_path}/text-domain-nl_NL.po`]); + + // Check contents + const result_string_content = result[0].contents.toString(); + expect(result_string_content).not.toMatch(/^# DEPRECATED$/m); + expect(result_string_content).toMatch( + /^"PO-Revision-Date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}\+0000\\n"$/m + ); + expect(result_string_content).toMatch( + /^"X-Generator: fill-pot-po\/\d+\.\d+\.\d+\\n"$/m + ); + + // Check if results were logged + expect(consoleSpy).toHaveBeenCalledTimes(4); + const logs = consoleSpy.mock.results.slice(); + expect(logs[0].value + logs[3].value).toEqual(''); + expect(logs[1].value).toMatch( + new RegExp('■ ' + potSource.split('/').slice(-1)[0]) + ); + expect(logs[2].value).toMatch( + new RegExp( + `${input_dir}text-domain-nl_NL.po —► ${folder_path}/text-domain-nl_NL.po` + ) + ); + + // Restore the console.log function + consoleSpy.mockRestore(); + }); + + test('extras - manual domain - no srcDir - no write', () => { + folder_i++; + let folder_path = `${test_dir}${folder_i}`; + if (!existsSync(folder_path)) { + mkdirSync(folder_path, { recursive: true }); + } + + const options = { + ...testOptions, + potSources: [potSource], + domainFromPOTPath: false, + domain: 'text-domain', + writeFiles: false, + destDir: folder_path, + }; + + // Errorless execution + let result; + expect(() => { + result = fillPotPo(options); + }).not.toThrow(); + + // Check returned array + expect(result).toHaveLength(0); + + // Check that no files were created + const files = matchedSync([`${folder_path}/*`]); + expect(files).toHaveLength(0); + }); }); // TODO? potSources: Vinyl or Array-of diff --git a/test/utils.test.js b/test/utils.test.js index 04ee175..8cc17ce 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -1,23 +1,23 @@ 'use strict'; const { - isArray, - isObject, - isString, - isBool, - isArrayOfStrings, - isArrayOfVinyls, - pathLineSort + isArray, + isObject, + isString, + isBool, + isArrayOfStrings, + isArrayOfVinyls, + pathLineSort, } = require('../src/utils'); const Vinyl = require('vinyl'); const vinyl_file = new Vinyl({ - contents: Buffer.from('some contents'), - path: 'filename.pot', + contents: Buffer.from('some contents'), + path: 'filename.pot', }); class SomeClass { - constructor() {} + constructor() {} } function someFunction() {} @@ -29,43 +29,26 @@ function someFunction() {} */ /* eslint-disable-next-line no-unused-vars */ function generatePaths() { - const dirs = [ - '', - 'some/other/path/', - 'some/other/folder/', - 'some/', - 'some/path/', - 'some/quoted/', - ]; - - const files = [ - 'file', - 'file.ext', - 'file.oth', - 'different_file.ext', - '.ext', - ]; - - const lines = [ - '', - ':10', - ':20', - ':100', - ]; - - let filepaths = [].concat( - ...dirs.map(d => - [d].concat( - ...files.map(f => - lines.map(l => - `${d}${f}${l}` - ) - ) - ) - ) - ); - - return filepaths; + const dirs = [ + '', + 'some/other/path/', + 'some/other/folder/', + 'some/', + 'some/path/', + 'some/quoted/', + ]; + + const files = ['file', 'file.ext', 'file.oth', 'different_file.ext', '.ext']; + + const lines = ['', ':10', ':20', ':100']; + + let filepaths = [].concat( + ...dirs.map((d) => + [d].concat(...files.map((f) => lines.map((l) => `${d}${f}${l}`))) + ) + ); + + return filepaths; } /** @@ -76,748 +59,765 @@ function generatePaths() { */ /* eslint-disable-next-line no-unused-vars */ function shuffle(array) { - let currentIndex = array.length, randomIndex; - - // While there remain elements to shuffle. - while (currentIndex != 0) { - - // Pick a remaining element. - randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex--; - - // And swap it with the current element. - [ - array[currentIndex], array[randomIndex] - ] = [ - array[randomIndex], array[currentIndex] - ]; - } - - return array; + let currentIndex = array.length, + randomIndex; + + // While there remain elements to shuffle. + while (currentIndex != 0) { + // Pick a remaining element. + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + + // And swap it with the current element. + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], + array[currentIndex], + ]; + } + + return array; } describe('utils.js - isArray', () => { - test('false on null', () => { - expect( isArray(null) ).toEqual(false); - }); + test('false on null', () => { + expect(isArray(null)).toEqual(false); + }); - test('false on undefined', () => { - expect( isArray(undefined) ).toEqual(false); - }); + test('false on undefined', () => { + expect(isArray(undefined)).toEqual(false); + }); - test('false on number', () => { - expect( isArray(1) ).toEqual(false); - }); + test('false on number', () => { + expect(isArray(1)).toEqual(false); + }); - test('false on boolean true', () => { - expect( isArray(true) ).toEqual(false); - }); + test('false on boolean true', () => { + expect(isArray(true)).toEqual(false); + }); - test('false on boolean false', () => { - expect( isArray(false) ).toEqual(false); - }); + test('false on boolean false', () => { + expect(isArray(false)).toEqual(false); + }); - test('false on string', () => { - expect( isArray('string') ).toEqual(false); - }); + test('false on string', () => { + expect(isArray('string')).toEqual(false); + }); - test('false on class instance', () => { - expect( isArray(new SomeClass()) ).toEqual(false); - }); + test('false on class instance', () => { + expect(isArray(new SomeClass())).toEqual(false); + }); - test('false on class', () => { - expect( isArray(SomeClass) ).toEqual(false); - }); + test('false on class', () => { + expect(isArray(SomeClass)).toEqual(false); + }); - test('false on arrow function', () => { - expect( isArray(() => {}) ).toEqual(false); - }); + test('false on arrow function', () => { + expect(isArray(() => {})).toEqual(false); + }); - test('false on function', () => { - expect( isArray(someFunction) ).toEqual(false); - }); + test('false on function', () => { + expect(isArray(someFunction)).toEqual(false); + }); - test('false on object', () => { - expect( isArray({}) ).toEqual(false); - }); + test('false on object', () => { + expect(isArray({})).toEqual(false); + }); - test('true on array', () => { - expect( isArray([]) ).toEqual(true); - }); + test('true on array', () => { + expect(isArray([])).toEqual(true); + }); }); describe('utils.js - isObject', () => { - test('false on null', () => { - expect( isObject(null) ).toEqual(false); - }); + test('false on null', () => { + expect(isObject(null)).toEqual(false); + }); - test('false on undefined', () => { - expect( isObject(undefined) ).toEqual(false); - }); + test('false on undefined', () => { + expect(isObject(undefined)).toEqual(false); + }); - test('false on number', () => { - expect( isObject(1) ).toEqual(false); - }); + test('false on number', () => { + expect(isObject(1)).toEqual(false); + }); - test('false on boolean true', () => { - expect( isObject(true) ).toEqual(false); - }); + test('false on boolean true', () => { + expect(isObject(true)).toEqual(false); + }); - test('false on boolean false', () => { - expect( isObject(false) ).toEqual(false); - }); + test('false on boolean false', () => { + expect(isObject(false)).toEqual(false); + }); - test('false on string', () => { - expect( isObject('string') ).toEqual(false); - }); + test('false on string', () => { + expect(isObject('string')).toEqual(false); + }); - test('true on class instance', () => { - expect( isObject(new SomeClass()) ).toEqual(true); - }); + test('true on class instance', () => { + expect(isObject(new SomeClass())).toEqual(true); + }); - test('false on class', () => { - expect( isObject(SomeClass) ).toEqual(false); - }); + test('false on class', () => { + expect(isObject(SomeClass)).toEqual(false); + }); - test('false on arrow function', () => { - expect( isObject(() => {}) ).toEqual(false); - }); + test('false on arrow function', () => { + expect(isObject(() => {})).toEqual(false); + }); - test('false on function', () => { - expect( isObject(someFunction) ).toEqual(false); - }); + test('false on function', () => { + expect(isObject(someFunction)).toEqual(false); + }); - test('true on object', () => { - expect( isObject({}) ).toEqual(true); - }); + test('true on object', () => { + expect(isObject({})).toEqual(true); + }); - test('false on array', () => { - expect( isObject([]) ).toEqual(false); - }); + test('false on array', () => { + expect(isObject([])).toEqual(false); + }); }); describe('utils.js - isString', () => { - test('false on null', () => { - expect( isString(null) ).toEqual(false); - }); + test('false on null', () => { + expect(isString(null)).toEqual(false); + }); - test('false on undefined', () => { - expect( isString(undefined) ).toEqual(false); - }); + test('false on undefined', () => { + expect(isString(undefined)).toEqual(false); + }); - test('false on number', () => { - expect( isString(1) ).toEqual(false); - }); + test('false on number', () => { + expect(isString(1)).toEqual(false); + }); - test('false on boolean true', () => { - expect( isString(true) ).toEqual(false); - }); + test('false on boolean true', () => { + expect(isString(true)).toEqual(false); + }); - test('false on boolean false', () => { - expect( isString(false) ).toEqual(false); - }); + test('false on boolean false', () => { + expect(isString(false)).toEqual(false); + }); - test('true on string', () => { - expect( isString('string') ).toEqual(true); - }); + test('true on string', () => { + expect(isString('string')).toEqual(true); + }); - test('false on class instance', () => { - expect( isString(new SomeClass()) ).toEqual(false); - }); + test('false on class instance', () => { + expect(isString(new SomeClass())).toEqual(false); + }); - test('false on class', () => { - expect( isString(SomeClass) ).toEqual(false); - }); + test('false on class', () => { + expect(isString(SomeClass)).toEqual(false); + }); - test('false on arrow function', () => { - expect( isString(() => {}) ).toEqual(false); - }); + test('false on arrow function', () => { + expect(isString(() => {})).toEqual(false); + }); - test('false on function', () => { - expect( isString(someFunction) ).toEqual(false); - }); + test('false on function', () => { + expect(isString(someFunction)).toEqual(false); + }); - test('false on object', () => { - expect( isString({}) ).toEqual(false); - }); + test('false on object', () => { + expect(isString({})).toEqual(false); + }); - test('false on array', () => { - expect( isString([]) ).toEqual(false); - }); + test('false on array', () => { + expect(isString([])).toEqual(false); + }); }); describe('utils.js - isBool', () => { - test('false on null', () => { - expect( isBool(null) ).toEqual(false); - }); + test('false on null', () => { + expect(isBool(null)).toEqual(false); + }); - test('false on undefined', () => { - expect( isBool(undefined) ).toEqual(false); - }); + test('false on undefined', () => { + expect(isBool(undefined)).toEqual(false); + }); - test('false on number', () => { - expect( isBool(1) ).toEqual(false); - }); + test('false on number', () => { + expect(isBool(1)).toEqual(false); + }); - test('true on boolean true', () => { - expect( isBool(true) ).toEqual(true); - }); + test('true on boolean true', () => { + expect(isBool(true)).toEqual(true); + }); - test('true on boolean false', () => { - expect( isBool(false) ).toEqual(true); - }); + test('true on boolean false', () => { + expect(isBool(false)).toEqual(true); + }); - test('false on string', () => { - expect( isBool('string') ).toEqual(false); - }); + test('false on string', () => { + expect(isBool('string')).toEqual(false); + }); - test('false on class instance', () => { - expect( isBool(new SomeClass()) ).toEqual(false); - }); + test('false on class instance', () => { + expect(isBool(new SomeClass())).toEqual(false); + }); - test('false on class', () => { - expect( isBool(SomeClass) ).toEqual(false); - }); + test('false on class', () => { + expect(isBool(SomeClass)).toEqual(false); + }); - test('false on arrow function', () => { - expect( isBool(() => {}) ).toEqual(false); - }); + test('false on arrow function', () => { + expect(isBool(() => {})).toEqual(false); + }); - test('false on function', () => { - expect( isBool(someFunction) ).toEqual(false); - }); + test('false on function', () => { + expect(isBool(someFunction)).toEqual(false); + }); - test('false on object', () => { - expect( isBool({}) ).toEqual(false); - }); + test('false on object', () => { + expect(isBool({})).toEqual(false); + }); - test('false on array', () => { - expect( isBool([]) ).toEqual(false); - }); + test('false on array', () => { + expect(isBool([])).toEqual(false); + }); }); describe('utils.js - isArrayOfStrings', () => { - test('false on null', () => { - expect( isArrayOfStrings(null) ).toEqual(false); - }); + test('false on null', () => { + expect(isArrayOfStrings(null)).toEqual(false); + }); - test('false on undefined', () => { - expect( isArrayOfStrings(undefined) ).toEqual(false); - }); + test('false on undefined', () => { + expect(isArrayOfStrings(undefined)).toEqual(false); + }); - test('false on number', () => { - expect( isArrayOfStrings(1) ).toEqual(false); - }); + test('false on number', () => { + expect(isArrayOfStrings(1)).toEqual(false); + }); - test('false on boolean true', () => { - expect( isArrayOfStrings(true) ).toEqual(false); - }); + test('false on boolean true', () => { + expect(isArrayOfStrings(true)).toEqual(false); + }); - test('false on boolean false', () => { - expect( isArrayOfStrings(false) ).toEqual(false); - }); + test('false on boolean false', () => { + expect(isArrayOfStrings(false)).toEqual(false); + }); - test('false on string', () => { - expect( isArrayOfStrings('string') ).toEqual(false); - }); + test('false on string', () => { + expect(isArrayOfStrings('string')).toEqual(false); + }); - test('false on class instance', () => { - expect( isArrayOfStrings(new SomeClass()) ).toEqual(false); - }); + test('false on class instance', () => { + expect(isArrayOfStrings(new SomeClass())).toEqual(false); + }); - test('false on class', () => { - expect( isArrayOfStrings(SomeClass) ).toEqual(false); - }); + test('false on class', () => { + expect(isArrayOfStrings(SomeClass)).toEqual(false); + }); - test('false on arrow function', () => { - expect( isArrayOfStrings(() => {}) ).toEqual(false); - }); + test('false on arrow function', () => { + expect(isArrayOfStrings(() => {})).toEqual(false); + }); - test('false on function', () => { - expect( isArrayOfStrings(someFunction) ).toEqual(false); - }); + test('false on function', () => { + expect(isArrayOfStrings(someFunction)).toEqual(false); + }); - test('false on object', () => { - expect( isArrayOfStrings({}) ).toEqual(false); - }); + test('false on object', () => { + expect(isArrayOfStrings({})).toEqual(false); + }); - test('true on array', () => { - expect( isArrayOfStrings([]) ).toEqual(true); - }); + test('true on array', () => { + expect(isArrayOfStrings([])).toEqual(true); + }); - test('false on array w/ null', () => { - expect( isArrayOfStrings([null]) ).toEqual(false); - }); + test('false on array w/ null', () => { + expect(isArrayOfStrings([null])).toEqual(false); + }); - test('false on array w/ undefined', () => { - expect( isArrayOfStrings([undefined]) ).toEqual(false); - }); + test('false on array w/ undefined', () => { + expect(isArrayOfStrings([undefined])).toEqual(false); + }); - test('false on array w/ boolean true', () => { - expect( isArrayOfStrings([true]) ).toEqual(false); - }); + test('false on array w/ boolean true', () => { + expect(isArrayOfStrings([true])).toEqual(false); + }); - test('false on array w/ boolean false', () => { - expect( isArrayOfStrings([false]) ).toEqual(false); - }); + test('false on array w/ boolean false', () => { + expect(isArrayOfStrings([false])).toEqual(false); + }); - test('false on array w/ number', () => { - expect( isArrayOfStrings([1]) ).toEqual(false); - }); + test('false on array w/ number', () => { + expect(isArrayOfStrings([1])).toEqual(false); + }); - test('true on array w/ string', () => { - expect( isArrayOfStrings(['']) ).toEqual(true); - }); + test('true on array w/ string', () => { + expect(isArrayOfStrings([''])).toEqual(true); + }); - test('false on array w/ class instance', () => { - expect( isArrayOfStrings([new SomeClass()]) ).toEqual(false); - }); + test('false on array w/ class instance', () => { + expect(isArrayOfStrings([new SomeClass()])).toEqual(false); + }); - test('false on array w/ class', () => { - expect( isArrayOfStrings([SomeClass]) ).toEqual(false); - }); + test('false on array w/ class', () => { + expect(isArrayOfStrings([SomeClass])).toEqual(false); + }); - test('false on array w/ arrow function', () => { - expect( isArrayOfStrings([() => {}]) ).toEqual(false); - }); + test('false on array w/ arrow function', () => { + expect(isArrayOfStrings([() => {}])).toEqual(false); + }); - test('false on array w/ function', () => { - expect( isArrayOfStrings([someFunction]) ).toEqual(false); - }); + test('false on array w/ function', () => { + expect(isArrayOfStrings([someFunction])).toEqual(false); + }); - test('false on array w/ object', () => { - expect( isArrayOfStrings([{}]) ).toEqual(false); - }); + test('false on array w/ object', () => { + expect(isArrayOfStrings([{}])).toEqual(false); + }); - test('false on array w/ array', () => { - expect( isArrayOfStrings([[]]) ).toEqual(false); - }); + test('false on array w/ array', () => { + expect(isArrayOfStrings([[]])).toEqual(false); + }); }); describe('utils.js - isArrayOfVinyls', () => { - test('false on null', () => { - expect( isArrayOfVinyls(null) ).toEqual(false); - }); + test('false on null', () => { + expect(isArrayOfVinyls(null)).toEqual(false); + }); - test('false on undefined', () => { - expect( isArrayOfVinyls(undefined) ).toEqual(false); - }); + test('false on undefined', () => { + expect(isArrayOfVinyls(undefined)).toEqual(false); + }); - test('false on number', () => { - expect( isArrayOfVinyls(1) ).toEqual(false); - }); + test('false on number', () => { + expect(isArrayOfVinyls(1)).toEqual(false); + }); - test('false on boolean true', () => { - expect( isArrayOfVinyls(true) ).toEqual(false); - }); + test('false on boolean true', () => { + expect(isArrayOfVinyls(true)).toEqual(false); + }); - test('false on boolean false', () => { - expect( isArrayOfVinyls(false) ).toEqual(false); - }); + test('false on boolean false', () => { + expect(isArrayOfVinyls(false)).toEqual(false); + }); - test('false on string', () => { - expect( isArrayOfVinyls('string') ).toEqual(false); - }); + test('false on string', () => { + expect(isArrayOfVinyls('string')).toEqual(false); + }); - test('false on class instance', () => { - expect( isArrayOfVinyls(new SomeClass()) ).toEqual(false); - }); + test('false on class instance', () => { + expect(isArrayOfVinyls(new SomeClass())).toEqual(false); + }); - test('false on class', () => { - expect( isArrayOfVinyls(SomeClass) ).toEqual(false); - }); + test('false on class', () => { + expect(isArrayOfVinyls(SomeClass)).toEqual(false); + }); - test('false on arrow function', () => { - expect( isArrayOfVinyls(() => {}) ).toEqual(false); - }); + test('false on arrow function', () => { + expect(isArrayOfVinyls(() => {})).toEqual(false); + }); - test('false on function', () => { - expect( isArrayOfVinyls(someFunction) ).toEqual(false); - }); + test('false on function', () => { + expect(isArrayOfVinyls(someFunction)).toEqual(false); + }); - test('false on object', () => { - expect( isArrayOfVinyls({}) ).toEqual(false); - }); + test('false on object', () => { + expect(isArrayOfVinyls({})).toEqual(false); + }); - test('true on array', () => { - expect( isArrayOfVinyls([]) ).toEqual(true); - }); + test('true on array', () => { + expect(isArrayOfVinyls([])).toEqual(true); + }); - test('false on array w/ null', () => { - expect( isArrayOfVinyls([null]) ).toEqual(false); - }); + test('false on array w/ null', () => { + expect(isArrayOfVinyls([null])).toEqual(false); + }); - test('false on array w/ undefined', () => { - expect( isArrayOfVinyls([undefined]) ).toEqual(false); - }); + test('false on array w/ undefined', () => { + expect(isArrayOfVinyls([undefined])).toEqual(false); + }); - test('false on array w/ boolean true', () => { - expect( isArrayOfVinyls([true]) ).toEqual(false); - }); + test('false on array w/ boolean true', () => { + expect(isArrayOfVinyls([true])).toEqual(false); + }); - test('false on array w/ boolean false', () => { - expect( isArrayOfVinyls([false]) ).toEqual(false); - }); + test('false on array w/ boolean false', () => { + expect(isArrayOfVinyls([false])).toEqual(false); + }); - test('false on array w/ number', () => { - expect( isArrayOfVinyls([1]) ).toEqual(false); - }); + test('false on array w/ number', () => { + expect(isArrayOfVinyls([1])).toEqual(false); + }); - test('false on array w/ string', () => { - expect( isArrayOfVinyls(['']) ).toEqual(false); - }); + test('false on array w/ string', () => { + expect(isArrayOfVinyls([''])).toEqual(false); + }); - test('true on array w/ Vinyls', () => { - expect( isArrayOfVinyls([vinyl_file]) ).toEqual(true); - }); + test('true on array w/ Vinyls', () => { + expect(isArrayOfVinyls([vinyl_file])).toEqual(true); + }); - test('false on array w/ class instance', () => { - expect( isArrayOfVinyls([new SomeClass()]) ).toEqual(false); - }); + test('false on array w/ class instance', () => { + expect(isArrayOfVinyls([new SomeClass()])).toEqual(false); + }); - test('false on array w/ class', () => { - expect( isArrayOfVinyls([SomeClass]) ).toEqual(false); - }); + test('false on array w/ class', () => { + expect(isArrayOfVinyls([SomeClass])).toEqual(false); + }); - test('false on array w/ arrow function', () => { - expect( isArrayOfVinyls([() => {}]) ).toEqual(false); - }); + test('false on array w/ arrow function', () => { + expect(isArrayOfVinyls([() => {}])).toEqual(false); + }); - test('false on array w/ function', () => { - expect( isArrayOfVinyls([someFunction]) ).toEqual(false); - }); + test('false on array w/ function', () => { + expect(isArrayOfVinyls([someFunction])).toEqual(false); + }); - test('false on array w/ object', () => { - expect( isArrayOfVinyls([{}]) ).toEqual(false); - }); + test('false on array w/ object', () => { + expect(isArrayOfVinyls([{}])).toEqual(false); + }); - test('false on array w/ array', () => { - expect( isArrayOfVinyls([[]]) ).toEqual(false); - }); + test('false on array w/ array', () => { + expect(isArrayOfVinyls([[]])).toEqual(false); + }); }); describe('utils.js - pathLineSort', () => { - // NOTE: no-extension files are treated as folder - // 1) folders first, files and empty last - // 2) folder/file names sort alphabetically - // (per depth; first by name, then by extension) - // 3) no line number first - // 4) line numbers ascending numerically - const sorted = [ - 'file', - 'file:10', - 'file:20', - 'file:100', - 'some/file', - 'some/file:10', - 'some/file:20', - 'some/file:100', - 'some/other/folder/file', - 'some/other/folder/file:10', - 'some/other/folder/file:20', - 'some/other/folder/file:100', - 'some/other/folder/.ext', - 'some/other/folder/.ext:10', - 'some/other/folder/.ext:20', - 'some/other/folder/.ext:100', - 'some/other/folder/different_file.ext', - 'some/other/folder/different_file.ext:10', - 'some/other/folder/different_file.ext:20', - 'some/other/folder/different_file.ext:100', - 'some/other/folder/file.ext', - 'some/other/folder/file.ext:10', - 'some/other/folder/file.ext:20', - 'some/other/folder/file.ext:100', - 'some/other/folder/file.oth', - 'some/other/folder/file.oth:10', - 'some/other/folder/file.oth:20', - 'some/other/folder/file.oth:100', - 'some/other/folder/', - 'some/other/path/file', - 'some/other/path/file:10', - 'some/other/path/file:20', - 'some/other/path/file:100', - 'some/other/path/.ext', - 'some/other/path/.ext:10', - 'some/other/path/.ext:20', - 'some/other/path/.ext:100', - 'some/other/path/different_file.ext', - 'some/other/path/different_file.ext:10', - 'some/other/path/different_file.ext:20', - 'some/other/path/different_file.ext:100', - 'some/other/path/file.ext', - 'some/other/path/file.ext:10', - 'some/other/path/file.ext:20', - 'some/other/path/file.ext:100', - 'some/other/path/file.oth', - 'some/other/path/file.oth:10', - 'some/other/path/file.oth:20', - 'some/other/path/file.oth:100', - 'some/other/path/', - 'some/path/file', - 'some/path/file:10', - 'some/path/file:20', - 'some/path/file:100', - 'some/path/.ext', - 'some/path/.ext:10', - 'some/path/.ext:20', - 'some/path/.ext:100', - 'some/path/different_file.ext', - 'some/path/different_file.ext:10', - 'some/path/different_file.ext:20', - 'some/path/different_file.ext:100', - 'some/path/file.ext', - 'some/path/file.ext:10', - 'some/path/file.ext:20', - 'some/path/file.ext:100', - 'some/path/file.oth', - 'some/path/file.oth:10', - 'some/path/file.oth:20', - 'some/path/file.oth:100', - 'some/path/', - 'some/quoted/file', - 'some/quoted/file:10', - 'some/quoted/file:20', - 'some/quoted/file:100', - 'some/quoted/.ext', - 'some/quoted/.ext:10', - 'some/quoted/.ext:20', - 'some/quoted/.ext:100', - 'some/quoted/different_file.ext', - 'some/quoted/different_file.ext:10', - 'some/quoted/different_file.ext:20', - 'some/quoted/different_file.ext:100', - 'some/quoted/file.ext', - 'some/quoted/file.ext:10', - 'some/quoted/file.ext:20', - 'some/quoted/file.ext:100', - 'some/quoted/file.oth', - 'some/quoted/file.oth:10', - 'some/quoted/file.oth:20', - 'some/quoted/file.oth:100', - 'some/quoted/', - 'some/.ext', - 'some/.ext:10', - 'some/.ext:20', - 'some/.ext:100', - 'some/different_file.ext', - 'some/different_file.ext:10', - 'some/different_file.ext:20', - 'some/different_file.ext:100', - 'some/file.ext', - 'some/file.ext:10', - 'some/file.ext:20', - 'some/file.ext:100', - 'some/file.oth', - 'some/file.oth:10', - 'some/file.oth:20', - 'some/file.oth:100', - 'some/', - '.ext', - '.ext:10', - '.ext:20', - '.ext:100', - 'different_file.ext', - 'different_file.ext:10', - 'different_file.ext:20', - 'different_file.ext:100', - 'file.a', - 'file.b', - 'file.c', - 'file.d', - 'file.e', - 'file.ext', - 'file.ext:10', - 'file.ext:20', - 'file.ext:100', - 'file.oth', - 'file.oth:10', - 'file.oth:20', - 'file.oth:100', - '' - ]; - - // let shuffled = shuffle( sorted.slice() ); - let shuffled = [ - 'some/quoted/file.ext:100', - 'some/path/file.ext:100', - 'some/path/file.ext:10', - 'some/path/.ext:20', - 'some/other/folder/file.oth:10', - 'some/other/path/file.oth', - 'some/path/file:100', - 'file.ext:10', - 'some/quoted/file:10', - 'some/file:20', - 'some/quoted/.ext', - 'some/other/folder/file.oth:20', - 'some/path/file', - 'some/other/path/file:100', - 'some/file', - 'some/other/path/.ext', - 'file.oth', - 'different_file.ext:10', - 'some/quoted/.ext:10', - 'some/quoted/file.ext', - 'file', - 'some/path/file.ext:20', - 'some/path/file.oth:100', - 'some/other/path/file.ext:100', - 'some/other/folder/file:100', - 'some/quoted/.ext:100', - 'some/other/path/different_file.ext:100', - 'some/quoted/file.oth:100', - 'file.oth:100', - 'some/quoted/file:20', - 'file.oth:20', - 'different_file.ext', - 'some/quoted/file.oth:20', - 'some/other/folder/file:10', - 'file.ext:20', - 'some/.ext:10', - '', - 'some/other/path/file.ext:10', - 'some/different_file.ext:20', - '.ext:20', - 'some/quoted/file', - 'some/different_file.ext:10', - 'some/file.oth:100', - 'some/path/file:20', - 'file.e', - 'file.ext', - 'some/', - 'some/path/file.oth:20', - 'some/different_file.ext', - 'some/other/path/file:10', - 'some/quoted/different_file.ext:100', - 'some/other/path/file.oth:10', - 'some/.ext', - 'some/other/folder/different_file.ext', - 'some/file.ext:100', - 'some/quoted/file:100', - 'some/other/path/different_file.ext', - 'file:100', - 'some/.ext:100', - 'some/path/different_file.ext:100', - 'some/path/different_file.ext:10', - 'some/other/path/file.oth:100', - 'some/other/folder/different_file.ext:10', - 'some/quoted/file.ext:20', - 'some/other/path/different_file.ext:20', - 'some/.ext:20', - 'some/quoted/.ext:20', - 'some/other/path/file:20', - 'some/path/file:10', - 'some/file:10', - 'some/path/.ext:10', - 'some/other/path/.ext:100', - 'file.c', - 'file.d', - 'some/path/different_file.ext:20', - 'some/file.ext:20', - 'some/file.oth:10', - 'some/other/path/file.ext:20', - 'some/other/folder/.ext:20', - 'file:20', - 'some/other/folder/file', - 'some/other/folder/file.ext:100', - 'some/other/folder/file.ext:10', - 'some/file.ext', - 'some/quoted/different_file.ext:20', - 'some/other/path/file.oth:20', - 'some/other/path/file', - 'some/other/path/', - '.ext:10', - 'some/other/path/.ext:10', - 'some/file.oth:20', - 'file.a', - 'some/other/path/file.ext', - 'some/file:100', - 'some/other/folder/file.oth', - 'some/different_file.ext:100', - 'some/other/folder/.ext:10', - 'some/path/different_file.ext', - 'some/quoted/file.oth', - 'some/other/path/different_file.ext:10', - 'some/file.ext:10', - 'some/other/folder/', - 'some/file.oth', - 'some/path/.ext:100', - 'file.oth:10', - 'some/other/folder/.ext', - 'some/quoted/different_file.ext', - 'some/other/folder/file:20', - 'some/quoted/', - 'file.ext:100', - 'some/other/folder/different_file.ext:20', - 'some/other/folder/file.ext:20', - 'some/quoted/different_file.ext:10', - '.ext', - 'some/quoted/file.ext:10', - 'some/path/', - 'some/path/file.oth', - 'some/other/path/.ext:20', - 'different_file.ext:100', - 'some/path/file.ext', - 'some/quoted/file.oth:10', - '.ext:100', - 'some/other/folder/different_file.ext:100', - 'some/path/file.oth:10', - 'some/other/folder/file.ext', - 'some/other/folder/.ext:100', - 'some/other/folder/file.oth:100', - 'file:10', - 'file.b', - 'different_file.ext:20', - 'some/path/.ext', - ]; - - test('used as function', () => { - expect( pathLineSort(shuffled) ).toEqual(sorted); - }); - - test('used as callback for array.sort()', () => { - expect( shuffled.sort(pathLineSort) ).toEqual(sorted); - }); - - test('handles only strings', () => { - expect(() => { - pathLineSort('test.txt', {}); - }).toThrow(new RegExp('pathLineSort: a or b not a string')); - expect(() => { - pathLineSort([], 'test.txt'); - }).toThrow(new RegExp('pathLineSort: a or b not a string')); - }); - - test('return 0 when the same', () => { - expect( pathLineSort('some/path/file.ext:10', 'some/path/file.ext:10') ).toEqual(0); - expect( pathLineSort('some/path/file.ext:', 'some/path/file.ext:') ).toEqual(0); - expect( pathLineSort('some/path/file.ext', 'some/path/file.ext') ).toEqual(0); - expect( pathLineSort('some/path/file.', 'some/path/file.') ).toEqual(0); - expect( pathLineSort('some/path/file', 'some/path/file') ).toEqual(0); - expect( pathLineSort('some', 'some') ).toEqual(0); - }); - - test('return -1', () => { - expect( pathLineSort('', '') ).toEqual(-1); - expect( pathLineSort('some/path/file.ext:1', 'some/path/file.ext:2') ).toEqual(-1); - expect( pathLineSort('some/path/file.ext', 'some/path/file.ext:10') ).toEqual(-1); - expect( pathLineSort('some/path/file.aaa', 'some/path/file.bbb') ).toEqual(-1); - expect( pathLineSort('some/path/aaaa.ext', 'some/path/bbbb.ext') ).toEqual(-1); - expect( pathLineSort('some/path/aaaa', 'some/path/bbbb') ).toEqual(-1); - expect( pathLineSort('some/path/file/', 'some/path/file.ext:10') ).toEqual(-1); - expect( pathLineSort('some/path/', 'some//') ).toEqual(-1); - expect( pathLineSort('some/path', 'some') ).toEqual(-1); - }); - - test('return 1', () => { - expect( pathLineSort('some/path/file.ext:2', 'some/path/file.ext:1') ).toEqual(1); - expect( pathLineSort('some/path/file.ext:10', 'some/path/file.ext') ).toEqual(1); - expect( pathLineSort('some/path/file.bbb', 'some/path/file.aaa') ).toEqual(1); - expect( pathLineSort('some/path/bbbb.ext', 'some/path/aaaa.ext') ).toEqual(1); - expect( pathLineSort('some/path/bbbb', 'some/path/aaaa') ).toEqual(1); - expect( pathLineSort('some/path/file.ext:10', 'some/path/file/') ).toEqual(1); - expect( pathLineSort('some//', 'some/path/') ).toEqual(1); - expect( pathLineSort('some', 'some/path') ).toEqual(1); - }); + // NOTE: no-extension files are treated as folder + // 1) folders first, files and empty last + // 2) folder/file names sort alphabetically + // (per depth; first by name, then by extension) + // 3) no line number first + // 4) line numbers ascending numerically + const sorted = [ + 'file', + 'file:10', + 'file:20', + 'file:100', + 'some/file', + 'some/file:10', + 'some/file:20', + 'some/file:100', + 'some/other/folder/file', + 'some/other/folder/file:10', + 'some/other/folder/file:20', + 'some/other/folder/file:100', + 'some/other/folder/.ext', + 'some/other/folder/.ext:10', + 'some/other/folder/.ext:20', + 'some/other/folder/.ext:100', + 'some/other/folder/different_file.ext', + 'some/other/folder/different_file.ext:10', + 'some/other/folder/different_file.ext:20', + 'some/other/folder/different_file.ext:100', + 'some/other/folder/file.ext', + 'some/other/folder/file.ext:10', + 'some/other/folder/file.ext:20', + 'some/other/folder/file.ext:100', + 'some/other/folder/file.oth', + 'some/other/folder/file.oth:10', + 'some/other/folder/file.oth:20', + 'some/other/folder/file.oth:100', + 'some/other/folder/', + 'some/other/path/file', + 'some/other/path/file:10', + 'some/other/path/file:20', + 'some/other/path/file:100', + 'some/other/path/.ext', + 'some/other/path/.ext:10', + 'some/other/path/.ext:20', + 'some/other/path/.ext:100', + 'some/other/path/different_file.ext', + 'some/other/path/different_file.ext:10', + 'some/other/path/different_file.ext:20', + 'some/other/path/different_file.ext:100', + 'some/other/path/file.ext', + 'some/other/path/file.ext:10', + 'some/other/path/file.ext:20', + 'some/other/path/file.ext:100', + 'some/other/path/file.oth', + 'some/other/path/file.oth:10', + 'some/other/path/file.oth:20', + 'some/other/path/file.oth:100', + 'some/other/path/', + 'some/path/file', + 'some/path/file:10', + 'some/path/file:20', + 'some/path/file:100', + 'some/path/.ext', + 'some/path/.ext:10', + 'some/path/.ext:20', + 'some/path/.ext:100', + 'some/path/different_file.ext', + 'some/path/different_file.ext:10', + 'some/path/different_file.ext:20', + 'some/path/different_file.ext:100', + 'some/path/file.ext', + 'some/path/file.ext:10', + 'some/path/file.ext:20', + 'some/path/file.ext:100', + 'some/path/file.oth', + 'some/path/file.oth:10', + 'some/path/file.oth:20', + 'some/path/file.oth:100', + 'some/path/', + 'some/quoted/file', + 'some/quoted/file:10', + 'some/quoted/file:20', + 'some/quoted/file:100', + 'some/quoted/.ext', + 'some/quoted/.ext:10', + 'some/quoted/.ext:20', + 'some/quoted/.ext:100', + 'some/quoted/different_file.ext', + 'some/quoted/different_file.ext:10', + 'some/quoted/different_file.ext:20', + 'some/quoted/different_file.ext:100', + 'some/quoted/file.ext', + 'some/quoted/file.ext:10', + 'some/quoted/file.ext:20', + 'some/quoted/file.ext:100', + 'some/quoted/file.oth', + 'some/quoted/file.oth:10', + 'some/quoted/file.oth:20', + 'some/quoted/file.oth:100', + 'some/quoted/', + 'some/.ext', + 'some/.ext:10', + 'some/.ext:20', + 'some/.ext:100', + 'some/different_file.ext', + 'some/different_file.ext:10', + 'some/different_file.ext:20', + 'some/different_file.ext:100', + 'some/file.ext', + 'some/file.ext:10', + 'some/file.ext:20', + 'some/file.ext:100', + 'some/file.oth', + 'some/file.oth:10', + 'some/file.oth:20', + 'some/file.oth:100', + 'some/', + '.ext', + '.ext:10', + '.ext:20', + '.ext:100', + 'different_file.ext', + 'different_file.ext:10', + 'different_file.ext:20', + 'different_file.ext:100', + 'file.a', + 'file.b', + 'file.c', + 'file.d', + 'file.e', + 'file.ext', + 'file.ext:10', + 'file.ext:20', + 'file.ext:100', + 'file.oth', + 'file.oth:10', + 'file.oth:20', + 'file.oth:100', + '', + ]; + + // let shuffled = shuffle( sorted.slice() ); + let shuffled = [ + 'some/quoted/file.ext:100', + 'some/path/file.ext:100', + 'some/path/file.ext:10', + 'some/path/.ext:20', + 'some/other/folder/file.oth:10', + 'some/other/path/file.oth', + 'some/path/file:100', + 'file.ext:10', + 'some/quoted/file:10', + 'some/file:20', + 'some/quoted/.ext', + 'some/other/folder/file.oth:20', + 'some/path/file', + 'some/other/path/file:100', + 'some/file', + 'some/other/path/.ext', + 'file.oth', + 'different_file.ext:10', + 'some/quoted/.ext:10', + 'some/quoted/file.ext', + 'file', + 'some/path/file.ext:20', + 'some/path/file.oth:100', + 'some/other/path/file.ext:100', + 'some/other/folder/file:100', + 'some/quoted/.ext:100', + 'some/other/path/different_file.ext:100', + 'some/quoted/file.oth:100', + 'file.oth:100', + 'some/quoted/file:20', + 'file.oth:20', + 'different_file.ext', + 'some/quoted/file.oth:20', + 'some/other/folder/file:10', + 'file.ext:20', + 'some/.ext:10', + '', + 'some/other/path/file.ext:10', + 'some/different_file.ext:20', + '.ext:20', + 'some/quoted/file', + 'some/different_file.ext:10', + 'some/file.oth:100', + 'some/path/file:20', + 'file.e', + 'file.ext', + 'some/', + 'some/path/file.oth:20', + 'some/different_file.ext', + 'some/other/path/file:10', + 'some/quoted/different_file.ext:100', + 'some/other/path/file.oth:10', + 'some/.ext', + 'some/other/folder/different_file.ext', + 'some/file.ext:100', + 'some/quoted/file:100', + 'some/other/path/different_file.ext', + 'file:100', + 'some/.ext:100', + 'some/path/different_file.ext:100', + 'some/path/different_file.ext:10', + 'some/other/path/file.oth:100', + 'some/other/folder/different_file.ext:10', + 'some/quoted/file.ext:20', + 'some/other/path/different_file.ext:20', + 'some/.ext:20', + 'some/quoted/.ext:20', + 'some/other/path/file:20', + 'some/path/file:10', + 'some/file:10', + 'some/path/.ext:10', + 'some/other/path/.ext:100', + 'file.c', + 'file.d', + 'some/path/different_file.ext:20', + 'some/file.ext:20', + 'some/file.oth:10', + 'some/other/path/file.ext:20', + 'some/other/folder/.ext:20', + 'file:20', + 'some/other/folder/file', + 'some/other/folder/file.ext:100', + 'some/other/folder/file.ext:10', + 'some/file.ext', + 'some/quoted/different_file.ext:20', + 'some/other/path/file.oth:20', + 'some/other/path/file', + 'some/other/path/', + '.ext:10', + 'some/other/path/.ext:10', + 'some/file.oth:20', + 'file.a', + 'some/other/path/file.ext', + 'some/file:100', + 'some/other/folder/file.oth', + 'some/different_file.ext:100', + 'some/other/folder/.ext:10', + 'some/path/different_file.ext', + 'some/quoted/file.oth', + 'some/other/path/different_file.ext:10', + 'some/file.ext:10', + 'some/other/folder/', + 'some/file.oth', + 'some/path/.ext:100', + 'file.oth:10', + 'some/other/folder/.ext', + 'some/quoted/different_file.ext', + 'some/other/folder/file:20', + 'some/quoted/', + 'file.ext:100', + 'some/other/folder/different_file.ext:20', + 'some/other/folder/file.ext:20', + 'some/quoted/different_file.ext:10', + '.ext', + 'some/quoted/file.ext:10', + 'some/path/', + 'some/path/file.oth', + 'some/other/path/.ext:20', + 'different_file.ext:100', + 'some/path/file.ext', + 'some/quoted/file.oth:10', + '.ext:100', + 'some/other/folder/different_file.ext:100', + 'some/path/file.oth:10', + 'some/other/folder/file.ext', + 'some/other/folder/.ext:100', + 'some/other/folder/file.oth:100', + 'file:10', + 'file.b', + 'different_file.ext:20', + 'some/path/.ext', + ]; + + test('used as function', () => { + expect(pathLineSort(shuffled)).toEqual(sorted); + }); + + test('used as callback for array.sort()', () => { + expect(shuffled.sort(pathLineSort)).toEqual(sorted); + }); + + test('handles only strings', () => { + expect(() => { + pathLineSort('test.txt', {}); + }).toThrow(new RegExp('pathLineSort: a or b not a string')); + expect(() => { + pathLineSort([], 'test.txt'); + }).toThrow(new RegExp('pathLineSort: a or b not a string')); + }); + + test('return 0 when the same', () => { + expect( + pathLineSort('some/path/file.ext:10', 'some/path/file.ext:10') + ).toEqual(0); + expect(pathLineSort('some/path/file.ext:', 'some/path/file.ext:')).toEqual( + 0 + ); + expect(pathLineSort('some/path/file.ext', 'some/path/file.ext')).toEqual(0); + expect(pathLineSort('some/path/file.', 'some/path/file.')).toEqual(0); + expect(pathLineSort('some/path/file', 'some/path/file')).toEqual(0); + expect(pathLineSort('some', 'some')).toEqual(0); + }); + + test('return -1', () => { + expect(pathLineSort('', '')).toEqual(-1); + expect( + pathLineSort('some/path/file.ext:1', 'some/path/file.ext:2') + ).toEqual(-1); + expect(pathLineSort('some/path/file.ext', 'some/path/file.ext:10')).toEqual( + -1 + ); + expect(pathLineSort('some/path/file.aaa', 'some/path/file.bbb')).toEqual( + -1 + ); + expect(pathLineSort('some/path/aaaa.ext', 'some/path/bbbb.ext')).toEqual( + -1 + ); + expect(pathLineSort('some/path/aaaa', 'some/path/bbbb')).toEqual(-1); + expect(pathLineSort('some/path/file/', 'some/path/file.ext:10')).toEqual( + -1 + ); + expect(pathLineSort('some/path/', 'some//')).toEqual(-1); + expect(pathLineSort('some/path', 'some')).toEqual(-1); + }); + + test('return 1', () => { + expect( + pathLineSort('some/path/file.ext:2', 'some/path/file.ext:1') + ).toEqual(1); + expect(pathLineSort('some/path/file.ext:10', 'some/path/file.ext')).toEqual( + 1 + ); + expect(pathLineSort('some/path/file.bbb', 'some/path/file.aaa')).toEqual(1); + expect(pathLineSort('some/path/bbbb.ext', 'some/path/aaaa.ext')).toEqual(1); + expect(pathLineSort('some/path/bbbb', 'some/path/aaaa')).toEqual(1); + expect(pathLineSort('some/path/file.ext:10', 'some/path/file/')).toEqual(1); + expect(pathLineSort('some//', 'some/path/')).toEqual(1); + expect(pathLineSort('some', 'some/path')).toEqual(1); + }); });