diff --git a/packages/@o3r/workspace/schematics/index.it.spec.ts b/packages/@o3r/workspace/schematics/index.it.spec.ts index bcc0750764..487dd5ec0c 100644 --- a/packages/@o3r/workspace/schematics/index.it.spec.ts +++ b/packages/@o3r/workspace/schematics/index.it.spec.ts @@ -13,6 +13,7 @@ import * as path from 'node:path'; import { getDefaultExecSyncOptions, getGitDiff, + getPackageManager, packageManagerExec, packageManagerInstall, packageManagerRun, @@ -95,6 +96,10 @@ describe('new otter workspace', () => { const execAppOptions = { ...getDefaultExecSyncOptions(), cwd: workspacePath }; const libName = 'test-library'; const inLibraryPath = path.resolve(workspacePath, 'libs', libName); + // TODO Remove node-linker once https://github.com/AmadeusITGroup/otter/issues/2620 is completed + if (getPackageManager() === 'yarn') { + packageManagerExec({ script: 'config', args: ['set', 'nodeLinker', 'node-modules'] }, execAppOptions); + } expect(() => packageManagerInstall(execAppOptions)).not.toThrow(); const generatedLibFiles = [ @@ -115,6 +120,7 @@ describe('new otter workspace', () => { expect(existsSync(path.join(workspacePath, 'project'))).toBe(false); generatedLibFiles.forEach((file) => expect(existsSync(path.join(inLibraryPath, file))).toBe(true)); expect(() => packageManagerRunOnProject(libName, true, { script: 'build' }, execAppOptions)).not.toThrow(); + expect(() => packageManagerExec({ script: 'ng', args: ['test', '--watch=false', '--browsers=ChromeHeadless'] }, execAppOptions)).not.toThrow(); }); test('should generate a monorepo setup', async () => { @@ -122,6 +128,10 @@ describe('new otter workspace', () => { const defaultOptions = getDefaultExecSyncOptions(); const execAppOptions = { ...defaultOptions, cwd: workspacePath, env: { ...defaultOptions.env, NX_CLOUD_ACCESS_TOKEN: '' } }; + // TODO Remove node-linker once https://github.com/AmadeusITGroup/otter/issues/2620 is completed + if (getPackageManager() === 'yarn') { + packageManagerExec({ script: 'config', args: ['set', 'nodeLinker', 'node-modules'] }, execAppOptions); + } expect(() => packageManagerInstall(execAppOptions)).not.toThrow(); const rootPackageJson = JSON.parse(await fs.readFile(path.join(workspacePath, 'package.json'), 'utf8')) as PackageJson; expect(rootPackageJson.scripts).toHaveProperty('build', 'lerna run build'); diff --git a/packages/@o3r/workspace/schematics/library/index.spec.ts b/packages/@o3r/workspace/schematics/library/index.spec.ts index e8ea8ad7e0..f6b6da8e82 100644 --- a/packages/@o3r/workspace/schematics/library/index.spec.ts +++ b/packages/@o3r/workspace/schematics/library/index.spec.ts @@ -17,6 +17,15 @@ jest.mock('@angular-devkit/schematics', () => { }); const collectionPath = path.join(__dirname, '..', '..', 'collection.json'); +const angularJsonFile = `{ + "version": 1, + "projects": { + "my-new-module": { + "projectType": "library", + "root": "packages-test/my-new-module" + } + } +}`; describe('New module generator', () => { let initialTree: Tree; @@ -36,7 +45,7 @@ describe('New module generator', () => { }); it('should generate the minimum mandatory files', async () => { - initialTree.create('angular.json', '{"version": 1, "projects": {} }'); + initialTree.create('angular.json', angularJsonFile); initialTree.create('package.json', '{ "version": "0.0.0-test" }'); initialTree.create('/packages-test/my-new-module/package.json', '{ "version": "0.0.0-test" }'); initialTree.create('/packages-test/my-new-module/ng-package.json', '{ }'); @@ -58,11 +67,36 @@ describe('New module generator', () => { expect(tree.exists('/packages-test/my-new-module/project.json')).toBe(false); expect(JSON.parse(tree.readContent('/tsconfig.base.json')).compilerOptions.paths['@my/new-module']).toContain('packages-test/my-new-module/src/public-api'); expect(JSON.parse(tree.readContent('/tsconfig.build.json')).compilerOptions.paths['@my/new-module'][0]).toBe('packages-test/my-new-module/dist'); + expect(tree.exists('/packages-test/my-new-module/testing/setup-jest.ts')).toBe(false); + expect(tree.exists('/packages-test/my-new-module/jest.config.js')).toBe(false); expect(tree.files.length).toBeGreaterThanOrEqual(9); }); - // eslint-disable-next-line jest/no-disabled-tests -- TODO: Should be re-enable when the following issue #2066 is fixed - describe.skip('in NX monorepo', () => { + it('should generate an project with jest files', async () => { + initialTree.create('angular.json', angularJsonFile); + initialTree.create('package.json', '{ "version": "0.0.0-test" }'); + initialTree.create('/packages-test/my-new-module/package.json', '{ "version": "0.0.0-test" }'); + initialTree.create('/packages-test/my-new-module/ng-package.json', '{ }'); + const runner = new SchematicTestRunner('schematics', collectionPath); + const angularPackageJson = require.resolve('@schematics/angular/package.json'); + const o3rCorePackageJson = require.resolve('@o3r/core/package.json'); + runner.registerCollection('@o3r/core', path.resolve(path.dirname(o3rCorePackageJson), require(o3rCorePackageJson).schematics)); + runner.registerCollection('@schematics/angular', path.resolve(path.dirname(angularPackageJson), require(angularPackageJson).schematics)); + jest.spyOn(require('@angular-devkit/schematics'), 'externalSchematic'); + const tree = await runner.runSchematic('library', { + path: 'packages-test', + name: '@my/new-module', + skipLinter: true, + skipInstall: true, + testingFramework: 'jest' + + }, initialTree); + expect(tree.exists('/packages-test/my-new-module/testing/setup-jest.ts')).toBe(true); + expect(tree.exists('/packages-test/my-new-module/jest.config.js')).toBe(true); + expect(JSON.parse(tree.readContent('/packages-test/my-new-module/package.json')).scripts.test).toContain('jest'); + }); + + describe('in NX monorepo', () => { it('should generate Nx project.json with given name', async () => { initialTree.create('nx.json', '{"workspaceLayout": { "libsDir": "packages-test" } }'); initialTree.create('angular.json', '{"version": 1, "projects": {} }'); @@ -77,7 +111,6 @@ describe('New module generator', () => { runner.registerCollection('@schematics/angular', path.resolve(path.dirname(angularPackageJson), require(angularPackageJson).schematics)); runner.registerCollection('@nx/workspace', path.resolve(path.dirname(nxWorkspacePackageJson), require(nxWorkspacePackageJson).generators)); const tree = await runner.runExternalSchematic('schematics', 'library', { - path: 'packages-test', name: '@my/new-module', projectName: 'test-module-name', skipLinter: true diff --git a/packages/@o3r/workspace/schematics/library/rules/rules.ng.ts b/packages/@o3r/workspace/schematics/library/rules/rules.ng.ts index 6384dd4447..9a7a3628ec 100644 --- a/packages/@o3r/workspace/schematics/library/rules/rules.ng.ts +++ b/packages/@o3r/workspace/schematics/library/rules/rules.ng.ts @@ -28,6 +28,7 @@ import type { NgGenerateModuleSchema, } from '../schema'; import { + setUpJest, updateNgPackagrFactory, updatePackageDependenciesFactory, } from './shared'; @@ -61,9 +62,25 @@ export function ngGenerateModule(options: NgGenerateModuleSchema & { targetPath: renameTemplateFiles(), move(options.targetPath) ]); + const templateJest = apply(url('./templates/jest'), [ + template({ + ...options, + tsconfigBasePath: findConfigFileRelativePath(tree, ['tsconfig.base.json', 'tsconfig.json'], options.targetPath) + }), + renameTemplateFiles(), + move(options.targetPath) + ]); + const packageJsonContent = tree.readText('/package.json'); + const hasJestInstalled = options.testingFramework === 'jest' || packageJsonContent.match('jest'); return chain([ mergeWith(templateNg, MergeStrategy.Overwrite), + ...hasJestInstalled + ? [ + mergeWith(templateJest, MergeStrategy.Overwrite), + setUpJest(options) + ] + : [], updatePackageDependenciesFactory(options.targetPath, otterVersion!, o3rCorePackageJson, options), updateNgPackagrFactory(options.targetPath), (t) => { diff --git a/packages/@o3r/workspace/schematics/library/rules/rules.nx.ts b/packages/@o3r/workspace/schematics/library/rules/rules.nx.ts index 7675ea7368..740e9ac44d 100644 --- a/packages/@o3r/workspace/schematics/library/rules/rules.nx.ts +++ b/packages/@o3r/workspace/schematics/library/rules/rules.nx.ts @@ -27,6 +27,7 @@ import type { NgGenerateModuleSchema, } from '../schema'; import { + setUpJest, updateNgPackagrFactory, updatePackageDependenciesFactory, } from './shared'; @@ -118,12 +119,28 @@ export function nxGenerateModule(options: NgGenerateModuleSchema & { packageJson renameTemplateFiles(), move(targetPath) ]); + const templateJest = apply(url('./templates/jest'), [ + template({ + ...options, + tsconfigBasePath: findConfigFileRelativePath(tree, ['tsconfig.base.json', 'tsconfig.json'], targetPath) + }), + renameTemplateFiles(), + move(targetPath) + ]); rules.push(mergeWith(templateNx, MergeStrategy.Overwrite)); + const packageJsonContent = tree.readText('/package.json'); + const hasJestInstalled = options.testingFramework === 'jest' || packageJsonContent.match('jest'); return chain([ ...rules, updatePackageDependenciesFactory(targetPath, otterVersion!, o3rCorePackageJson, options), updateNgPackagrFactory(targetPath), + ...hasJestInstalled + ? [ + mergeWith(templateJest, MergeStrategy.Overwrite), + setUpJest(options) + ] + : [], (t) => { const packageJson = t.readJson(path.posix.join(targetPath, 'package.json')) as PackageJson; packageJson.name = options.packageJsonName; diff --git a/packages/@o3r/workspace/schematics/library/rules/shared.ts b/packages/@o3r/workspace/schematics/library/rules/shared.ts index 2307fd7714..3d7d20c680 100644 --- a/packages/@o3r/workspace/schematics/library/rules/shared.ts +++ b/packages/@o3r/workspace/schematics/library/rules/shared.ts @@ -1,6 +1,8 @@ import * as path from 'node:path'; import type { Rule, + SchematicContext, + Tree, } from '@angular-devkit/schematics'; import { getPackageManagerRunner, @@ -13,6 +15,27 @@ import { NgGenerateModuleSchema, } from '../schema'; +/** + * Set jest files and script in the generated library. + * @param options + */ +export function setUpJest(options: NgGenerateModuleSchema) { + return (tree: Tree, context: SchematicContext) => { + const workspaceConfig = getWorkspaceConfig(tree); + const workspaceProject = (options.name && workspaceConfig?.projects?.[options.name]) || undefined; + if (!workspaceProject?.root) { + context.logger.error(`Failed to find a package json for ${options.name}`); + return; + } + const packageJsonPath = path.join(workspaceProject.root, 'package.json'); + const packageJsonContent = tree.readJson(packageJsonPath) as PackageJson; + packageJsonContent.scripts ||= {}; + packageJsonContent.scripts.test ||= 'jest'; + tree.overwrite(packageJsonPath, JSON.stringify(packageJsonContent, null, 2)); + return tree; + }; +} + /** * Generate rule to update generated package.json file * @param targetPath Path of the generated files diff --git a/packages/@o3r/workspace/schematics/library/templates/ng/jest.config.js.template b/packages/@o3r/workspace/schematics/library/templates/jest/jest.config.js.template similarity index 100% rename from packages/@o3r/workspace/schematics/library/templates/ng/jest.config.js.template rename to packages/@o3r/workspace/schematics/library/templates/jest/jest.config.js.template diff --git a/packages/@o3r/workspace/schematics/library/templates/ng/testing/setup-jest.ts.template b/packages/@o3r/workspace/schematics/library/templates/jest/testing/setup-jest.ts.template similarity index 100% rename from packages/@o3r/workspace/schematics/library/templates/ng/testing/setup-jest.ts.template rename to packages/@o3r/workspace/schematics/library/templates/jest/testing/setup-jest.ts.template diff --git a/packages/@o3r/workspace/schematics/library/templates/ng/tsconfig.spec.json.template b/packages/@o3r/workspace/schematics/library/templates/ng/tsconfig.spec.json.template deleted file mode 100644 index 4dba869bbf..0000000000 --- a/packages/@o3r/workspace/schematics/library/templates/ng/tsconfig.spec.json.template +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "<%= tsconfigSpecPath %>", - "compilerOptions": { - "composite": true, - "outDir": "test", - "rootDir": ".", - }, - "include": [ - "./src/**/*.spec.ts" - ], - "exclude": [], - "references": [ - { - "path": "./tsconfig.build.composite.json" - } - ] -} diff --git a/packages/@o3r/workspace/schematics/library/templates/nx/jest.config.js.template b/packages/@o3r/workspace/schematics/library/templates/nx/jest.config.js.template deleted file mode 100644 index b983a234bc..0000000000 --- a/packages/@o3r/workspace/schematics/library/templates/nx/jest.config.js.template +++ /dev/null @@ -1,38 +0,0 @@ -const { pathsToModuleNameMapper } = require('ts-jest'); -const { compilerOptions } = require('<%= tsconfigBasePath %>'); - -globalThis.ngJest = { - skipNgcc: true -}; - -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { - displayName: require('./package.json').name, - preset: 'jest-preset-angular', - setupFilesAfterEnv: ['/testing/setup-jest.ts'], - rootDir: '.', - moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths), - testPathIgnorePatterns: [ - '/dist', - ], - reporters: [ - 'default', - 'github-actions' - ], - globalSetup: 'jest-preset-angular/global-setup', - transform: { - // eslint-disable-next-line @typescript-eslint/naming-convention - '^.+\\.tsx?$': [ - 'jest-preset-angular', - { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\.html$' - } - ] - }, - snapshotSerializers: [ - 'jest-preset-angular/build/serializers/no-ng-attributes', - 'jest-preset-angular/build/serializers/ng-snapshot', - 'jest-preset-angular/build/serializers/html-comment', - ] -}; diff --git a/packages/@o3r/workspace/schematics/library/templates/nx/testing/setup-jest.ts.template b/packages/@o3r/workspace/schematics/library/templates/nx/testing/setup-jest.ts.template deleted file mode 100644 index ab68e1eb87..0000000000 --- a/packages/@o3r/workspace/schematics/library/templates/nx/testing/setup-jest.ts.template +++ /dev/null @@ -1,2 +0,0 @@ -import 'jest-preset-angular/setup-jest'; - diff --git a/tools/github-actions/new-version/packaged-action/LICENSE.txt b/tools/github-actions/new-version/packaged-action/LICENSE.txt index 7ee631dd7c..4ec329636a 100644 --- a/tools/github-actions/new-version/packaged-action/LICENSE.txt +++ b/tools/github-actions/new-version/packaged-action/LICENSE.txt @@ -94,6 +94,33 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @o3r/new-version +Copyright Amadeus SAS + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + @octokit/auth-token MIT