diff --git a/README.md b/README.md index 0e8ce9f..ba996af 100644 --- a/README.md +++ b/README.md @@ -58,11 +58,14 @@ Options: --debug Run tests in debug mode (https://jestjs.io/docs/en/troubleshooting) [boolean] [default: false] + --projectJson Path to the project JSON file (defaults to + sfdx-project.json) [string] --help Show help [boolean] Examples: - sfdx-lwc-jest --coverage Collect coverage and display in output - sfdx-lwc-jest -- --json All params after `--` are directly passed to Jest + sfdx-lwc-jest --coverage Collect coverage and display in output + sfdx-lwc-jest --projectJson my.json Use custom project file + sfdx-lwc-jest -- --json All params after `--` are directly passed to Jest ``` ## Passing Additional Jest CLI Options @@ -75,6 +78,22 @@ sfdx-lwc-jest -- --json See the [Jest documentation](http://facebook.github.io/jest/docs/en/cli.html) for all CLI options. +## Custom Project Configuration + +By default, `sfdx-lwc-jest` looks for `sfdx-project.json` in the current working directory to discover LWC component paths. You can specify a custom project JSON file using the `--projectJson` option: + +```bash +sfdx-lwc-jest --projectJson ./configs/my-project.json +``` + +This is useful for: + +- Projects with non-standard project file names +- Testing against multiple project configurations +- CI/CD pipelines with custom project setups + +The custom project file should follow the same format as `sfdx-project.json` with `packageDirectories` containing paths to your LWC components. + ## Debug Mode Debug mode lets you easily debug your Jest tests. diff --git a/config.js b/config.js index 368f2af..638b456 100644 --- a/config.js +++ b/config.js @@ -6,5 +6,5 @@ */ 'use strict'; -const { jestConfig } = require('./src/config'); -module.exports = { jestConfig }; +const { getJestConfig } = require('./src/config'); +module.exports = { jestConfig: getJestConfig() }; diff --git a/src/config.js b/src/config.js index abd0c23..1ead497 100644 --- a/src/config.js +++ b/src/config.js @@ -27,26 +27,28 @@ function getCoveragePaths() { .flat(); } -const jestConfig = { - // Inherited from @lwc/jest-preset - moduleFileExtensions: jestPreset.moduleFileExtensions || ['ts', 'js', 'html'], - testEnvironment: jestPreset.testEnvironment || 'jsdom', - transform: { - ...jestPreset.transform, - '^.+\\.(js|ts|html|css)$': require.resolve('@lwc/jest-transformer'), - }, - setupFilesAfterEnv: jestPreset.setupFilesAfterEnv || [], - snapshotSerializers: jestPreset.snapshotSerializers || [ - require.resolve('@lwc/jest-serializer'), - ], - // Specific to sfdx-lwc-jest - collectCoverageFrom: getCoveragePaths(), - resolver: path.join(__dirname, './resolver.js'), - rootDir: PROJECT_ROOT, - testPathIgnorePatterns: ['/node_modules/', '/test/specs/'], - transformIgnorePatterns: [ - '/node_modules/(?!(.*@salesforce/sfdx-lwc-jest/src/lightning-stubs)/)', - ], -}; +function getJestConfig() { + return { + // Inherited from @lwc/jest-preset + moduleFileExtensions: jestPreset.moduleFileExtensions || ['ts', 'js', 'html'], + testEnvironment: jestPreset.testEnvironment || 'jsdom', + transform: { + ...jestPreset.transform, + '^.+\\.(js|ts|html|css)$': require.resolve('@lwc/jest-transformer'), + }, + setupFilesAfterEnv: jestPreset.setupFilesAfterEnv || [], + snapshotSerializers: jestPreset.snapshotSerializers || [ + require.resolve('@lwc/jest-serializer'), + ], + // Specific to sfdx-lwc-jest + collectCoverageFrom: getCoveragePaths(), + resolver: path.join(__dirname, './resolver.js'), + rootDir: PROJECT_ROOT, + testPathIgnorePatterns: ['/node_modules/', '/test/specs/'], + transformIgnorePatterns: [ + '/node_modules/(?!(.*@salesforce/sfdx-lwc-jest/src/lightning-stubs)/)', + ], + }; +} -module.exports = { jestConfig }; +module.exports = { getJestConfig }; diff --git a/src/options/options.js b/src/options/options.js index b8a1e88..4a73708 100644 --- a/src/options/options.js +++ b/src/options/options.js @@ -42,6 +42,11 @@ const testOptions = { description: 'Deprecated: this noop flag is kept for backward compatibility', type: 'boolean', }, + + projectJson: { + description: 'Path to the project JSON file (defaults to sfdx-project.json)', + type: 'string', + }, }; module.exports = testOptions; diff --git a/src/utils/__mocks__/project.js b/src/utils/__mocks__/project.js index 0b2d18e..0b891b6 100644 --- a/src/utils/__mocks__/project.js +++ b/src/utils/__mocks__/project.js @@ -12,4 +12,5 @@ module.exports = { return { mock: true }; }, getModulePaths: () => ['force-app/main/unix/lwc', 'force-app\\main\\windows\\lwc'], + setCustomProjectJsonPath: () => {}, }; diff --git a/src/utils/project.js b/src/utils/project.js index 5adf11a..dd10eed 100644 --- a/src/utils/project.js +++ b/src/utils/project.js @@ -13,13 +13,22 @@ const fg = require('fast-glob'); const PROJECT_ROOT = fs.realpathSync(process.cwd()); let PATHS = []; +let CUSTOM_PROJECT_JSON_PATH = null; + +function setCustomProjectJsonPath(customPath) { + CUSTOM_PROJECT_JSON_PATH = customPath; + // Clear cached paths when project JSON changes + PATHS = []; +} function getSfdxProjectJson() { - const sfdxProjectJson = path.join(PROJECT_ROOT, 'sfdx-project.json'); + const sfdxProjectJson = CUSTOM_PROJECT_JSON_PATH + ? path.resolve(CUSTOM_PROJECT_JSON_PATH) + : path.join(PROJECT_ROOT, 'sfdx-project.json'); if (!fs.existsSync(sfdxProjectJson)) { throw new Error( - 'Could not find sfdx-project.json. Make sure `lwc-jest` is run from project root', + `Could not find ${sfdxProjectJson}. Make sure the project JSON file exists and 'lwc-jest' is run from project root`, ); } @@ -40,4 +49,5 @@ module.exports = { PROJECT_ROOT, getSfdxProjectJson, getModulePaths, + setCustomProjectJsonPath, }; diff --git a/src/utils/test-runner.js b/src/utils/test-runner.js index 3413608..aa7e4f6 100644 --- a/src/utils/test-runner.js +++ b/src/utils/test-runner.js @@ -10,10 +10,10 @@ const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); -const { PROJECT_ROOT } = require('./project'); +const { PROJECT_ROOT, setCustomProjectJsonPath } = require('./project'); const { info, warn } = require('../log'); -const { jestConfig } = require('../config'); +const { getJestConfig } = require('../config'); // List of CLI options that should be passthrough to jest. const JEST_PASSTHROUGH_OPTIONS = new Set(['coverage', 'updateSnapshot', 'verbose', 'watch']); @@ -54,6 +54,7 @@ function getJestArgs(argv) { fs.existsSync(path.resolve(PROJECT_ROOT, 'jest.config.mjs')) || fs.existsSync(path.resolve(PROJECT_ROOT, 'jest.config.cjs')); if (!hasCustomConfig) { + const jestConfig = getJestConfig(); jestArgs.unshift(`--config=${JSON.stringify(jestConfig)}`); } @@ -61,6 +62,11 @@ function getJestArgs(argv) { } async function testRunner(argv) { + // Set custom project JSON path if provided + if (argv.projectJson) { + setCustomProjectJsonPath(argv.projectJson); + } + if (argv.skipApiVersionCheck !== undefined) { warn( 'The --skipApiVersionCheck flag is deprecated and will be removed in future versions.', diff --git a/tests/__snapshots__/help.test.js.snap b/tests/__snapshots__/help.test.js.snap index 564d62b..c02fc92 100644 --- a/tests/__snapshots__/help.test.js.snap +++ b/tests/__snapshots__/help.test.js.snap @@ -18,6 +18,8 @@ Options: [boolean] [default: false] --skipApiVersionCheck Deprecated: this noop flag is kept for backward compatibility [boolean] + --projectJson Path to the project JSON file (defaults to + sfdx-project.json) [string] --help Show help [boolean] Examples: diff --git a/tests/config.test.js b/tests/config.test.js index 66f9f36..f43e062 100644 --- a/tests/config.test.js +++ b/tests/config.test.js @@ -7,7 +7,8 @@ 'use strict'; jest.mock('../src/utils/project'); -const { jestConfig } = require('../src/config'); +const { getJestConfig } = require('../src/config'); +const jestConfig = getJestConfig(); test('coveragePaths correctly build', () => { expect(jestConfig.collectCoverageFrom).toStrictEqual([