From 0d148a290ce9d630e2f0b109f9011778ca3e1bdf Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 3 Sep 2024 11:53:49 +0200 Subject: [PATCH 001/172] Convert the package to ESM. --- .../ckeditor5-dev-release-tools/lib/index.js | 72 ++++++------------- .../lib/tasks/cleanuppackages.js | 12 ++-- .../lib/tasks/commitandtag.js | 14 ++-- .../lib/tasks/creategithubrelease.js | 12 ++-- .../generatechangelogformonorepository.js | 42 ++++++----- .../generatechangelogforsinglepackage.js | 34 +++++---- .../lib/tasks/preparerepository.js | 12 ++-- .../lib/tasks/publishpackages.js | 22 +++--- .../lib/tasks/push.js | 10 ++- .../lib/tasks/reassignnpmtags.js | 24 +++---- .../lib/tasks/updatedependencies.js | 12 ++-- .../lib/tasks/updateversions.js | 16 ++--- .../tasks/verifypackagespublishedcorrectly.js | 14 ++-- .../lib/utils/abortcontroller.js | 6 +- .../lib/utils/assertfilestopublish.js | 12 ++-- .../lib/utils/assertnpmauthorization.js | 8 +-- .../lib/utils/assertnpmtag.js | 12 ++-- .../lib/utils/assertpackages.js | 10 ++- .../lib/utils/changelog.js | 12 ++-- .../lib/utils/checkversionavailability.js | 10 ++- .../lib/utils/cli.js | 10 ++- .../lib/utils/displaycommits.js | 14 ++-- .../lib/utils/displayskippedpackages.js | 14 ++-- .../lib/utils/executeinparallel.js | 23 +++--- .../lib/utils/generatechangelog.js | 12 ++-- .../lib/utils/getchangedfilesforcommit.js | 8 +-- .../lib/utils/getcommits.js | 18 +++-- .../lib/utils/getnewversiontype.js | 6 +- .../lib/utils/getnpmtagfromversion.js | 8 +-- .../lib/utils/getpackagejson.js | 10 ++- .../lib/utils/getpackagespaths.js | 14 ++-- .../lib/utils/getwriteroptions.js | 12 ++-- .../lib/utils/isversionpublishablefortag.js | 10 +-- .../lib/utils/parallelworker.cjs | 6 +- .../lib/utils/parseroptions.js | 4 +- .../lib/utils/publishpackageonnpmcallback.js | 14 ++-- .../lib/utils/transformcommitfactory.js | 12 ++-- .../lib/utils/transformcommitutils.js | 6 +- .../lib/utils/validaterepositorytorelease.js | 8 +-- .../lib/utils/versions.js | 10 ++- .../ckeditor5-dev-release-tools/package.json | 13 ++-- ...nuppackages.js => cleanuppackages.test.js} | 0 42 files changed, 243 insertions(+), 355 deletions(-) rename packages/ckeditor5-dev-release-tools/tests/tasks/{cleanuppackages.js => cleanuppackages.test.js} (100%) diff --git a/packages/ckeditor5-dev-release-tools/lib/index.js b/packages/ckeditor5-dev-release-tools/lib/index.js index 5fe738e25..f8e7f6909 100644 --- a/packages/ckeditor5-dev-release-tools/lib/index.js +++ b/packages/ckeditor5-dev-release-tools/lib/index.js @@ -3,20 +3,18 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const generateChangelogForSinglePackage = require( './tasks/generatechangelogforsinglepackage' ); -const generateChangelogForMonoRepository = require( './tasks/generatechangelogformonorepository' ); -const updateDependencies = require( './tasks/updatedependencies' ); -const commitAndTag = require( './tasks/commitandtag' ); -const createGithubRelease = require( './tasks/creategithubrelease' ); -const reassignNpmTags = require( './tasks/reassignnpmtags' ); -const prepareRepository = require( './tasks/preparerepository' ); -const push = require( './tasks/push' ); -const publishPackages = require( './tasks/publishpackages' ); -const updateVersions = require( './tasks/updateversions' ); -const cleanUpPackages = require( './tasks/cleanuppackages' ); -const { +export { default as generateChangelogForSinglePackage } from './tasks/generatechangelogforsinglepackage'; +export { default as generateChangelogForMonoRepository } from './tasks/generatechangelogformonorepository'; +export { default as updateDependencies } from './tasks/updatedependencies'; +export { default as commitAndTag } from './tasks/commitandtag'; +export { default as createGithubRelease } from './tasks/creategithubrelease'; +export { default as reassignNpmTags } from './tasks/reassignnpmtags'; +export { default as prepareRepository } from './tasks/preparerepository'; +export { default as push } from './tasks/push'; +export { default as publishPackages } from './tasks/publishpackages'; +export { default as updateVersions } from './tasks/updateversions'; +export { default as cleanUpPackages } from './tasks/cleanuppackages'; +export { getLastFromChangelog, getLastPreRelease, getNextPreRelease, @@ -24,41 +22,11 @@ const { getNextNightly, getCurrent, getLastTagFromGit -} = require( './utils/versions' ); -const { getChangesForVersion, getChangelog, saveChangelog } = require( './utils/changelog' ); -const executeInParallel = require( './utils/executeinparallel' ); -const validateRepositoryToRelease = require( './utils/validaterepositorytorelease' ); -const checkVersionAvailability = require( './utils/checkversionavailability' ); -const verifyPackagesPublishedCorrectly = require( './tasks/verifypackagespublishedcorrectly' ); -const getNpmTagFromVersion = require( './utils/getnpmtagfromversion' ); -const isVersionPublishableForTag = require( './utils/isversionpublishablefortag' ); - -module.exports = { - generateChangelogForSinglePackage, - generateChangelogForMonoRepository, - updateDependencies, - updateVersions, - prepareRepository, - commitAndTag, - createGithubRelease, - push, - cleanUpPackages, - publishPackages, - reassignNpmTags, - executeInParallel, - getLastFromChangelog, - getLastPreRelease, - getNextPreRelease, - getLastNightly, - getNextNightly, - getCurrent, - getLastTagFromGit, - getNpmTagFromVersion, - getChangesForVersion, - getChangelog, - saveChangelog, - validateRepositoryToRelease, - verifyPackagesPublishedCorrectly, - checkVersionAvailability, - isVersionPublishableForTag -}; +} from './utils/versions'; +export { getChangesForVersion, getChangelog, saveChangelog } from './utils/changelog'; +export { default as executeInParallel } from './utils/executeinparallel'; +export { default as validateRepositoryToRelease } from './utils/validaterepositorytorelease'; +export { default as checkVersionAvailability } from './utils/checkversionavailability'; +export { default as verifyPackagesPublishedCorrectly } from './tasks/verifypackagespublishedcorrectly'; +export { default as getNpmTagFromVersion } from './utils/getnpmtagfromversion'; +export { default as isVersionPublishableForTag } from './utils/isversionpublishablefortag'; diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js b/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js index 7e6e7c420..17dbb607a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs-extra' ); -const upath = require( 'upath' ); -const { glob } = require( 'glob' ); +import fs from 'fs-extra'; +import upath from 'upath'; +import { glob } from 'glob'; /** * The purpose of the script is to clean all packages prepared for the release. The cleaning consists of two stages: @@ -29,7 +27,7 @@ const { glob } = require( 'glob' ); * @param {String} [options.cwd] Current working directory from which all paths will be resolved. * @returns {Promise} */ -module.exports = async function cleanUpPackages( options ) { +export default async function cleanUpPackages( options ) { const { packagesDirectory, packageJsonFieldsToRemove, preservePostInstallHook, cwd } = parseOptions( options ); const packageJsonPaths = await glob( '*/package.json', { @@ -47,7 +45,7 @@ module.exports = async function cleanUpPackages( options ) { await fs.writeJson( packageJsonPath, packageJson, { spaces: 2 } ); } -}; +} /** * Prepares the configuration options for the script. diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js index 510e1a7be..4bf4b45f8 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const { toUnix } = require( 'upath' ); -const { glob } = require( 'glob' ); -const shellEscape = require( 'shell-escape' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { toUnix } from 'upath'; +import { glob } from 'glob'; +import shellEscape from 'shell-escape'; /** * Creates a commit and a tag for specified version. @@ -19,7 +17,7 @@ const shellEscape = require( 'shell-escape' ); * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ -module.exports = async function commitAndTag( { version, files, cwd = process.cwd() } ) { +export async function commitAndTag( { version, files, cwd = process.cwd() } ) { const normalizedCwd = toUnix( cwd ); const filePathsToAdd = await glob( files, { cwd: normalizedCwd, absolute: true, nodir: true } ); @@ -41,4 +39,4 @@ module.exports = async function commitAndTag( { version, files, cwd = process.cw const escapedVersion = shellEscape( [ version ] ); await tools.shExec( `git commit --message "Release: v${ escapedVersion }." --no-verify`, shExecOptions ); await tools.shExec( `git tag v${ escapedVersion }`, shExecOptions ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js index d7f49876d..cbdfb16e6 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { Octokit } = require( '@octokit/rest' ); -const semver = require( 'semver' ); -const { getRepositoryUrl } = require( '../utils/transformcommitutils' ); +import { Octokit } from '@octokit/rest'; +import semver from 'semver'; +import { getRepositoryUrl } from '../utils/transformcommitutils'; /** * Create a GitHub release. @@ -19,7 +17,7 @@ const { getRepositoryUrl } = require( '../utils/transformcommitutils' ); * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise.} */ -module.exports = async function createGithubRelease( options ) { +export async function createGithubRelease( options ) { const { token, version, @@ -46,7 +44,7 @@ module.exports = async function createGithubRelease( options ) { } return `https://github.com/${ repositoryOwner }/${ repositoryName }/releases/tag/v${ version }`; -}; +} /** * Returns an npm tag based on the specified release version. diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js index 85e798fda..7dbe8f24f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js @@ -3,26 +3,24 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const { tools, logger } = require( '@ckeditor/ckeditor5-dev-utils' ); -const compareFunc = require( 'compare-func' ); -const chalk = require( 'chalk' ); -const semver = require( 'semver' ); -const changelogUtils = require( '../utils/changelog' ); -const cli = require( '../utils/cli' ); -const displayCommits = require( '../utils/displaycommits' ); -const displaySkippedPackages = require( '../utils/displayskippedpackages' ); -const generateChangelog = require( '../utils/generatechangelog' ); -const getPackageJson = require( '../utils/getpackagejson' ); -const getPackagesPaths = require( '../utils/getpackagespaths' ); -const getCommits = require( '../utils/getcommits' ); -const getNewVersionType = require( '../utils/getnewversiontype' ); -const getWriterOptions = require( '../utils/getwriteroptions' ); -const { getRepositoryUrl } = require( '../utils/transformcommitutils' ); -const transformCommitFactory = require( '../utils/transformcommitfactory' ); +import fs from 'fs'; +import path from 'path'; +import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; +import compareFunc from 'compare-func'; +import chalk from 'chalk'; +import semver from 'semver'; +import changelogUtils from '../utils/changelog'; +import cli from '../utils/cli'; +import displayCommits from '../utils/displaycommits'; +import displaySkippedPackages from '../utils/displayskippedpackages'; +import generateChangelog from '../utils/generatechangelog'; +import getPackageJson from '../utils/getpackagejson'; +import getPackagesPaths from '../utils/getpackagespaths'; +import getCommits from '../utils/getcommits'; +import getNewVersionType from '../utils/getnewversiontype'; +import getWriterOptions from '../utils/getwriteroptions'; +import { getRepositoryUrl } from '../utils/transformcommitutils'; +import transformCommitFactory from '../utils/transformcommitfactory'; const VERSIONING_POLICY_URL = 'https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html'; const noteInfo = `[ℹī¸](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-changes)`; @@ -63,7 +61,7 @@ const noteInfo = `[ℹī¸](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-c * * @returns {Promise.} */ -module.exports = async function generateChangelogForMonoRepository( options ) { +export async function generateChangelogForMonoRepository( options ) { const log = logger(); const cwd = process.cwd(); const rootPkgJson = getPackageJson( options.cwd ); @@ -723,7 +721,7 @@ module.exports = async function generateChangelogForMonoRepository( options ) { log[ method ]( `${ startWithNewLine ? '\n' : '' }${ ' '.repeat( indentLevel * cli.INDENT_SIZE ) }` + message ); } -}; +} /** * @typedef {Object} Version diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js index 660591680..b538200ce 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js @@ -3,22 +3,20 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const { tools, logger } = require( '@ckeditor/ckeditor5-dev-utils' ); -const chalk = require( 'chalk' ); -const semver = require( 'semver' ); -const cli = require( '../utils/cli' ); -const changelogUtils = require( '../utils/changelog' ); -const displayCommits = require( '../utils/displaycommits' ); -const generateChangelog = require( '../utils/generatechangelog' ); -const getPackageJson = require( '../utils/getpackagejson' ); -const getNewVersionType = require( '../utils/getnewversiontype' ); -const getCommits = require( '../utils/getcommits' ); -const getWriterOptions = require( '../utils/getwriteroptions' ); -const { getRepositoryUrl } = require( '../utils/transformcommitutils' ); -const transformCommitFactory = require( '../utils/transformcommitfactory' ); +import fs from 'fs'; +import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; +import chalk from 'chalk'; +import semver from 'semver'; +import cli from '../utils/cli'; +import changelogUtils from '../utils/changelog'; +import displayCommits from '../utils/displaycommits'; +import generateChangelog from '../utils/generatechangelog'; +import getPackageJson from '../utils/getpackagejson'; +import getNewVersionType from '../utils/getnewversiontype'; +import getCommits from '../utils/getcommits'; +import getWriterOptions from '../utils/getwriteroptions'; +import { getRepositoryUrl } from '../utils/transformcommitutils'; +import transformCommitFactory from '../utils/transformcommitfactory'; const SKIP_GENERATE_CHANGELOG = 'Typed "skip" as a new version. Aborting.'; @@ -40,7 +38,7 @@ const SKIP_GENERATE_CHANGELOG = 'Typed "skip" as a new version. Aborting.'; * * @returns {Promise} */ -module.exports = async function generateChangelogForSinglePackage( options = {} ) { +export async function generateChangelogForSinglePackage( options = {} ) { const log = logger(); const pkgJson = getPackageJson(); @@ -195,7 +193,7 @@ module.exports = async function generateChangelogForSinglePackage( options = {} log[ method ]( `${ startWithNewLine ? '\n' : '' }${ ' '.repeat( indentLevel * cli.INDENT_SIZE ) }` + message ); } -}; +} /** * @callback FormatDateCallback diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js index 048a5396b..b8b3be569 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs-extra' ); -const glob = require( 'glob' ); -const upath = require( 'upath' ); +import fs from 'fs-extra'; +import glob from 'glob'; +import upath from 'upath'; /** * The goal is to prepare the release directory containing the packages we want to publish. @@ -23,7 +21,7 @@ const upath = require( 'upath' ); * If not specified, the root package will not be created. * @returns {Promise} */ -module.exports = async function prepareRepository( options ) { +export async function prepareRepository( options ) { const { outputDirectory, packagesDirectory, @@ -70,7 +68,7 @@ module.exports = async function prepareRepository( options ) { } return Promise.all( copyPromises ); -}; +} /** * @param {Object} packageJson diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js index 256c70b48..1debef2e4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js @@ -3,16 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const upath = require( 'upath' ); -const { glob } = require( 'glob' ); -const assertNpmAuthorization = require( '../utils/assertnpmauthorization' ); -const assertPackages = require( '../utils/assertpackages' ); -const assertNpmTag = require( '../utils/assertnpmtag' ); -const assertFilesToPublish = require( '../utils/assertfilestopublish' ); -const executeInParallel = require( '../utils/executeinparallel' ); -const publishPackageOnNpmCallback = require( '../utils/publishpackageonnpmcallback' ); +import upath from 'upath'; +import { glob } from 'glob'; +import assertNpmAuthorization from '../utils/assertnpmauthorization'; +import assertPackages from '../utils/assertpackages'; +import assertNpmTag from '../utils/assertnpmtag'; +import assertFilesToPublish from '../utils/assertfilestopublish'; +import executeInParallel from '../utils/executeinparallel'; +import publishPackageOnNpmCallback from '../utils/publishpackageonnpmcallback'; /** * The purpose of the script is to validate the packages prepared for the release and then release them on npm. @@ -46,7 +44,7 @@ const publishPackageOnNpmCallback = require( '../utils/publishpackageonnpmcallba * @param {Number} [options.concurrency=4] Number of CPUs that will execute the task. * @returns {Promise} */ -module.exports = async function publishPackages( options ) { +export async function publishPackages( options ) { const { packagesDirectory, npmOwner, @@ -87,4 +85,4 @@ module.exports = async function publishPackages( options ) { concurrency } ); } -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/push.js b/packages/ckeditor5-dev-release-tools/lib/tasks/push.js index ff6f1aebe..1ed786b73 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/push.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/push.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const shellEscape = require( 'shell-escape' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import shellEscape from 'shell-escape'; /** * Push the local changes to a remote server. @@ -17,7 +15,7 @@ const shellEscape = require( 'shell-escape' ); * @param {String} [options.cwd] Root of the repository to prepare. `process.cwd()` by default. * @returns {Promise} */ -module.exports = async function push( options ) { +export async function push( options ) { const { releaseBranch, version, @@ -27,4 +25,4 @@ module.exports = async function push( options ) { const command = `git push origin ${ shellEscape( [ releaseBranch ] ) } v${ shellEscape( [ version ] ) }`; return tools.shExec( command, { cwd, verbosity: 'error', async: true } ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js index 68d22f3fb..833b1b99f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js @@ -5,17 +5,15 @@ * For licensing, see LICENSE.md. */ -/* eslint-env node */ +import chalk from 'chalk'; +import columns from 'cli-columns'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import util from 'util'; +import shellEscape from 'shell-escape'; +import assertNpmAuthorization from '../utils/assertnpmauthorization'; +import { exec } from 'child_process'; -'use strict'; - -const chalk = require( 'chalk' ); -const columns = require( 'cli-columns' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const util = require( 'util' ); -const shellEscape = require( 'shell-escape' ); -const exec = util.promisify( require( 'child_process' ).exec ); -const assertNpmAuthorization = require( '../utils/assertnpmauthorization' ); +const execPromise = util.promisify( exec ); /** * Used to switch the tags from `staging` to `latest` for specified array of packages. @@ -27,7 +25,7 @@ const assertNpmAuthorization = require( '../utils/assertnpmauthorization' ); * @param {Array.} options.packages Array of packages' names to reassign tags for. * @returns {Promise} */ -module.exports = async function reassignNpmTags( { npmOwner, version, packages } ) { +export async function reassignNpmTags( { npmOwner, version, packages } ) { const errors = []; const packagesSkipped = []; const packagesUpdated = []; @@ -39,7 +37,7 @@ module.exports = async function reassignNpmTags( { npmOwner, version, packages } const updateTagPromises = packages.map( async packageName => { const command = `npm dist-tag add ${ shellEscape( [ packageName ] ) }@${ shellEscape( [ version ] ) } latest`; - const updateLatestTagRetryable = retry( () => exec( command ) ); + const updateLatestTagRetryable = retry( () => execPromise( command ) ); await updateLatestTagRetryable() .then( response => { if ( response.stdout ) { @@ -78,7 +76,7 @@ module.exports = async function reassignNpmTags( { npmOwner, version, packages } console.log( chalk.bold.red( '🐛 Errors found:' ) ); errors.forEach( msg => console.log( `* ${ msg }` ) ); } -}; +} /** * @param {String} message diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js index ddd078be7..3e8ed58b3 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs-extra' ); -const { glob } = require( 'glob' ); -const upath = require( 'upath' ); +import fs from 'fs-extra'; +import { glob } from 'glob'; +import upath from 'upath'; /** * The purpose of this script is to update all eligible dependencies to a version specified in the `options.version`. The following packages @@ -31,7 +29,7 @@ const upath = require( 'upath' ); * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ -module.exports = async function updateDependencies( options ) { +export async function updateDependencies( options ) { const { version, packagesDirectory, @@ -59,7 +57,7 @@ module.exports = async function updateDependencies( options ) { await fs.writeJson( pkgJsonPath, pkgJson, { spaces: 2 } ); } -}; +} /** * Updates the version for each eligible dependency. diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js index 96010dbc7..1372d9659 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js @@ -3,13 +3,11 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { glob } = require( 'glob' ); -const fs = require( 'fs-extra' ); -const semver = require( 'semver' ); -const { normalizeTrim, toUnix, dirname, join } = require( 'upath' ); -const checkVersionAvailability = require( '../utils/checkversionavailability' ); +import { glob } from 'glob'; +import fs from 'fs-extra'; +import semver from 'semver'; +import { normalizeTrim, toUnix, dirname, join } from 'upath'; +import checkVersionAvailability from '../utils/checkversionavailability'; /** * The purpose of the script is to update the version of a root package found in the current working @@ -31,7 +29,7 @@ const checkVersionAvailability = require( '../utils/checkversionavailability' ); * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ -module.exports = async function updateVersions( options ) { +export async function updateVersions( options ) { const { packagesDirectory, version, @@ -63,7 +61,7 @@ module.exports = async function updateVersions( options ) { pkgJson.version = version; await fs.writeJson( pkgJsonPath, pkgJson, { spaces: 2 } ); } -}; +} /** * @param {String} cwd diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js index eda72f483..79beb9238 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const upath = require( 'upath' ); -const { glob } = require( 'glob' ); -const fs = require( 'fs-extra' ); -const { checkVersionAvailability } = require( '../utils/checkversionavailability' ); +import upath from 'upath'; +import { glob } from 'glob'; +import fs from 'fs-extra'; +import { checkVersionAvailability } from '../utils/checkversionavailability'; /** * Npm sometimes throws incorrect error 409 while publishing, while the package uploads correctly. @@ -20,7 +18,7 @@ const { checkVersionAvailability } = require( '../utils/checkversionavailability * @param {Function} options.onSuccess Callback fired when function is successful. * @returns {Promise} */ -module.exports = async function verifyPackagesPublishedCorrectly( options ) { +export async function verifyPackagesPublishedCorrectly( options ) { const { packagesDirectory, version, onSuccess } = options; const packagesToVerify = await glob( upath.join( packagesDirectory, '*' ), { absolute: true } ); const errors = []; @@ -52,4 +50,4 @@ module.exports = async function verifyPackagesPublishedCorrectly( options ) { } onSuccess( 'All packages that returned 409 were uploaded correctly.' ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js b/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js index 0fcb83352..fbda9ad24 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js @@ -3,10 +3,6 @@ * For licensing, see LICENSE.md. */ -/* eslint-env node */ - -'use strict'; - /** * Creates an AbortController instance and registers the listener function on SIGINT event that aborts the asynchronous process. * @@ -41,7 +37,7 @@ function deregisterAbortController( abortController ) { process.removeListener( 'SIGINT', abortController._listener ); } -module.exports = { +export { registerAbortController, deregisterAbortController }; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js index 3ae0ebbff..fab39bab2 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs-extra' ); -const upath = require( 'upath' ); -const { glob } = require( 'glob' ); +import fs from 'fs-extra'; +import upath from 'upath'; +import { glob } from 'glob'; /** * Checks if all files expected to be released actually exist in the package directory. Verification takes place for all packages. @@ -16,7 +14,7 @@ const { glob } = require( 'glob' ); * @param {Object.>|null} optionalEntries * @returns {Promise} */ -module.exports = async function assertFilesToPublish( packagePaths, optionalEntries ) { +export async function assertFilesToPublish( packagePaths, optionalEntries ) { const errors = []; for ( const packagePath of packagePaths ) { @@ -59,7 +57,7 @@ module.exports = async function assertFilesToPublish( packagePaths, optionalEntr if ( errors.length ) { throw new Error( errors.join( '\n' ) ); } -}; +} /** * Filters out the optional entries from the `files` field and returns only the required ones. diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js index 498023670..71104be35 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** * Checks whether a user is logged to npm as the provided account name. @@ -13,7 +11,7 @@ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); * @param {String} npmOwner Expected npm account name that should be logged into npm. * @returns {Promise} */ -module.exports = async function assertNpmAuthorization( npmOwner ) { +export async function assertNpmAuthorization( npmOwner ) { return tools.shExec( 'npm whoami', { verbosity: 'error', async: true } ) .then( npmCurrentUser => { if ( npmOwner !== npmCurrentUser.trim() ) { @@ -23,4 +21,4 @@ module.exports = async function assertNpmAuthorization( npmOwner ) { .catch( () => { throw new Error( `You must be logged to npm as "${ npmOwner }" to execute this release step.` ); } ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js index e6ec54d8e..cbc989e8e 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs-extra' ); -const upath = require( 'upath' ); -const semver = require( 'semver' ); +import fs from 'fs-extra'; +import upath from 'upath'; +import semver from 'semver'; /** * Checks if the npm tag matches the tag calculated from the package version. Verification takes place for all packages. @@ -16,7 +14,7 @@ const semver = require( 'semver' ); * @param {String} npmTag * @returns {Promise} */ -module.exports = async function assertNpmTag( packagePaths, npmTag ) { +export async function assertNpmTag( packagePaths, npmTag ) { const errors = []; for ( const packagePath of packagePaths ) { @@ -38,7 +36,7 @@ module.exports = async function assertNpmTag( packagePaths, npmTag ) { if ( errors.length ) { throw new Error( errors.join( '\n' ) ); } -}; +} /** * Returns the version tag for the package. diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js index dbc5226e0..1b74cab74 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs-extra' ); -const upath = require( 'upath' ); +import fs from 'fs-extra'; +import upath from 'upath'; /** * Checks if all packages in the provided directories contain the `package.json` file. @@ -19,7 +17,7 @@ const upath = require( 'upath' ); * this array contains a list of packages that will not be checked. In other words, they do not have to define the entry point. * @returns {Promise} */ -module.exports = async function assertPackages( packagePaths, options ) { +export async function assertPackages( packagePaths, options ) { const errors = []; const { requireEntryPoint, optionalEntryPointPackages } = options; @@ -49,4 +47,4 @@ module.exports = async function assertPackages( packagePaths, options ) { if ( errors.length ) { throw new Error( errors.join( '\n' ) ); } -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js index 6f1fc4b59..b7b80feec 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const { format } = require( 'date-fns' ); -const { getRepositoryUrl } = require( './transformcommitutils' ); +import fs from 'fs'; +import path from 'path'; +import { format } from 'date-fns'; +import { getRepositoryUrl } from './transformcommitutils'; const utils = { /** @@ -106,4 +104,4 @@ const utils = { } }; -module.exports = utils; +export default utils; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js b/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js index 547265df4..1c6abc3de 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const shellEscape = require( 'shell-escape' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import shellEscape from 'shell-escape'; /** * Checks if the provided version for the package exists in the npm registry. @@ -18,7 +16,7 @@ const shellEscape = require( 'shell-escape' ); * @param {String} packageName * @returns {Promise} */ -module.exports = async function checkVersionAvailability( version, packageName ) { +export async function checkVersionAvailability( version, packageName ) { const command = `npm show ${ shellEscape( [ packageName ] ) }@${ shellEscape( [ version ] ) } version`; return tools.shExec( command, { verbosity: 'silent', async: true } ) @@ -42,4 +40,4 @@ module.exports = async function checkVersionAvailability( version, packageName ) // Npm < 8.13.0 should never reach this line. return true; } ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/cli.js b/packages/ckeditor5-dev-release-tools/lib/utils/cli.js index 7fa0caed4..8db21a515 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/cli.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/cli.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const inquirer = require( 'inquirer' ); -const semver = require( 'semver' ); -const chalk = require( 'chalk' ); +import inquirer from 'inquirer'; +import semver from 'semver'; +import chalk from 'chalk'; const QUESTION_MARK = chalk.cyan( '?' ); @@ -342,7 +340,7 @@ const cli = { } }; -module.exports = cli; +export default cli; function getPrefix( indent ) { return ' '.repeat( indent * cli.INDENT_SIZE ) + QUESTION_MARK; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js index caa117e8a..1e7c5d269 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); -const utils = require( './transformcommitutils' ); -const { INDENT_SIZE, COMMIT_INDENT_SIZE } = require( './cli' ); +import chalk from 'chalk'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import utils from './transformcommitutils'; +import { INDENT_SIZE, COMMIT_INDENT_SIZE } from './cli'; /** * @param {Array.|Set.} commits @@ -16,7 +14,7 @@ const { INDENT_SIZE, COMMIT_INDENT_SIZE } = require( './cli' ); * @param {Boolean} [options.attachLinkToCommit=false] Whether to attach a link to parsed commit. * @param {Number} [options.indentLevel=1] The indent level. */ -module.exports = function displayCommits( commits, options = {} ) { +export function displayCommits( commits, options = {} ) { const log = logger(); const attachLinkToCommit = options.attachLinkToCommit || false; @@ -102,4 +100,4 @@ module.exports = function displayCommits( commits, options = {} ) { return commitGroups.get( commit.hash ); } -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js b/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js index edbba4657..f492da277 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js @@ -3,19 +3,17 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); -const getPackageJson = require( './getpackagejson' ); -const { INDENT_SIZE } = require( './cli' ); +import chalk from 'chalk'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import getPackageJson from './getpackagejson'; +import { INDENT_SIZE } from './cli'; /** * Displays skipped packages. * * @param {Set} skippedPackagesPaths */ -module.exports = function displaySkippedPackages( skippedPackagesPaths ) { +export function displaySkippedPackages( skippedPackagesPaths ) { if ( !skippedPackagesPaths.size ) { return; } @@ -29,4 +27,4 @@ module.exports = function displaySkippedPackages( skippedPackagesPaths ) { message += packageNames.map( line => indent + ` * ${ line }` ).join( '\n' ); logger().info( message ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js index 3ec2c208d..89b258afb 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js @@ -5,14 +5,13 @@ /* eslint-env node */ -'use strict'; - -const crypto = require( 'crypto' ); -const upath = require( 'upath' ); -const fs = require( 'fs/promises' ); -const { Worker } = require( 'worker_threads' ); -const { glob } = require( 'glob' ); -const { registerAbortController, deregisterAbortController } = require( './abortcontroller' ); +import crypto from 'crypto'; +import upath from 'upath'; +import os from 'os'; +import fs from 'fs/promises'; +import { Worker } from 'worker_threads'; +import { glob } from 'glob'; +import { registerAbortController, deregisterAbortController } from './abortcontroller'; const WORKER_SCRIPT = upath.join( __dirname, 'parallelworker.cjs' ); @@ -37,7 +36,7 @@ const WORKER_SCRIPT = upath.join( __dirname, 'parallelworker.cjs' ); * @param {Number} [options.concurrency=require( 'os' ).cpus().length / 2] Number of CPUs that will execute the task. * @returns {Promise} */ -module.exports = async function executeInParallel( options ) { +export async function executeInParallel( options ) { const { packagesDirectory, taskToExecute, @@ -46,7 +45,7 @@ module.exports = async function executeInParallel( options ) { taskOptions = null, packagesDirectoryFilter = null, cwd = process.cwd(), - concurrency = require( 'os' ).cpus().length / 2 + concurrency = os.cpus().length / 2 } = options; const normalizedCwd = upath.toUnix( cwd ); @@ -62,7 +61,7 @@ module.exports = async function executeInParallel( options ) { const packagesInThreads = getPackagesGroupedByThreads( packagesToProcess, concurrency ); const callbackModule = upath.join( cwd, crypto.randomUUID() + '.cjs' ); - await fs.writeFile( callbackModule, `'use strict';\nmodule.exports = ${ taskToExecute };`, 'utf-8' ); + await fs.writeFile( callbackModule, `'use strict';\nexport ${ taskToExecute };`, 'utf-8' ); const onPackageDone = progressFactory( listrTask, packagesToProcess.length ); @@ -96,7 +95,7 @@ module.exports = async function executeInParallel( options ) { deregisterAbortController( defaultAbortController ); } } ); -}; +} /** * @param {ListrTaskObject} listrTask diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js index b0eb4ee60..99af19898 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { Readable } = require( 'stream' ); -const { stream } = require( '@ckeditor/ckeditor5-dev-utils' ); -const conventionalChangelogWriter = require( 'conventional-changelog-writer' ); +import { Readable } from 'stream'; +import { stream } from '@ckeditor/ckeditor5-dev-utils'; +import conventionalChangelogWriter from 'conventional-changelog-writer'; const UPDATED_TRANSLATION_COMMIT = '* Updated translations.'; @@ -38,7 +36,7 @@ const UPDATED_TRANSLATION_COMMIT = '* Updated translations.'; * * @returns {Promise.} */ -module.exports = function generateChangelog( commits, context, options ) { +export function generateChangelog( commits, context, options ) { const commitStream = new Readable( { objectMode: true } ); /* istanbul ignore next */ commitStream._read = function() {}; @@ -61,7 +59,7 @@ module.exports = function generateChangelog( commits, context, options ) { } ) ) .on( 'error', reject ); } ); -}; +} /** * Merges multiple "Updated translations." entries into the single commit. diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js index 311a2f36d..3688edfc4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** * Returns an array with paths to changed files for given commit. @@ -13,7 +11,7 @@ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); * @param {String} commitId * @returns {Array.} */ -module.exports = function getChangedFilesForCommit( commitId ) { +export function getChangedFilesForCommit( commitId ) { const gitCommand = `git log -m -1 --name-only --pretty="format:" ${ commitId }`; const changedFiles = tools.shExec( gitCommand, { verbosity: 'error' } ).trim(); @@ -30,4 +28,4 @@ module.exports = function getChangedFilesForCommit( commitId ) { .split( '\n' ) .map( file => file.trim() ) .filter( item => item ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js index e92fd06f6..2faa99149 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js @@ -3,14 +3,12 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const conventionalCommitsParser = require( 'conventional-commits-parser' ); -const conventionalCommitsFilter = require( 'conventional-commits-filter' ); -const gitRawCommits = require( 'git-raw-commits' ); -const concat = require( 'concat-stream' ); -const parserOptions = require( './parseroptions' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +import conventionalCommitsParser from 'conventional-commits-parser'; +import conventionalCommitsFilter from 'conventional-commits-filter'; +import gitRawCommits from 'git-raw-commits'; +import concat from 'concat-stream'; +import parserOptions from './parseroptions'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** * Returns a promise that resolves an array of commits since the last tag specified as `options.from`. @@ -22,7 +20,7 @@ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); * @param {String} [options.mainBranch='master'] A name of the main branch in the repository. * @returns {Promise.>} */ -module.exports = function getCommits( transformCommit, options = {} ) { +export function getCommits( transformCommit, options = {} ) { const releaseBranch = options.releaseBranch || 'master'; const mainBranch = options.mainBranch || 'master'; @@ -101,4 +99,4 @@ module.exports = function getCommits( transformCommit, options = {} ) { function exec( command ) { return tools.shExec( command, { verbosity: 'error' } ); } -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js b/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js index e0bcda677..5f7490c43 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js @@ -3,15 +3,13 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Proposes new version based on commits. * * @param {Array.} commits * @returns {String|null} */ -module.exports = function getNewVersionType( commits ) { +export function getNewVersionType( commits ) { // No commits = no changes. if ( !commits.length ) { return 'skip'; @@ -50,4 +48,4 @@ module.exports = function getNewVersionType( commits ) { } return 'patch'; -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js b/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js index 2d5b8c488..cca489914 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js @@ -3,16 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const semver = require( 'semver' ); +import semver from 'semver'; /** * @param {String} version * @returns {String} */ -module.exports = function getNpmTagFromVersion( version ) { +export function getNpmTagFromVersion( version ) { const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ]; return versionTag; -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js index ffe67412a..1be20a5ca 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const upath = require( 'upath' ); +import fs from 'fs'; +import upath from 'upath'; /** * Returns object from `package.json`. @@ -17,7 +15,7 @@ const upath = require( 'upath' ); * @param {String} [cwd=process.cwd()] Where to look for package.json. * @returns {Object} */ -module.exports = function getPackageJson( cwd = process.cwd() ) { +export function getPackageJson( cwd = process.cwd() ) { let pkgJsonPath = cwd; if ( !pkgJsonPath.endsWith( 'package.json' ) ) { @@ -25,4 +23,4 @@ module.exports = function getPackageJson( cwd = process.cwd() ) { } return JSON.parse( fs.readFileSync( pkgJsonPath, 'utf-8' ) ); -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js index 7d72d06d6..224b468c3 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const minimatch = require( 'minimatch' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const getPackageJson = require( './getpackagejson' ); +import path from 'path'; +import minimatch from 'minimatch'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import getPackageJson from './getpackagejson'; /** * Returns an object with two collections of paths to packages which are located in single repository. @@ -28,7 +26,7 @@ const getPackageJson = require( './getpackagejson' ); * @param {Boolean} [options.skipMainRepository=false] If set on true, package found in `options.cwd` will be skipped. * @returns {PathsCollection} */ -module.exports = function getPackagesPaths( options ) { +export function getPackagesPaths( options ) { const pathsCollection = { matched: new Set(), skipped: new Set() @@ -79,7 +77,7 @@ module.exports = function getPackagesPaths( options ) { return true; } -}; +} /** * @typedef {Object} PathsCollection diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js index bdc5918a0..ca6a31638 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js @@ -3,18 +3,16 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); +import fs from 'fs'; +import path from 'path'; const templatePath = path.join( __dirname, '..', 'templates' ); -const { getTypeOrder } = require( './transformcommitutils' ); +import { getTypeOrder } from './transformcommitutils'; /** * @param {Function|Object} transform * @returns {Object} */ -module.exports = function getWriterOptions( transform ) { +export function getWriterOptions( transform ) { return { transform, groupBy: 'type', @@ -26,7 +24,7 @@ module.exports = function getWriterOptions( transform ) { commitPartial: fs.readFileSync( path.join( templatePath, 'commit.hbs' ), 'utf-8' ), footerPartial: fs.readFileSync( path.join( templatePath, 'footer.hbs' ), 'utf-8' ) }; -}; +} function sortFunction( a, b ) { return getTypeOrder( a.title ) - getTypeOrder( b.title ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js b/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js index 2b9a13856..b088a7e7f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js @@ -3,9 +3,9 @@ * For licensing, see LICENSE.md. */ -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const semver = require( 'semver' ); -const shellEscape = require( 'shell-escape' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import semver from 'semver'; +import shellEscape from 'shell-escape'; /** * This util aims to verify if the given `packageName` can be published with the given `version` on the `npmTag`. @@ -15,7 +15,7 @@ const shellEscape = require( 'shell-escape' ); * @param {String} npmTag * @return {Promise.} */ -module.exports = async function isVersionPublishableForTag( packageName, version, npmTag ) { +export async function isVersionPublishableForTag( packageName, version, npmTag ) { const command = `npm view ${ shellEscape( [ packageName ] ) }@${ shellEscape( [ npmTag ] ) } version --silent`; const npmVersion = await tools.shExec( command, { async: true, verbosity: 'silent' } ) .then( value => value.trim() ) @@ -27,4 +27,4 @@ module.exports = async function isVersionPublishableForTag( packageName, version } return true; -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.cjs b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.cjs index 49b6adb4e..41231633a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.cjs +++ b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.cjs @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - // This file is covered by the "executeInParallel() - integration" test cases. // Required due to top-level await. @@ -13,7 +11,9 @@ * @param {String} callbackModule * @param {Array.} packages */ - const { parentPort, workerData } = require( 'worker_threads' ); + import { parentPort, workerData } from 'worker_threads'; + + // TODO: To decide what to do. const callback = require( workerData.callbackModule ); for ( const packagePath of workerData.packages ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/parseroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/parseroptions.js index a7d6ed298..ff54cac4b 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/parseroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/parseroptions.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = { +export default { mergePattern: /^Merge .*$/, headerPattern: /^([^:]+): (.*)$/, headerCorrespondence: [ diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js index fb9b5dcb5..59041f85a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js @@ -3,9 +3,9 @@ * For licensing, see LICENSE.md. */ -/* eslint-env node */ - -'use strict'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import upath from 'upath'; +import fs from 'fs-extra'; /** * Calls the npm command to publish the package. When a package is successfully published, it is removed from the filesystem. @@ -15,11 +15,7 @@ * @param {String} taskOptions.npmTag * @returns {Promise} */ -module.exports = async function publishPackageOnNpmCallback( packagePath, taskOptions ) { - const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); - const upath = require( 'upath' ); - const fs = require( 'fs-extra' ); - +export async function publishPackageOnNpmCallback( packagePath, taskOptions ) { const result = await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, { cwd: packagePath, async: true, @@ -38,4 +34,4 @@ module.exports = async function publishPackageOnNpmCallback( packagePath, taskOp if ( !result || !result.shouldKeepDirectory ) { await fs.remove( packagePath ); } -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js index 5835ac426..41233f5f9 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { cloneDeepWith } = require( 'lodash' ); -const utils = require( './transformcommitutils' ); -const getChangedFilesForCommit = require( './getchangedfilesforcommit' ); +import { cloneDeepWith } from 'lodash'; +import utils from './transformcommitutils'; +import getChangedFilesForCommit from './getchangedfilesforcommit'; // Squash commit follows the pattern: "A pull request title (#{number})". const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; @@ -31,7 +29,7 @@ const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; * as "BREAKING CHANGES". * @returns {TransformCommit} */ -module.exports = function transformCommitFactory( options = {} ) { +export function transformCommitFactory( options = {} ) { return rawCommit => { const commit = transformCommit( rawCommit ); @@ -484,7 +482,7 @@ module.exports = function transformCommitFactory( options = {} ) { return !!squashCommit.header.match( SQUASH_COMMIT_REGEXP ); } -}; +} /** * @callback TransformCommit diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js index b456c7cd4..473ca7e12 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const getPackageJson = require( './getpackagejson' ); +import getPackageJson from './getpackagejson'; const transformCommitUtils = { /** @@ -160,4 +158,4 @@ const transformCommitUtils = { } }; -module.exports = transformCommitUtils; +export default transformCommitUtils; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js b/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js index 6a38e894e..55731a7ce 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** * @param {Object} options @@ -15,7 +13,7 @@ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); * @param {String} [options.branch='master'] A name of the branch that should be used for releasing packages. * @returns {Promise.>} */ -module.exports = async function validateRepositoryToRelease( options ) { +export async function validateRepositoryToRelease( options ) { const { version, changes, @@ -56,4 +54,4 @@ module.exports = async function validateRepositoryToRelease( options ) { async function exec( command ) { return tools.shExec( command, { verbosity: 'error', async: true } ); } -}; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js index b7137c717..70e3d7946 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const changelogUtils = require( './changelog' ); -const getPackageJson = require( './getpackagejson' ); +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import changelogUtils from './changelog'; +import getPackageJson from './getpackagejson'; const versions = { /** @@ -132,7 +130,7 @@ const versions = { } }; -module.exports = versions; +export default versions; /** * @typedef {String} ReleaseIdentifier The pre-release identifier without the last dynamic part (the pre-release sequential number). diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 16fdb2207..6240928dc 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -16,6 +16,7 @@ "node": ">=18.0.0", "npm": ">=5.7.1" }, + "type": "module", "main": "lib/index.js", "files": [ "lib" @@ -42,16 +43,12 @@ "upath": "^2.0.1" }, "devDependencies": { - "chai": "^4.2.0", + "vitest": "^2.0.5", "handlebars": "^4.7.6", - "mocha": "^7.1.2", - "mockery": "^2.1.0", - "mock-fs": "^5.1.2", - "proxyquire": "^2.1.3", - "sinon": "^9.2.4" + "mock-fs": "^5.1.2" }, "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" + "test": "vitest run --config vitest.config.ts", + "coverage": "vitest run --config vitest.config.ts --coverage" } } diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js similarity index 100% rename from packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js rename to packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js From 98989f52991ef8bf38f21e074a90d8824430e7bc Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 3 Sep 2024 11:54:36 +0200 Subject: [PATCH 002/172] Vitest init + first test migrated. --- .../tests/tasks/cleanuppackages.test.js | 1329 ++++++++--------- .../vitest.config.js | 20 + 2 files changed, 682 insertions(+), 667 deletions(-) create mode 100644 packages/ckeditor5-dev-release-tools/vitest.config.js diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js index 5f925b337..59359ba8b 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js @@ -5,759 +5,754 @@ 'use strict'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); -const fs = require( 'fs-extra' ); -const upath = require( 'upath' ); -const { glob } = require( 'glob' ); - -const mockFs = require( 'mock-fs' ); +import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import upath from 'upath'; +import { glob } from 'glob'; +import mockFs from 'mock-fs'; + +describe( 'dev-release-tools/tasks/cleanUpPackages()', () => { + let cleanUpPackages, stubs; + + beforeEach( async () => { + // Calls to `fs` and `glob` are stubbed, but they are passed through to the real implementation, because we want to test the + // real behavior of the script. The whole filesystem is mocked by the `mock-fs` util for testing purposes. A virtual project is + // prepared in tests on this mocked filesystem. + vi.doMock( 'glob', () => ( { + glob: vi.fn().mockImplementation( glob ) + } ) ); + vi.doMock( 'fs-extra', () => ( { + default: { + readJson: vi.fn().mockImplementation( fs.readJson ), + writeJson: vi.fn().mockImplementation( fs.writeJson ), + remove: vi.fn().mockImplementation( fs.remove ), + readdir: vi.fn().mockImplementation( fs.readdir ) + } + } ) ); + + stubs = { + ...await import( 'glob' ), + ...( await import( 'fs-extra' ) ).default + }; + + cleanUpPackages = ( await import( '../../lib/tasks/cleanuppackages' ) ).default; + } ); -describe( 'dev-release-tools/tasks', () => { - describe( 'cleanUpPackages()', () => { - let cleanUpPackages, sandbox, stubs; + afterEach( () => { + vi.resetModules(); + } ); + describe( 'preparing options', () => { beforeEach( () => { - sandbox = sinon.createSandbox(); - - // Calls to `fs` and `glob` are stubbed, but they are passed through to the real implementation, because we want to test the - // real behavior of the script. The whole filesystem is mocked by the `mock-fs` util for testing purposes. A virtual project is - // prepared in tests on this mocked filesystem. - stubs = { - fs: { - readJson: sandbox.stub().callsFake( ( ...args ) => fs.readJson( ...args ) ), - writeJson: sandbox.stub().callsFake( ( ...args ) => fs.writeJson( ...args ) ), - remove: sandbox.stub().callsFake( ( ...args ) => fs.remove( ...args ) ), - readdir: sandbox.stub().callsFake( ( ...args ) => fs.readdir( ...args ) ) - }, - glob: { - glob: sandbox.stub().callsFake( ( ...args ) => glob( ...args ) ) - } - }; + mockFs( {} ); + } ); - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false + it( 'should use provided `cwd` to search for packages', async () => { + await cleanUpPackages( { + packagesDirectory: 'release', + cwd: '/work/another/project' } ); - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( 'glob', stubs.glob ); - - cleanUpPackages = require( '../../lib/tasks/cleanuppackages' ); + expect( stubs.glob ).toHaveBeenCalledOnce(); + expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + cwd: '/work/another/project/release' + } ) ); } ); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - mockFs.restore(); - sandbox.restore(); - } ); + it( 'should use `process.cwd()` to search for packages if `cwd` option is not provided', async () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/project' ); - describe( 'preparing options', () => { - beforeEach( () => { - mockFs( {} ); + await cleanUpPackages( { + packagesDirectory: 'release' } ); - it( 'should use provided `cwd` to search for packages', async () => { - await cleanUpPackages( { - packagesDirectory: 'release', - cwd: '/work/another/project' - } ); + expect( stubs.glob ).toHaveBeenCalledOnce(); + expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + cwd: '/work/project/release' + } ) ); + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'cwd', '/work/another/project/release' ); + it( 'should match only files', async () => { + await cleanUpPackages( { + packagesDirectory: 'release' } ); - it( 'should use `process.cwd()` to search for packages if `cwd` option is not provided', async () => { - sandbox.stub( process, 'cwd' ).returns( '/work/project' ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); + expect( stubs.glob ).toHaveBeenCalledOnce(); + expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + nodir: true + } ) ); + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'cwd', '/work/project/release' ); + it( 'should always receive absolute paths for matched files', async () => { + await cleanUpPackages( { + packagesDirectory: 'release' } ); - it( 'should match only files', async () => { - await cleanUpPackages( { - packagesDirectory: 'release' - } ); + expect( stubs.glob ).toHaveBeenCalledOnce(); + expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + absolute: true + } ) ); + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'nodir', true ); + it( 'should search for `package.json` in `cwd`', async () => { + await cleanUpPackages( { + packagesDirectory: 'release' } ); - it( 'should always receive absolute paths for matched files', async () => { - await cleanUpPackages( { - packagesDirectory: 'release' - } ); + expect( stubs.glob ).toHaveBeenCalledOnce(); + expect( stubs.glob ).toHaveBeenCalledWith( '*/package.json', expect.any( Object ) ); + } ); + } ); + + describe( 'cleaning package directory', () => { + it( 'should remove empty directories', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo' + } ), + 'ckeditor5-metadata.json': '', + 'src': {} + } + } + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'absolute', true ); + await cleanUpPackages( { + packagesDirectory: 'release' } ); - it( 'should search for `package.json` in `cwd`', async () => { - await cleanUpPackages( { - packagesDirectory: 'release' - } ); + const actualPaths = await getAllPaths(); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 0 ] ).to.equal( '*/package.json' ); - } ); + expect( actualPaths ).to.have.members( [ + getPathTo( '.' ), + getPathTo( 'release' ), + getPathTo( 'release/ckeditor5-foo' ), + getPathTo( 'release/ckeditor5-foo/package.json' ), + getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ) + ] ); } ); - describe( 'cleaning package directory', () => { - it( 'should remove empty directories', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo' - } ), - 'ckeditor5-metadata.json': '', - 'src': {} - } - } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); - - const actualPaths = await getAllPaths(); - - expect( actualPaths ).to.have.members( [ - getPathTo( '.' ), - getPathTo( 'release' ), - getPathTo( 'release/ckeditor5-foo' ), - getPathTo( 'release/ckeditor5-foo/package.json' ), - getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ) - ] ); - } ); - - it( 'should remove `node_modules`', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo' - } ), - 'ckeditor5-metadata.json': '', - 'node_modules': { - '.bin': {}, - '@ckeditor': { - 'ckeditor5-dev-release-tools': { - 'package.json': '' - } + it( 'should remove `node_modules`', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo' + } ), + 'ckeditor5-metadata.json': '', + 'node_modules': { + '.bin': {}, + '@ckeditor': { + 'ckeditor5-dev-release-tools': { + 'package.json': '' } } } } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); - - const actualPaths = await getAllPaths(); - - expect( actualPaths ).to.have.members( [ - getPathTo( '.' ), - getPathTo( 'release' ), - getPathTo( 'release/ckeditor5-foo' ), - getPathTo( 'release/ckeditor5-foo/package.json' ), - getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ) - ] ); - } ); - - it( 'should not remove any file if `files` field is not set', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo' - } ), - 'ckeditor5-metadata.json': '' - } + } + } ); + + await cleanUpPackages( { + packagesDirectory: 'release' + } ); + + const actualPaths = await getAllPaths(); + + expect( actualPaths ).to.have.members( [ + getPathTo( '.' ), + getPathTo( 'release' ), + getPathTo( 'release/ckeditor5-foo' ), + getPathTo( 'release/ckeditor5-foo/package.json' ), + getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ) + ] ); + } ); + + it( 'should not remove any file if `files` field is not set', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo' + } ), + 'ckeditor5-metadata.json': '' } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); - - const actualPaths = await getAllPaths(); - - expect( actualPaths ).to.have.members( [ - getPathTo( '.' ), - getPathTo( 'release' ), - getPathTo( 'release/ckeditor5-foo' ), - getPathTo( 'release/ckeditor5-foo/package.json' ), - getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ) - ] ); - } ); - - it( 'should not remove mandatory files', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - main: 'src/index.js', - types: 'src/index.d.ts', - files: [ - 'foo' - ] - } ), - 'README.md': '', - 'LICENSE.md': '', - 'src': { - 'index.js': '', - 'index.d.ts': '' - } + } + } ); + + await cleanUpPackages( { + packagesDirectory: 'release' + } ); + + const actualPaths = await getAllPaths(); + + expect( actualPaths ).to.have.members( [ + getPathTo( '.' ), + getPathTo( 'release' ), + getPathTo( 'release/ckeditor5-foo' ), + getPathTo( 'release/ckeditor5-foo/package.json' ), + getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ) + ] ); + } ); + + it( 'should not remove mandatory files', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + main: 'src/index.js', + types: 'src/index.d.ts', + files: [ + 'foo' + ] + } ), + 'README.md': '', + 'LICENSE.md': '', + 'src': { + 'index.js': '', + 'index.d.ts': '' } } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); - - const actualPaths = await getAllPaths(); - - expect( actualPaths ).to.have.members( [ - getPathTo( '.' ), - getPathTo( 'release' ), - getPathTo( 'release/ckeditor5-foo' ), - getPathTo( 'release/ckeditor5-foo/package.json' ), - getPathTo( 'release/ckeditor5-foo/README.md' ), - getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), - getPathTo( 'release/ckeditor5-foo/src' ), - getPathTo( 'release/ckeditor5-foo/src/index.js' ), - getPathTo( 'release/ckeditor5-foo/src/index.d.ts' ) - ] ); - } ); - - it( 'should remove not matched dot files and dot directories', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - '.github': { - 'template.md': '' - }, - '.eslintrc.js': '', - '.IMPORTANT.md': '', - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - files: [ - '.IMPORTANT.md', - 'src' - ] - } ), - 'README.md': '', - 'LICENSE.md': '', - 'src': { - 'index.js': '', - 'index.d.ts': '' - } + } + } ); + + await cleanUpPackages( { + packagesDirectory: 'release' + } ); + + const actualPaths = await getAllPaths(); + + expect( actualPaths ).to.have.members( [ + getPathTo( '.' ), + getPathTo( 'release' ), + getPathTo( 'release/ckeditor5-foo' ), + getPathTo( 'release/ckeditor5-foo/package.json' ), + getPathTo( 'release/ckeditor5-foo/README.md' ), + getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), + getPathTo( 'release/ckeditor5-foo/src' ), + getPathTo( 'release/ckeditor5-foo/src/index.js' ), + getPathTo( 'release/ckeditor5-foo/src/index.d.ts' ) + ] ); + } ); + + it( 'should remove not matched dot files and dot directories', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + '.github': { + 'template.md': '' + }, + '.eslintrc.js': '', + '.IMPORTANT.md': '', + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + files: [ + '.IMPORTANT.md', + 'src' + ] + } ), + 'README.md': '', + 'LICENSE.md': '', + 'src': { + 'index.js': '', + 'index.d.ts': '' } } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); - - const actualPaths = await getAllPaths(); - - expect( actualPaths ).to.have.members( [ - getPathTo( '.' ), - getPathTo( 'release' ), - getPathTo( 'release/ckeditor5-foo' ), - getPathTo( 'release/ckeditor5-foo/.IMPORTANT.md' ), - getPathTo( 'release/ckeditor5-foo/package.json' ), - getPathTo( 'release/ckeditor5-foo/README.md' ), - getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), - getPathTo( 'release/ckeditor5-foo/src' ), - getPathTo( 'release/ckeditor5-foo/src/index.js' ), - getPathTo( 'release/ckeditor5-foo/src/index.d.ts' ) - ] ); - } ); - - it( 'should remove not matched files, empty directories and `node_modules` - pattern without globs', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - files: [ - 'ckeditor5-metadata.json', - 'src' - ] - } ), - 'README.md': '', - 'LICENSE.md': '', - 'ckeditor5-metadata.json': '', - 'docs': { - 'assets': { - 'img': { - 'asset.png': '' - } - }, - 'api': { - 'foo.md': '' - }, - 'features': { - 'foo.md': '' + } + } ); + + await cleanUpPackages( { + packagesDirectory: 'release' + } ); + + const actualPaths = await getAllPaths(); + + expect( actualPaths ).to.have.members( [ + getPathTo( '.' ), + getPathTo( 'release' ), + getPathTo( 'release/ckeditor5-foo' ), + getPathTo( 'release/ckeditor5-foo/.IMPORTANT.md' ), + getPathTo( 'release/ckeditor5-foo/package.json' ), + getPathTo( 'release/ckeditor5-foo/README.md' ), + getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), + getPathTo( 'release/ckeditor5-foo/src' ), + getPathTo( 'release/ckeditor5-foo/src/index.js' ), + getPathTo( 'release/ckeditor5-foo/src/index.d.ts' ) + ] ); + } ); + + it( 'should remove not matched files, empty directories and `node_modules` - pattern without globs', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + files: [ + 'ckeditor5-metadata.json', + 'src' + ] + } ), + 'README.md': '', + 'LICENSE.md': '', + 'ckeditor5-metadata.json': '', + 'docs': { + 'assets': { + 'img': { + 'asset.png': '' } }, - 'node_modules': { - '.bin': {}, - '@ckeditor': { - 'ckeditor5-dev-release-tools': { - 'package.json': '' - } + 'api': { + 'foo.md': '' + }, + 'features': { + 'foo.md': '' + } + }, + 'node_modules': { + '.bin': {}, + '@ckeditor': { + 'ckeditor5-dev-release-tools': { + 'package.json': '' } + } + }, + 'src': { + 'commands': { + 'command-foo.js': '', + 'command-bar.js': '' }, - 'src': { - 'commands': { - 'command-foo.js': '', - 'command-bar.js': '' - }, - 'ui': { - 'view-foo.js': '', - 'view-bar.js': '' - }, - 'index.js': '' + 'ui': { + 'view-foo.js': '', + 'view-bar.js': '' }, - 'tests': { - '_utils': {}, - 'index.js': '' - } + 'index.js': '' + }, + 'tests': { + '_utils': {}, + 'index.js': '' } } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); - - const actualPaths = await getAllPaths(); - - expect( actualPaths ).to.have.members( [ - getPathTo( '.' ), - getPathTo( 'release' ), - getPathTo( 'release/ckeditor5-foo' ), - getPathTo( 'release/ckeditor5-foo/package.json' ), - getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ), - getPathTo( 'release/ckeditor5-foo/README.md' ), - getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), - getPathTo( 'release/ckeditor5-foo/src' ), - getPathTo( 'release/ckeditor5-foo/src/ui' ), - getPathTo( 'release/ckeditor5-foo/src/index.js' ), - getPathTo( 'release/ckeditor5-foo/src/commands' ), - getPathTo( 'release/ckeditor5-foo/src/ui/view-foo.js' ), - getPathTo( 'release/ckeditor5-foo/src/ui/view-bar.js' ), - getPathTo( 'release/ckeditor5-foo/src/commands/command-foo.js' ), - getPathTo( 'release/ckeditor5-foo/src/commands/command-bar.js' ) - ] ); - } ); - - it( 'should remove not matched files, empty directories and `node_modules` - pattern with globs', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - files: [ - 'ckeditor5-metadata.json', - 'src/**/*.js' - ] - } ), - 'README.md': '', - 'LICENSE.md': '', - 'ckeditor5-metadata.json': '', - 'docs': { - 'assets': { - 'img': { - 'asset.png': '' - } - }, - 'api': { - 'foo.md': '' - }, - 'features': { - 'foo.md': '' + } + } ); + + await cleanUpPackages( { + packagesDirectory: 'release' + } ); + + const actualPaths = await getAllPaths(); + + expect( actualPaths ).to.have.members( [ + getPathTo( '.' ), + getPathTo( 'release' ), + getPathTo( 'release/ckeditor5-foo' ), + getPathTo( 'release/ckeditor5-foo/package.json' ), + getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ), + getPathTo( 'release/ckeditor5-foo/README.md' ), + getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), + getPathTo( 'release/ckeditor5-foo/src' ), + getPathTo( 'release/ckeditor5-foo/src/ui' ), + getPathTo( 'release/ckeditor5-foo/src/index.js' ), + getPathTo( 'release/ckeditor5-foo/src/commands' ), + getPathTo( 'release/ckeditor5-foo/src/ui/view-foo.js' ), + getPathTo( 'release/ckeditor5-foo/src/ui/view-bar.js' ), + getPathTo( 'release/ckeditor5-foo/src/commands/command-foo.js' ), + getPathTo( 'release/ckeditor5-foo/src/commands/command-bar.js' ) + ] ); + } ); + + it( 'should remove not matched files, empty directories and `node_modules` - pattern with globs', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + files: [ + 'ckeditor5-metadata.json', + 'src/**/*.js' + ] + } ), + 'README.md': '', + 'LICENSE.md': '', + 'ckeditor5-metadata.json': '', + 'docs': { + 'assets': { + 'img': { + 'asset.png': '' } }, - 'node_modules': { - '.bin': {}, - '@ckeditor': { - 'ckeditor5-dev-release-tools': { - 'package.json': '' - } + 'api': { + 'foo.md': '' + }, + 'features': { + 'foo.md': '' + } + }, + 'node_modules': { + '.bin': {}, + '@ckeditor': { + 'ckeditor5-dev-release-tools': { + 'package.json': '' } + } + }, + 'src': { + 'commands': { + 'command-foo.js': '', + 'command-foo.js.map': '', + 'command-foo.ts': '', + 'command-bar.js': '', + 'command-bar.js.map': '', + 'command-bar.ts': '' }, - 'src': { - 'commands': { - 'command-foo.js': '', - 'command-foo.js.map': '', - 'command-foo.ts': '', - 'command-bar.js': '', - 'command-bar.js.map': '', - 'command-bar.ts': '' - }, - 'ui': { - 'view-foo.js': '', - 'view-foo.js.map': '', - 'view-foo.ts': '', - 'view-bar.js': '', - 'view-bar.js.map': '', - 'view-bar.ts': '' - }, - 'index.js': '', - 'index.js.map': '', - 'index.ts': '' + 'ui': { + 'view-foo.js': '', + 'view-foo.js.map': '', + 'view-foo.ts': '', + 'view-bar.js': '', + 'view-bar.js.map': '', + 'view-bar.ts': '' }, - 'tests': { - '_utils': {}, - 'index.js': '' - } + 'index.js': '', + 'index.js.map': '', + 'index.ts': '' + }, + 'tests': { + '_utils': {}, + 'index.js': '' } } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release' - } ); - - const actualPaths = await getAllPaths(); - - expect( actualPaths ).to.have.members( [ - getPathTo( '.' ), - getPathTo( 'release' ), - getPathTo( 'release/ckeditor5-foo' ), - getPathTo( 'release/ckeditor5-foo/package.json' ), - getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ), - getPathTo( 'release/ckeditor5-foo/README.md' ), - getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), - getPathTo( 'release/ckeditor5-foo/src' ), - getPathTo( 'release/ckeditor5-foo/src/ui' ), - getPathTo( 'release/ckeditor5-foo/src/index.js' ), - getPathTo( 'release/ckeditor5-foo/src/commands' ), - getPathTo( 'release/ckeditor5-foo/src/ui/view-foo.js' ), - getPathTo( 'release/ckeditor5-foo/src/ui/view-bar.js' ), - getPathTo( 'release/ckeditor5-foo/src/commands/command-foo.js' ), - getPathTo( 'release/ckeditor5-foo/src/commands/command-bar.js' ) - ] ); + } } ); + + await cleanUpPackages( { + packagesDirectory: 'release' + } ); + + const actualPaths = await getAllPaths(); + + expect( actualPaths ).to.have.members( [ + getPathTo( '.' ), + getPathTo( 'release' ), + getPathTo( 'release/ckeditor5-foo' ), + getPathTo( 'release/ckeditor5-foo/package.json' ), + getPathTo( 'release/ckeditor5-foo/ckeditor5-metadata.json' ), + getPathTo( 'release/ckeditor5-foo/README.md' ), + getPathTo( 'release/ckeditor5-foo/LICENSE.md' ), + getPathTo( 'release/ckeditor5-foo/src' ), + getPathTo( 'release/ckeditor5-foo/src/ui' ), + getPathTo( 'release/ckeditor5-foo/src/index.js' ), + getPathTo( 'release/ckeditor5-foo/src/commands' ), + getPathTo( 'release/ckeditor5-foo/src/ui/view-foo.js' ), + getPathTo( 'release/ckeditor5-foo/src/ui/view-bar.js' ), + getPathTo( 'release/ckeditor5-foo/src/commands/command-foo.js' ), + getPathTo( 'release/ckeditor5-foo/src/commands/command-bar.js' ) + ] ); } ); + } ); - describe( 'cleaning `package.json`', () => { - it( 'should read and write `package.json` from each found package', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo' - } ) - }, - 'ckeditor5-bar': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-bar' - } ) - } + describe( 'cleaning `package.json`', () => { + it( 'should read and write `package.json` from each found package', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo' + } ) + }, + 'ckeditor5-bar': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-bar' + } ) } - } ); + } + } ); - await cleanUpPackages( { - packagesDirectory: 'release' - } ); + await cleanUpPackages( { + packagesDirectory: 'release' + } ); - // Reading `package.json`. - expect( stubs.fs.readJson.callCount ).to.equal( 2 ); + // Reading `package.json`. + expect( stubs.readJson ).toHaveBeenCalledTimes( 2 ); - let call = stubs.fs.readJson.getCall( 0 ); + let input = stubs.readJson.mock.calls[ 0 ]; + let call = stubs.readJson.mock.results[ 0 ]; - expect( await call.returnValue ).to.have.property( 'name', 'ckeditor5-foo' ); - expect( upath.normalize( call.args[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); + expect( await call.value ).to.have.property( 'name', 'ckeditor5-foo' ); + expect( upath.normalize( input[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); - call = stubs.fs.readJson.getCall( 1 ); + input = stubs.readJson.mock.calls[ 1 ]; + call = stubs.readJson.mock.results[ 1 ]; - expect( await call.returnValue ).to.have.property( 'name', 'ckeditor5-bar' ); - expect( upath.normalize( call.args[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-bar/package.json' ) ); + expect( await call.value ).to.have.property( 'name', 'ckeditor5-bar' ); + expect( upath.normalize( input[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-bar/package.json' ) ); - // Writing `package.json`. - expect( stubs.fs.writeJson.callCount ).to.equal( 2 ); + // Writing `package.json`. + expect( stubs.writeJson ).toHaveBeenCalledTimes( 2 ); - call = stubs.fs.writeJson.getCall( 0 ); + input = stubs.writeJson.mock.calls[ 0 ]; - expect( upath.normalize( call.args[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); + expect( upath.normalize( input[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); - call = stubs.fs.writeJson.getCall( 1 ); + input = stubs.writeJson.mock.calls[ 1 ]; - expect( upath.normalize( call.args[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-bar/package.json' ) ); - } ); + expect( upath.normalize( input[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-bar/package.json' ) ); + } ); - it( 'should not remove any field from `package.json` if all of them are mandatory', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - version: '1.0.0', - description: 'Example package.', - dependencies: { - 'ckeditor5': '^37.1.0' - }, - main: 'src/index.ts' - } ) - } + it( 'should not remove any field from `package.json` if all of them are mandatory', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + version: '1.0.0', + description: 'Example package.', + dependencies: { + 'ckeditor5': '^37.1.0' + }, + main: 'src/index.ts' + } ) } - } ); + } + } ); - await cleanUpPackages( { - packagesDirectory: 'release' - } ); + await cleanUpPackages( { + packagesDirectory: 'release' + } ); - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); + expect( stubs.writeJson ).toHaveBeenCalledTimes( 1 ); - const call = stubs.fs.writeJson.getCall( 0 ); + const input = stubs.writeJson.mock.calls[ 0 ]; - expect( upath.normalize( call.args[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); - expect( call.args[ 1 ] ).to.deep.equal( { - name: 'ckeditor5-foo', - version: '1.0.0', - description: 'Example package.', - dependencies: { - 'ckeditor5': '^37.1.0' - }, - main: 'src/index.ts' - } ); - } ); - - it( 'should remove default unnecessary fields from `package.json`', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - version: '1.0.0', - description: 'Example package.', - dependencies: { - 'ckeditor5': '^37.1.0' - }, - devDependencies: { - 'typescript': '^4.8.4' - }, - main: 'src/index.ts', - depcheckIgnore: [ - 'eslint-plugin-ckeditor5-rules' - ], - scripts: { - 'build': 'tsc -p ./tsconfig.json', - 'dll:build': 'webpack' - }, - private: true - } ) - } + expect( upath.normalize( input[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); + expect( input[ 1 ] ).to.deep.equal( { + name: 'ckeditor5-foo', + version: '1.0.0', + description: 'Example package.', + dependencies: { + 'ckeditor5': '^37.1.0' + }, + main: 'src/index.ts' + } ); + } ); + + it( 'should remove default unnecessary fields from `package.json`', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + version: '1.0.0', + description: 'Example package.', + dependencies: { + 'ckeditor5': '^37.1.0' + }, + devDependencies: { + 'typescript': '^4.8.4' + }, + main: 'src/index.ts', + depcheckIgnore: [ + 'eslint-plugin-ckeditor5-rules' + ], + scripts: { + 'build': 'tsc -p ./tsconfig.json', + 'dll:build': 'webpack' + }, + private: true + } ) } - } ); + } + } ); - await cleanUpPackages( { - packagesDirectory: 'release' - } ); + await cleanUpPackages( { + packagesDirectory: 'release' + } ); - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); + expect( stubs.writeJson ).toHaveBeenCalledTimes( 1 ); - const call = stubs.fs.writeJson.getCall( 0 ); + const input = stubs.writeJson.mock.calls[ 0 ]; - expect( upath.normalize( call.args[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); - expect( call.args[ 1 ] ).to.deep.equal( { - name: 'ckeditor5-foo', - version: '1.0.0', - description: 'Example package.', - dependencies: { - 'ckeditor5': '^37.1.0' - }, - main: 'src/index.ts' - } ); - } ); - - it( 'should remove provided unnecessary fields from `package.json`', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - author: 'CKEditor 5 Devops Team', - version: '1.0.0', - description: 'Example package.', - dependencies: { - 'ckeditor5': '^37.1.0' - }, - devDependencies: { - 'typescript': '^4.8.4' - }, - main: 'src/index.ts', - depcheckIgnore: [ - 'eslint-plugin-ckeditor5-rules' - ], - scripts: { - 'build': 'tsc -p ./tsconfig.json', - 'dll:build': 'webpack' - } - } ) - } + expect( upath.normalize( input[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); + expect( input[ 1 ] ).to.deep.equal( { + name: 'ckeditor5-foo', + version: '1.0.0', + description: 'Example package.', + dependencies: { + 'ckeditor5': '^37.1.0' + }, + main: 'src/index.ts' + } ); + } ); + + it( 'should remove provided unnecessary fields from `package.json`', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + author: 'CKEditor 5 Devops Team', + version: '1.0.0', + description: 'Example package.', + dependencies: { + 'ckeditor5': '^37.1.0' + }, + devDependencies: { + 'typescript': '^4.8.4' + }, + main: 'src/index.ts', + depcheckIgnore: [ + 'eslint-plugin-ckeditor5-rules' + ], + scripts: { + 'build': 'tsc -p ./tsconfig.json', + 'dll:build': 'webpack' + } + } ) } - } ); + } + } ); - await cleanUpPackages( { - packagesDirectory: 'release', - packageJsonFieldsToRemove: [ 'author' ] - } ); + await cleanUpPackages( { + packagesDirectory: 'release', + packageJsonFieldsToRemove: [ 'author' ] + } ); - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); + expect( stubs.writeJson ).toHaveBeenCalledTimes( 1 ); - const call = stubs.fs.writeJson.getCall( 0 ); + const input = stubs.writeJson.mock.calls[ 0 ]; - expect( upath.normalize( call.args[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); - expect( call.args[ 1 ] ).to.deep.equal( { - name: 'ckeditor5-foo', - version: '1.0.0', - description: 'Example package.', - dependencies: { - 'ckeditor5': '^37.1.0' - }, - devDependencies: { - 'typescript': '^4.8.4' - }, - main: 'src/index.ts', - depcheckIgnore: [ - 'eslint-plugin-ckeditor5-rules' - ], - scripts: { - 'build': 'tsc -p ./tsconfig.json', - 'dll:build': 'webpack' - } - } ); - } ); - - it( 'should keep postinstall hook in `package.json` when preservePostInstallHook is set to true', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - scripts: { - 'postinstall': 'node my-node-script.js', - 'build': 'tsc -p ./tsconfig.json', - 'dll:build': 'webpack' - } - } ) - } + expect( upath.normalize( input[ 0 ] ) ).to.equal( getPathTo( 'release/ckeditor5-foo/package.json' ) ); + expect( input[ 1 ] ).to.deep.equal( { + name: 'ckeditor5-foo', + version: '1.0.0', + description: 'Example package.', + dependencies: { + 'ckeditor5': '^37.1.0' + }, + devDependencies: { + 'typescript': '^4.8.4' + }, + main: 'src/index.ts', + depcheckIgnore: [ + 'eslint-plugin-ckeditor5-rules' + ], + scripts: { + 'build': 'tsc -p ./tsconfig.json', + 'dll:build': 'webpack' + } + } ); + } ); + + it( 'should keep postinstall hook in `package.json` when preservePostInstallHook is set to true', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + scripts: { + 'postinstall': 'node my-node-script.js', + 'build': 'tsc -p ./tsconfig.json', + 'dll:build': 'webpack' + } + } ) } - } ); + } + } ); - await cleanUpPackages( { - packagesDirectory: 'release', - preservePostInstallHook: true - } ); + await cleanUpPackages( { + packagesDirectory: 'release', + preservePostInstallHook: true + } ); + const input = stubs.writeJson.mock.calls[ 0 ]; - const call = stubs.fs.writeJson.getCall( 0 ); + expect( input[ 1 ] ).to.deep.equal( { + scripts: { + 'postinstall': 'node my-node-script.js' + } + } ); + } ); - expect( call.args[ 1 ] ).to.deep.equal( { - scripts: { - 'postinstall': 'node my-node-script.js' - } - } ); - } ); - - it( 'should not remove scripts unless it is explicitly specified in packageJsonFieldsToRemove', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - author: 'author', - scripts: { - 'postinstall': 'node my-node-script.js', - 'build': 'tsc -p ./tsconfig.json', - 'dll:build': 'webpack' - } - } ) - } - } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release', - preservePostInstallHook: true, - packageJsonFieldsToRemove: [ - 'author' - ] - } ); - - const call = stubs.fs.writeJson.getCall( 0 ); - - expect( call.args[ 1 ] ).to.deep.equal( { - scripts: { - 'postinstall': 'node my-node-script.js', - 'build': 'tsc -p ./tsconfig.json', - 'dll:build': 'webpack' - } - } ); - } ); - - it( 'should accept a callback for packageJsonFieldsToRemove', async () => { - mockFs( { - 'release': { - 'ckeditor5-foo': { - 'package.json': JSON.stringify( { - name: 'ckeditor5-foo', - author: 'CKEditor 5 Devops Team', - version: '1.0.0', - description: 'Example package.', - dependencies: { - 'ckeditor5': '^37.1.0' - }, - devDependencies: { - 'typescript': '^4.8.4' - }, - main: 'src/index.ts', - depcheckIgnore: [ - 'eslint-plugin-ckeditor5-rules' - ], - scripts: { - 'postinstall': 'node my-node-script.js', - 'build': 'tsc -p ./tsconfig.json', - 'dll:build': 'webpack' - } - } ) - } + it( 'should not remove scripts unless it is explicitly specified in packageJsonFieldsToRemove', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + author: 'author', + scripts: { + 'postinstall': 'node my-node-script.js', + 'build': 'tsc -p ./tsconfig.json', + 'dll:build': 'webpack' + } + } ) } - } ); - - await cleanUpPackages( { - packagesDirectory: 'release', - packageJsonFieldsToRemove: defaults => [ - ...defaults, - 'author' - ] - } ); - - const call = stubs.fs.writeJson.getCall( 0 ); - - expect( call.args[ 1 ] ).to.deep.equal( { - description: 'Example package.', - main: 'src/index.ts', - name: 'ckeditor5-foo', - version: '1.0.0', - dependencies: { - 'ckeditor5': '^37.1.0' + } + } ); + + await cleanUpPackages( { + packagesDirectory: 'release', + preservePostInstallHook: true, + packageJsonFieldsToRemove: [ + 'author' + ] + } ); + + const input = stubs.writeJson.mock.calls[ 0 ]; + + expect( input[ 1 ] ).to.deep.equal( { + scripts: { + 'postinstall': 'node my-node-script.js', + 'build': 'tsc -p ./tsconfig.json', + 'dll:build': 'webpack' + } + } ); + } ); + + it( 'should accept a callback for packageJsonFieldsToRemove', async () => { + mockFs( { + 'release': { + 'ckeditor5-foo': { + 'package.json': JSON.stringify( { + name: 'ckeditor5-foo', + author: 'CKEditor 5 Devops Team', + version: '1.0.0', + description: 'Example package.', + dependencies: { + 'ckeditor5': '^37.1.0' + }, + devDependencies: { + 'typescript': '^4.8.4' + }, + main: 'src/index.ts', + depcheckIgnore: [ + 'eslint-plugin-ckeditor5-rules' + ], + scripts: { + 'postinstall': 'node my-node-script.js', + 'build': 'tsc -p ./tsconfig.json', + 'dll:build': 'webpack' + } + } ) } - } ); + } + } ); + + await cleanUpPackages( { + packagesDirectory: 'release', + packageJsonFieldsToRemove: defaults => [ + ...defaults, + 'author' + ] + } ); + + const input = stubs.writeJson.mock.calls[ 0 ]; + + expect( input[ 1 ] ).to.deep.equal( { + description: 'Example package.', + main: 'src/index.ts', + name: 'ckeditor5-foo', + version: '1.0.0', + dependencies: { + 'ckeditor5': '^37.1.0' + } } ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/vitest.config.js b/packages/ckeditor5-dev-release-tools/vitest.config.js new file mode 100644 index 000000000..5cf966978 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/vitest.config.js @@ -0,0 +1,20 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + testTimeout: 10000, + restoreMocks: true, + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From a563fd3ec77e943321fe46abf12b6c799a6fb748 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 3 Sep 2024 13:52:34 +0200 Subject: [PATCH 003/172] Tests: packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.test.js. --- .../tests/tasks/commitandtag.js | 119 ------------------ .../tests/tasks/commitandtag.test.js | 94 ++++++++++++++ 2 files changed, 94 insertions(+), 119 deletions(-) delete mode 100644 packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.test.js diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js b/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js deleted file mode 100644 index 55d4975c1..000000000 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { expect } = require( 'chai' ); -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); - -describe( 'commitAndTag()', () => { - let stubs, commitAndTag; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - tools: { - shExec: sinon.stub().resolves() - }, - glob: { - glob: sinon.stub().returns( [] ) - }, - shellEscape: sinon.stub().callsFake( v => v[ 0 ] ) - }; - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - tools: stubs.tools - } ); - mockery.registerMock( 'glob', stubs.glob ); - mockery.registerMock( 'shell-escape', stubs.shellEscape ); - - commitAndTag = require( '../../lib/tasks/commitandtag' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sinon.restore(); - } ); - - it( 'should not create a commit and tag if there are no files modified', async () => { - await commitAndTag( {} ); - - expect( stubs.tools.shExec.called ).to.equal( false ); - } ); - - it( 'should allow to specify custom cwd', async () => { - stubs.glob.glob.resolves( [ 'package.json' ] ); - - await commitAndTag( { version: '1.0.0', cwd: 'my-cwd' } ); - - expect( stubs.tools.shExec.firstCall.args[ 1 ].cwd ).to.deep.equal( 'my-cwd' ); - expect( stubs.tools.shExec.secondCall.args[ 1 ].cwd ).to.deep.equal( 'my-cwd' ); - expect( stubs.tools.shExec.thirdCall.args[ 1 ].cwd ).to.deep.equal( 'my-cwd' ); - } ); - - it( 'should add provided files to git one by one', async () => { - stubs.glob.glob.resolves( [ - 'package.json', - 'README.md', - 'packages/custom-package/package.json', - 'packages/custom-package/README.md' - ] ); - - await commitAndTag( { - version: '1.0.0', - files: [ 'package.json', 'README.md', 'packages/*/package.json', 'packages/*/README.md' ] - } ); - - expect( stubs.tools.shExec.callCount ).to.equal( 6 ); - expect( stubs.tools.shExec.getCall( 0 ).args[ 0 ] ).to.equal( 'git add package.json' ); - expect( stubs.tools.shExec.getCall( 1 ).args[ 0 ] ).to.equal( 'git add README.md' ); - expect( stubs.tools.shExec.getCall( 2 ).args[ 0 ] ).to.equal( 'git add packages/custom-package/package.json' ); - expect( stubs.tools.shExec.getCall( 3 ).args[ 0 ] ).to.equal( 'git add packages/custom-package/README.md' ); - } ); - - it( 'should set correct commit message', async () => { - stubs.glob.glob.resolves( [ 'package.json' ] ); - - await commitAndTag( { version: '1.0.0', packagesDirectory: 'packages' } ); - - expect( stubs.tools.shExec.secondCall.args[ 0 ] ).to.equal( 'git commit --message "Release: v1.0.0." --no-verify' ); - } ); - - it( 'should set correct tag', async () => { - stubs.glob.glob.resolves( [ 'package.json' ] ); - - await commitAndTag( { version: '1.0.0', packagesDirectory: 'packages' } ); - - expect( stubs.tools.shExec.thirdCall.args[ 0 ] ).to.equal( 'git tag v1.0.0' ); - } ); - - it( 'should escape arguments passed to a shell command', async () => { - stubs.glob.glob.resolves( [ - 'package.json', - 'README.md', - 'packages/custom-package/package.json', - 'packages/custom-package/README.md' - ] ); - - await commitAndTag( { - version: '1.0.0', - files: [ 'package.json', 'README.md', 'packages/*/package.json', 'packages/*/README.md' ] - } ); - - expect( stubs.shellEscape.callCount ).to.equal( 5 ); - expect( stubs.shellEscape.getCall( 0 ).args[ 0 ] ).to.deep.equal( [ 'package.json' ] ); - expect( stubs.shellEscape.getCall( 1 ).args[ 0 ] ).to.deep.equal( [ 'README.md' ] ); - expect( stubs.shellEscape.getCall( 2 ).args[ 0 ] ).to.deep.equal( [ 'packages/custom-package/package.json' ] ); - expect( stubs.shellEscape.getCall( 3 ).args[ 0 ] ).to.deep.equal( [ 'packages/custom-package/README.md' ] ); - expect( stubs.shellEscape.getCall( 4 ).args[ 0 ] ).to.deep.equal( [ '1.0.0' ] ); - } ); -} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.test.js b/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.test.js new file mode 100644 index 000000000..1fb6f0e69 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.test.js @@ -0,0 +1,94 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import shellEscape from 'shell-escape'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { glob } from 'glob'; +import commitAndTag from '../../lib/tasks/commitandtag.js'; + +vi.mock( 'glob' ); +vi.mock( 'shell-escape' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); + +describe( 'commitAndTag()', () => { + beforeEach( () => { + vi.mocked( glob ).mockResolvedValue( [] ); + vi.mocked( shellEscape ).mockImplementation( v => v[ 0 ] ); + } ); + + it( 'should not create a commit and tag if there are no files modified', async () => { + await commitAndTag( {} ); + + expect( vi.mocked( tools.shExec ) ).not.toHaveBeenCalled(); + } ); + + it( 'should allow to specify custom cwd', async () => { + vi.mocked( glob ).mockResolvedValue( [ 'package.json' ] ); + + await commitAndTag( { version: '1.0.0', cwd: 'my-cwd' } ); + + expect( vi.mocked( tools.shExec ).mock.calls[ 0 ][ 1 ].cwd ).to.deep.equal( 'my-cwd' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 1 ][ 1 ].cwd ).to.deep.equal( 'my-cwd' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 2 ][ 1 ].cwd ).to.deep.equal( 'my-cwd' ); + } ); + + it( 'should add provided files to git one by one', async () => { + vi.mocked( glob ).mockResolvedValue( [ + 'package.json', + 'README.md', + 'packages/custom-package/package.json', + 'packages/custom-package/README.md' + ] ); + + await commitAndTag( { + version: '1.0.0', + files: [ 'package.json', 'README.md', 'packages/*/package.json', 'packages/*/README.md' ] + } ); + + expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledTimes( 6 ); + expect( vi.mocked( tools.shExec ).mock.calls[ 0 ][ 0 ] ).to.equal( 'git add package.json' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 1 ][ 0 ] ).to.equal( 'git add README.md' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 2 ][ 0 ] ).to.equal( 'git add packages/custom-package/package.json' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 3 ][ 0 ] ).to.equal( 'git add packages/custom-package/README.md' ); + } ); + + it( 'should set correct commit message', async () => { + vi.mocked( glob ).mockResolvedValue( [ 'package.json' ] ); + + await commitAndTag( { version: '1.0.0', packagesDirectory: 'packages' } ); + + expect( vi.mocked( tools.shExec ).mock.calls[ 1 ][ 0 ] ).to.equal( 'git commit --message "Release: v1.0.0." --no-verify' ); + } ); + + it( 'should set correct tag', async () => { + vi.mocked( glob ).mockResolvedValue( [ 'package.json' ] ); + + await commitAndTag( { version: '1.0.0', packagesDirectory: 'packages' } ); + + expect( vi.mocked( tools.shExec ).mock.calls[ 2 ][ 0 ] ).to.equal( 'git tag v1.0.0' ); + } ); + + it( 'should escape arguments passed to a shell command', async () => { + vi.mocked( glob ).mockResolvedValue( [ + 'package.json', + 'README.md', + 'packages/custom-package/package.json', + 'packages/custom-package/README.md' + ] ); + + await commitAndTag( { + version: '1.0.0', + files: [ 'package.json', 'README.md', 'packages/*/package.json', 'packages/*/README.md' ] + } ); + + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledTimes( 5 ); + expect( vi.mocked( shellEscape ).mock.calls[ 0 ][ 0 ] ).to.deep.equal( [ 'package.json' ] ); + expect( vi.mocked( shellEscape ).mock.calls[ 1 ][ 0 ] ).to.deep.equal( [ 'README.md' ] ); + expect( vi.mocked( shellEscape ).mock.calls[ 2 ][ 0 ] ).to.deep.equal( [ 'packages/custom-package/package.json' ] ); + expect( vi.mocked( shellEscape ).mock.calls[ 3 ][ 0 ] ).to.deep.equal( [ 'packages/custom-package/README.md' ] ); + expect( vi.mocked( shellEscape ).mock.calls[ 4 ][ 0 ] ).to.deep.equal( [ '1.0.0' ] ); + } ); +} ); From 7440d95d23c51857c2ccf9c6fffa7b692e5cdba9 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 3 Sep 2024 13:52:57 +0200 Subject: [PATCH 004/172] To merge with ESM. --- packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js | 2 +- .../lib/tasks/creategithubrelease.js | 2 +- .../lib/tasks/generatechangelogformonorepository.js | 2 +- .../lib/tasks/generatechangelogforsinglepackage.js | 2 +- .../ckeditor5-dev-release-tools/lib/tasks/preparerepository.js | 2 +- .../ckeditor5-dev-release-tools/lib/tasks/publishpackages.js | 2 +- packages/ckeditor5-dev-release-tools/lib/tasks/push.js | 2 +- .../ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js | 2 +- .../ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js | 2 +- .../ckeditor5-dev-release-tools/lib/tasks/updateversions.js | 2 +- .../lib/tasks/verifypackagespublishedcorrectly.js | 2 +- .../lib/utils/assertfilestopublish.js | 2 +- .../lib/utils/assertnpmauthorization.js | 2 +- packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js | 2 +- .../ckeditor5-dev-release-tools/lib/utils/assertpackages.js | 2 +- .../lib/utils/checkversionavailability.js | 2 +- .../ckeditor5-dev-release-tools/lib/utils/executeinparallel.js | 2 +- .../lib/utils/isversionpublishablefortag.js | 2 +- .../lib/utils/publishpackageonnpmcallback.js | 2 +- .../lib/utils/validaterepositorytorelease.js | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js index 4bf4b45f8..b6c32ac1f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js @@ -17,7 +17,7 @@ import shellEscape from 'shell-escape'; * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ -export async function commitAndTag( { version, files, cwd = process.cwd() } ) { +export default async function commitAndTag( { version, files, cwd = process.cwd() } ) { const normalizedCwd = toUnix( cwd ); const filePathsToAdd = await glob( files, { cwd: normalizedCwd, absolute: true, nodir: true } ); diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js index cbdfb16e6..af846352c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js @@ -17,7 +17,7 @@ import { getRepositoryUrl } from '../utils/transformcommitutils'; * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise.} */ -export async function createGithubRelease( options ) { +export default async function createGithubRelease( options ) { const { token, version, diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js index 7dbe8f24f..ec9be9bc8 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js @@ -61,7 +61,7 @@ const noteInfo = `[ℹī¸](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-c * * @returns {Promise.} */ -export async function generateChangelogForMonoRepository( options ) { +export default async function generateChangelogForMonoRepository( options ) { const log = logger(); const cwd = process.cwd(); const rootPkgJson = getPackageJson( options.cwd ); diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js index b538200ce..884df91b4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js @@ -38,7 +38,7 @@ const SKIP_GENERATE_CHANGELOG = 'Typed "skip" as a new version. Aborting.'; * * @returns {Promise} */ -export async function generateChangelogForSinglePackage( options = {} ) { +export default async function generateChangelogForSinglePackage( options = {} ) { const log = logger(); const pkgJson = getPackageJson(); diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js index b8b3be569..ece147e37 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js @@ -21,7 +21,7 @@ import upath from 'upath'; * If not specified, the root package will not be created. * @returns {Promise} */ -export async function prepareRepository( options ) { +export default async function prepareRepository( options ) { const { outputDirectory, packagesDirectory, diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js index 1debef2e4..b4c3c8db9 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js @@ -44,7 +44,7 @@ import publishPackageOnNpmCallback from '../utils/publishpackageonnpmcallback'; * @param {Number} [options.concurrency=4] Number of CPUs that will execute the task. * @returns {Promise} */ -export async function publishPackages( options ) { +export default async function publishPackages( options ) { const { packagesDirectory, npmOwner, diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/push.js b/packages/ckeditor5-dev-release-tools/lib/tasks/push.js index 1ed786b73..70f77b7e0 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/push.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/push.js @@ -15,7 +15,7 @@ import shellEscape from 'shell-escape'; * @param {String} [options.cwd] Root of the repository to prepare. `process.cwd()` by default. * @returns {Promise} */ -export async function push( options ) { +export default async function push( options ) { const { releaseBranch, version, diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js index 833b1b99f..2a0715a1a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js @@ -25,7 +25,7 @@ const execPromise = util.promisify( exec ); * @param {Array.} options.packages Array of packages' names to reassign tags for. * @returns {Promise} */ -export async function reassignNpmTags( { npmOwner, version, packages } ) { +export default async function reassignNpmTags( { npmOwner, version, packages } ) { const errors = []; const packagesSkipped = []; const packagesUpdated = []; diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js index 3e8ed58b3..7327965a4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js @@ -29,7 +29,7 @@ import upath from 'upath'; * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ -export async function updateDependencies( options ) { +export default async function updateDependencies( options ) { const { version, packagesDirectory, diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js index 1372d9659..adaa891a5 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js @@ -29,7 +29,7 @@ import checkVersionAvailability from '../utils/checkversionavailability'; * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ -export async function updateVersions( options ) { +export default async function updateVersions( options ) { const { packagesDirectory, version, diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js index 79beb9238..1ae3d850a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js @@ -18,7 +18,7 @@ import { checkVersionAvailability } from '../utils/checkversionavailability'; * @param {Function} options.onSuccess Callback fired when function is successful. * @returns {Promise} */ -export async function verifyPackagesPublishedCorrectly( options ) { +export default async function verifyPackagesPublishedCorrectly( options ) { const { packagesDirectory, version, onSuccess } = options; const packagesToVerify = await glob( upath.join( packagesDirectory, '*' ), { absolute: true } ); const errors = []; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js index fab39bab2..d1e26b6ac 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js @@ -14,7 +14,7 @@ import { glob } from 'glob'; * @param {Object.>|null} optionalEntries * @returns {Promise} */ -export async function assertFilesToPublish( packagePaths, optionalEntries ) { +export default async function assertFilesToPublish( packagePaths, optionalEntries ) { const errors = []; for ( const packagePath of packagePaths ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js index 71104be35..fa117233b 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js @@ -11,7 +11,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; * @param {String} npmOwner Expected npm account name that should be logged into npm. * @returns {Promise} */ -export async function assertNpmAuthorization( npmOwner ) { +export default async function assertNpmAuthorization( npmOwner ) { return tools.shExec( 'npm whoami', { verbosity: 'error', async: true } ) .then( npmCurrentUser => { if ( npmOwner !== npmCurrentUser.trim() ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js index cbc989e8e..17a5a459a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js @@ -14,7 +14,7 @@ import semver from 'semver'; * @param {String} npmTag * @returns {Promise} */ -export async function assertNpmTag( packagePaths, npmTag ) { +export default async function assertNpmTag( packagePaths, npmTag ) { const errors = []; for ( const packagePath of packagePaths ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js index 1b74cab74..10699c69f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js @@ -17,7 +17,7 @@ import upath from 'upath'; * this array contains a list of packages that will not be checked. In other words, they do not have to define the entry point. * @returns {Promise} */ -export async function assertPackages( packagePaths, options ) { +export default async function assertPackages( packagePaths, options ) { const errors = []; const { requireEntryPoint, optionalEntryPointPackages } = options; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js b/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js index 1c6abc3de..af181049e 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js @@ -16,7 +16,7 @@ import shellEscape from 'shell-escape'; * @param {String} packageName * @returns {Promise} */ -export async function checkVersionAvailability( version, packageName ) { +export default async function checkVersionAvailability( version, packageName ) { const command = `npm show ${ shellEscape( [ packageName ] ) }@${ shellEscape( [ version ] ) } version`; return tools.shExec( command, { verbosity: 'silent', async: true } ) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js index 89b258afb..5216e1958 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js @@ -36,7 +36,7 @@ const WORKER_SCRIPT = upath.join( __dirname, 'parallelworker.cjs' ); * @param {Number} [options.concurrency=require( 'os' ).cpus().length / 2] Number of CPUs that will execute the task. * @returns {Promise} */ -export async function executeInParallel( options ) { +export default async function executeInParallel( options ) { const { packagesDirectory, taskToExecute, diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js b/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js index b088a7e7f..6c5e0863f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js @@ -15,7 +15,7 @@ import shellEscape from 'shell-escape'; * @param {String} npmTag * @return {Promise.} */ -export async function isVersionPublishableForTag( packageName, version, npmTag ) { +export default async function isVersionPublishableForTag( packageName, version, npmTag ) { const command = `npm view ${ shellEscape( [ packageName ] ) }@${ shellEscape( [ npmTag ] ) } version --silent`; const npmVersion = await tools.shExec( command, { async: true, verbosity: 'silent' } ) .then( value => value.trim() ) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js index 59041f85a..d4eb4fe6f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js @@ -15,7 +15,7 @@ import fs from 'fs-extra'; * @param {String} taskOptions.npmTag * @returns {Promise} */ -export async function publishPackageOnNpmCallback( packagePath, taskOptions ) { +export default async function publishPackageOnNpmCallback( packagePath, taskOptions ) { const result = await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, { cwd: packagePath, async: true, diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js b/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js index 55731a7ce..999046324 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js @@ -13,7 +13,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; * @param {String} [options.branch='master'] A name of the branch that should be used for releasing packages. * @returns {Promise.>} */ -export async function validateRepositoryToRelease( options ) { +export default async function validateRepositoryToRelease( options ) { const { version, changes, From dedb08178ea93a3afdcc838a71ad4f66e288ebdb Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:00:19 +0200 Subject: [PATCH 005/172] Converted the package to ESM. --- .../bin/circle-disable-auto-cancel-builds.js | 4 +- .../bin/circle-enable-auto-cancel-builds.js | 4 +- .../bin/circle-workflow-notifier.js | 10 +- .../bin/is-job-triggered-by-member.js | 4 +- .../bin/notify-circle-status.js | 6 +- .../bin/notify-travis-status.js | 4 +- .../bin/trigger-circle-build.js | 4 +- .../lib/circle-update-auto-cancel-builds.js | 10 +- .../ckeditor5-dev-ci/lib/format-message.js | 14 +- packages/ckeditor5-dev-ci/lib/index.js | 4 +- .../lib/is-job-triggered-by-member.js | 10 +- .../lib/process-job-statuses.js | 6 +- .../lib/trigger-circle-build.js | 8 +- .../lib/utils/get-job-approver.js | 12 +- packages/ckeditor5-dev-ci/package.json | 10 +- ... circle-update-auto-cancel-builds.test.js} | 41 ++--- .../data/{members.js => members.test.js} | 10 +- ...rmat-message.js => format-message.test.js} | 41 ++--- .../tests/is-job-triggered-by-member.js | 124 -------------- .../tests/is-job-triggered-by-member.test.js | 115 +++++++++++++ ...atuses.js => process-job-statuses.test.js} | 29 ++-- ...-build.js => trigger-circle-build.test.js} | 158 ++++++++++-------- ...b-approver.js => get-job-approver.test.js} | 62 +++---- packages/ckeditor5-dev-ci/vitest.config.js | 20 +++ 24 files changed, 325 insertions(+), 385 deletions(-) rename packages/ckeditor5-dev-ci/tests/{circle-update-auto-cancel-builds.js => circle-update-auto-cancel-builds.test.js} (59%) rename packages/ckeditor5-dev-ci/tests/data/{members.js => members.test.js} (73%) rename packages/ckeditor5-dev-ci/tests/{format-message.js => format-message.test.js} (88%) delete mode 100644 packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js create mode 100644 packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.test.js rename packages/ckeditor5-dev-ci/tests/{process-job-statuses.js => process-job-statuses.test.js} (92%) rename packages/ckeditor5-dev-ci/tests/{trigger-circle-build.js => trigger-circle-build.test.js} (52%) rename packages/ckeditor5-dev-ci/tests/utils/{get-job-approver.js => get-job-approver.test.js} (51%) create mode 100644 packages/ckeditor5-dev-ci/vitest.config.js diff --git a/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js index e27c08ff4..16e0c926e 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js @@ -5,9 +5,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const circleUpdateAutoCancelBuilds = require( '../lib/circle-update-auto-cancel-builds' ); +import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds'; /** * This script updates CircleCI settings to disable the "Auto-cancel redundant workflows" option. diff --git a/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js index c5c1b9d06..f4cfbef89 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js @@ -5,9 +5,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const circleUpdateAutoCancelBuilds = require( '../lib/circle-update-auto-cancel-builds' ); +import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds'; /** * This script updates CircleCI settings to enable the "Auto-cancel redundant workflows" option. diff --git a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js index 1b80a27d1..f71e8dcc1 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js +++ b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js @@ -7,12 +7,10 @@ /* eslint-env node */ -'use strict'; - -const { execSync } = require( 'child_process' ); -const fetch = require( 'node-fetch' ); -const minimist = require( 'minimist' ); -const processJobStatuses = require( '../lib/process-job-statuses' ); +import { execSync } from 'child_process'; +import fetch from 'node-fetch'; +import minimist from 'minimist'; +import processJobStatuses from '../lib/process-job-statuses'; // This script allows the creation of a new job within a workflow that will be executed // in the end, when all other jobs will be finished or errored. diff --git a/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js b/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js index 42e9a6584..77e171659 100755 --- a/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js +++ b/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js @@ -5,9 +5,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const isJobTriggeredByMember = require( '../lib/is-job-triggered-by-member' ); +import isJobTriggeredByMember from '../lib/is-job-triggered-by-member'; /** * This script checks if a user that approved an approval job could do that. diff --git a/packages/ckeditor5-dev-ci/bin/notify-circle-status.js b/packages/ckeditor5-dev-ci/bin/notify-circle-status.js index 6ee788746..9cb6bc9d8 100755 --- a/packages/ckeditor5-dev-ci/bin/notify-circle-status.js +++ b/packages/ckeditor5-dev-ci/bin/notify-circle-status.js @@ -7,9 +7,9 @@ /* eslint-env node */ -const fetch = require( 'node-fetch' ); -const slackNotify = require( 'slack-notify' ); -const formatMessage = require( '../lib/format-message' ); +import fetch from 'node-fetch'; +import slackNotify from 'slack-notify'; +import formatMessage from '../lib/format-message'; // This script assumes that is being executed on Circle CI. // Step it is used on should have set value: `when: on_fail`, since it does not diff --git a/packages/ckeditor5-dev-ci/bin/notify-travis-status.js b/packages/ckeditor5-dev-ci/bin/notify-travis-status.js index eb1bd479f..0eea57603 100755 --- a/packages/ckeditor5-dev-ci/bin/notify-travis-status.js +++ b/packages/ckeditor5-dev-ci/bin/notify-travis-status.js @@ -7,8 +7,8 @@ /* eslint-env node */ -const formatMessage = require( '../lib/format-message' ); -const slackNotify = require( 'slack-notify' ); +import formatMessage from '../lib/format-message'; +import slackNotify from 'slack-notify'; const ALLOWED_BRANCHES = [ 'stable', diff --git a/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js b/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js index eb0b43d3c..290e35d87 100755 --- a/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js @@ -5,9 +5,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const triggerCircleBuild = require( '../lib/trigger-circle-build' ); +import triggerCircleBuild from '../lib/trigger-circle-build'; /** * This script triggers a new CircleCI build. diff --git a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js index 46da393d5..9526fcee8 100644 --- a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fetch = require( 'node-fetch' ); +import nodeFetch from 'node-fetch'; /** * @param options @@ -15,7 +13,7 @@ const fetch = require( 'node-fetch' ); * @param {Boolean} options.newValue * @return {Promise.} */ -module.exports = async function circleUpdateAutoCancelBuilds( options ) { +export default async function circleUpdateAutoCancelBuilds( options ) { const { circleToken, githubOrganization, @@ -38,6 +36,6 @@ module.exports = async function circleUpdateAutoCancelBuilds( options ) { const settingsUpdateUrl = `https://circleci.com/api/v2/project/github/${ githubOrganization }/${ githubRepository }/settings`; - return fetch( settingsUpdateUrl, circleRequestOptions ) + return nodeFetch( settingsUpdateUrl, circleRequestOptions ) .then( r => r.json() ); -}; +} diff --git a/packages/ckeditor5-dev-ci/lib/format-message.js b/packages/ckeditor5-dev-ci/lib/format-message.js index 35b900348..3faa50c70 100644 --- a/packages/ckeditor5-dev-ci/lib/format-message.js +++ b/packages/ckeditor5-dev-ci/lib/format-message.js @@ -5,11 +5,9 @@ /* eslint-env node */ -'use strict'; - -const fetch = require( 'node-fetch' ); -const bots = require( './data/bots.json' ); -const members = require( './data/members.json' ); +import nodeFetch from 'node-fetch'; +import bots from './data/bots.json'; +import members from './data/members.json'; const REPOSITORY_REGEXP = /github\.com\/([^/]+)\/([^/]+)/; @@ -29,7 +27,7 @@ const REPOSITORY_REGEXP = /github\.com\/([^/]+)\/([^/]+)/; * @param {Number} options.endTime * @param {Boolean} options.shouldHideAuthor */ -module.exports = async function formatMessage( options ) { +export default async function formatMessage( options ) { const commitDetails = await getCommitDetails( options.triggeringCommitUrl, options.githubToken ); const repoUrl = `https://github.com/${ options.repositoryOwner }/${ options.repositoryName }`; @@ -63,7 +61,7 @@ module.exports = async function formatMessage( options ) { } ] } ] }; -}; +} /** * Returns the additional message that will be added to the notifier post. @@ -196,7 +194,7 @@ function getCommitDetails( triggeringCommitUrl, githubToken ) { } }; - return fetch( apiGithubUrlCommit, options ) + return nodeFetch( apiGithubUrlCommit, options ) .then( response => response.json() ) .then( json => ( { githubAccount: json.author ? json.author.login : null, diff --git a/packages/ckeditor5-dev-ci/lib/index.js b/packages/ckeditor5-dev-ci/lib/index.js index 6b714f736..45193e6d8 100644 --- a/packages/ckeditor5-dev-ci/lib/index.js +++ b/packages/ckeditor5-dev-ci/lib/index.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = { +export default { getJobApprover: require( './utils/get-job-approver' ), members: require( './data/members.json' ) }; diff --git a/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js b/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js index 185218ccc..4964fd2b6 100644 --- a/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js +++ b/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { Octokit } = require( '@octokit/rest' ); -const getJobApprover = require( './utils/get-job-approver' ); +import { Octokit } from '@octokit/rest'; +import getJobApprover from './utils/get-job-approver'; /** * @param options @@ -18,7 +16,7 @@ const getJobApprover = require( './utils/get-job-approver' ); * @param {String} options.githubToken * @return {Promise.} */ -module.exports = async function isJobTriggeredByMember( options ) { +export default async function isJobTriggeredByMember( options ) { const { circleToken, circleWorkflowId, @@ -41,4 +39,4 @@ module.exports = async function isJobTriggeredByMember( options ) { return data .map( ( { login } ) => login ) .includes( login ); -}; +} diff --git a/packages/ckeditor5-dev-ci/lib/process-job-statuses.js b/packages/ckeditor5-dev-ci/lib/process-job-statuses.js index 6d8b69055..64051a60d 100644 --- a/packages/ckeditor5-dev-ci/lib/process-job-statuses.js +++ b/packages/ckeditor5-dev-ci/lib/process-job-statuses.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * The function aims to determine a proper build status for children's jobs based on their parent's statuses. * @@ -19,7 +17,7 @@ * @param {Array.} jobs * @returns {Array.} */ -module.exports = function processJobStatuses( jobs ) { +export default function processJobStatuses( jobs ) { // To avoid modifying the original object, let's clone. const jobsClone = clone( jobs ); @@ -55,7 +53,7 @@ module.exports = function processJobStatuses( jobs ) { } return jobsClone; -}; +} /** * @param {WorkflowJob} job diff --git a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js index 4a7504578..f1a0f8cad 100644 --- a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js @@ -5,7 +5,7 @@ /* eslint-env node */ -const fetch = require( 'node-fetch' ); +import nodeFetch from 'node-fetch'; /** * @param options @@ -17,7 +17,7 @@ const fetch = require( 'node-fetch' ); * @param {String|null} [options.triggerRepositorySlug=null] A repository slug (org/name) that triggers a new build. * @return {Promise} */ -module.exports = async function triggerCircleBuild( options ) { +export default async function triggerCircleBuild( options ) { const { circleToken, commit, @@ -51,7 +51,7 @@ module.exports = async function triggerCircleBuild( options ) { body: JSON.stringify( { branch, parameters } ) }; - return fetch( requestUrl, requestOptions ) + return nodeFetch( requestUrl, requestOptions ) .then( res => res.json() ) .then( response => { if ( response.error_message ) { @@ -62,4 +62,4 @@ module.exports = async function triggerCircleBuild( options ) { throw new Error( `CI trigger failed: "${ response.message }".` ); } } ); -}; +} diff --git a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js index 37998992d..393239d96 100644 --- a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fetch = require( 'node-fetch' ); +import nodeFetch from 'node-fetch'; /** * Returns a promise that resolves to GitHub name of a developer who approved the `jobName` job. @@ -15,7 +13,7 @@ const fetch = require( 'node-fetch' ); * @param {String} jobName * @returns {Promise.} */ -module.exports = async function getJobApprover( circleCiToken, workflowId, jobName ) { +export default async function getJobApprover( circleCiToken, workflowId, jobName ) { const circleRequestOptions = { method: 'get', headers: { @@ -27,12 +25,12 @@ module.exports = async function getJobApprover( circleCiToken, workflowId, jobNa // Find an identifier of a developer who approved an approval job. const workflowJobsUrl = `https://circleci.com/api/v2/workflow/${ workflowId }/job`; - const workflowJobs = await fetch( workflowJobsUrl, circleRequestOptions ).then( r => r.json() ); + const workflowJobs = await nodeFetch( workflowJobsUrl, circleRequestOptions ).then( r => r.json() ); const { approved_by: approvedBy } = workflowJobs.items.find( job => job.name === jobName ); // Find a username based on the identifier. const userDetailsUrl = `https://circleci.com/api/v2/user/${ approvedBy }`; - const { login } = await fetch( userDetailsUrl, circleRequestOptions ).then( r => r.json() ); + const { login } = await nodeFetch( userDetailsUrl, circleRequestOptions ).then( r => r.json() ); return login; -}; +} diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 975d82aa6..502cac9b6 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -16,6 +16,7 @@ "node": ">=18.0.0", "npm": ">=5.7.1" }, + "type": "module", "main": "lib/index.js", "files": [ "bin", @@ -41,12 +42,13 @@ "devDependencies": { "chai": "^4.2.0", "mocha": "^7.1.2", - "proxyquire": "^2.1.3", "mockery": "^2.1.0", - "sinon": "^9.2.4" + "proxyquire": "^2.1.3", + "sinon": "^9.2.4", + "vitest": "^2.0.5" }, "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.ts --coverage" } } diff --git a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js similarity index 59% rename from packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js rename to packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js index 66c0ee3db..7f2f21b66 100644 --- a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js @@ -3,41 +3,20 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds'; +import nodeFetch from 'node-fetch'; -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'node-fetch' ); describe( 'lib/circleUpdateAutoCancelBuilds', () => { - let stubs, circleUpdateAutoCancelBuilds; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - fetch: sinon.stub() - }; - - mockery.registerMock( 'node-fetch', stubs.fetch ); - - circleUpdateAutoCancelBuilds = require( '../lib/circle-update-auto-cancel-builds' ); - } ); - - afterEach( () => { - mockery.disable(); - } ); - it( 'should send a request to CircleCI to update the redundant workflows option', async () => { const response = {}; - stubs.fetch.resolves( { - json: () => Promise.resolve( response ) - } ); + vi.mocked( nodeFetch ) + .mockResolvedValue( { + json: () => Promise.resolve( response ) + } ); const results = await circleUpdateAutoCancelBuilds( { circleToken: 'circle-token', @@ -46,10 +25,10 @@ describe( 'lib/circleUpdateAutoCancelBuilds', () => { newValue: true } ); - expect( stubs.fetch.callCount ).to.equal( 1 ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); expect( results ).to.deep.equal( response ); - const [ url, options ] = stubs.fetch.firstCall.args; + const [ url, options ] = vi.mocked( nodeFetch ).mock.calls[ 0 ]; expect( url ).to.equal( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-foo/settings' ); expect( options ).to.have.property( 'method', 'patch' ); diff --git a/packages/ckeditor5-dev-ci/tests/data/members.js b/packages/ckeditor5-dev-ci/tests/data/members.test.js similarity index 73% rename from packages/ckeditor5-dev-ci/tests/data/members.js rename to packages/ckeditor5-dev-ci/tests/data/members.test.js index 8dfa6ecab..1801c0b0e 100644 --- a/packages/ckeditor5-dev-ci/tests/data/members.js +++ b/packages/ckeditor5-dev-ci/tests/data/members.test.js @@ -3,15 +3,11 @@ * For licensing, see LICENSE.md. */ -/* eslint-env node */ - -'use strict'; - -const members = require( '../../lib/data/members.json' ); -const expect = require( 'chai' ).expect; +import { describe, expect, it } from 'vitest'; +import members from '../../lib/data/members.json'; describe( 'lib/data/members', () => { - it( 'should be a function', () => { + it( 'should be an object', () => { expect( members ).to.be.a( 'object' ); } ); diff --git a/packages/ckeditor5-dev-ci/tests/format-message.js b/packages/ckeditor5-dev-ci/tests/format-message.test.js similarity index 88% rename from packages/ckeditor5-dev-ci/tests/format-message.js rename to packages/ckeditor5-dev-ci/tests/format-message.test.js index c826497a1..e97e807fd 100644 --- a/packages/ckeditor5-dev-ci/tests/format-message.js +++ b/packages/ckeditor5-dev-ci/tests/format-message.test.js @@ -5,35 +5,26 @@ /* eslint-env node */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import nodeFetch from 'node-fetch'; +import formatMessage from '../lib/format-message'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +vi.mock( 'node-fetch' ); -describe( 'lib/format-message', () => { - let formatMessage, stubs; - - beforeEach( () => { - stubs = { - nodeFetch: sinon.stub() - }; - - formatMessage = proxyquire( '../lib/format-message', { - 'node-fetch': stubs.nodeFetch, - './data/members.json': { - ExampleNick: 'slackId' - } - } ); - } ); +vi.mock( './data/members.json', () => { + return { + ExampleNick: 'slackId' + }; +} ); +describe( 'lib/format-message', () => { describe( 'formatMessage()', () => { it( 'should be a function', () => { - expect( formatMessage ).to.be.a( 'function' ); + expect( formatMessage ).toBeInstanceOf( Function ); } ); it( 'should display a message for bot if a login is included in the "bots" array', async () => { - stubs.nodeFetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: { @@ -72,7 +63,7 @@ describe( 'lib/format-message', () => { } ); it( 'should display a message for bot if a login is unavailable but author name is included in the "bots" array', async () => { - stubs.nodeFetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: null, @@ -109,7 +100,7 @@ describe( 'lib/format-message', () => { } ); it( 'should mention the channel if a login is unavailable and author name is not included in the "bots" array', async () => { - stubs.nodeFetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: null, @@ -146,7 +137,7 @@ describe( 'lib/format-message', () => { } ); it( 'should find a Slack account based on a GitHub account case-insensitive', async () => { - stubs.nodeFetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: { @@ -181,7 +172,7 @@ describe( 'lib/format-message', () => { expect( message ).to.be.an( 'object' ); expect( message ).to.have.property( 'text' ); - expect( message.text ).to.equal( '<@slackId>, could you take a look?' ); + expect( message.text ).toEqual( ' (Example Nick), could you take a look?' ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js b/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js deleted file mode 100644 index b215d1b02..000000000 --- a/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); -const proxyquire = require( 'proxyquire' ); - -describe( 'lib/isJobTriggeredByMember', () => { - let stubs, isJobTriggeredByMember; - - beforeEach( () => { - stubs = { - fetch: sinon.stub( global, 'fetch' ), - getJobApprover: sinon.stub(), - octokitRestInstance: { - request: sinon.stub() - }, - octokitRest: sinon.stub().callsFake( () => stubs.octokitRestInstance ) - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( '@octokit/rest', { Octokit: stubs.octokitRest } ); - - isJobTriggeredByMember = proxyquire( '../lib/is-job-triggered-by-member', { - './utils/get-job-approver': stubs.getJobApprover - } ); - } ); - - afterEach( () => { - mockery.disable(); - sinon.restore(); - } ); - - it( 'should pass given parameters to services', async () => { - stubs.getJobApprover.resolves( 'foo' ); - stubs.octokitRestInstance.request.resolves( { data: [] } ); - - await isJobTriggeredByMember( { - circleToken: 'circle-token', - circleWorkflowId: 'abc-123-abc-456', - circleApprovalJobName: 'approval-job', - githubOrganization: 'ckeditor', - githubTeamSlug: 'team-slug', - githubToken: 'github-token' - } ); - - expect( stubs.octokitRest.callCount ).to.equal( 1 ); - expect( stubs.octokitRest.firstCall.firstArg ).to.have.property( 'auth', 'github-token' ); - - expect( stubs.getJobApprover.callCount ).to.equal( 1 ); - expect( stubs.getJobApprover.firstCall.args ).to.deep.equal( [ - 'circle-token', - 'abc-123-abc-456', - 'approval-job' - ] ); - - expect( stubs.octokitRestInstance.request.callCount ).to.equal( 1 ); - - const [ url, data ] = stubs.octokitRestInstance.request.firstCall.args; - - expect( url ).to.equal( 'GET /orgs/{org}/teams/{team_slug}/members' ); - expect( data ).to.have.property( 'org', 'ckeditor' ); - expect( data ).to.have.property( 'team_slug', 'team-slug' ); - expect( data ).to.have.property( 'headers' ); - expect( data.headers ).to.have.property( 'X-GitHub-Api-Version', '2022-11-28' ); - } ); - - it( 'should resolves true when a team member is allowed to trigger the given job', async () => { - // Who triggered. - stubs.getJobApprover.resolves( 'foo' ); - - // Who is allowed to trigger. - stubs.octokitRestInstance.request.resolves( { - data: [ - { login: 'foo' }, - { login: 'bar' } - ] - } ); - - const result = await isJobTriggeredByMember( { - circleToken: 'circle-token', - circleWorkflowId: 'abc-123-abc-456', - circleApprovalJobName: 'approval-job', - githubOrganization: 'ckeditor', - githubTeamSlug: 'team-slug', - githubToken: 'github-token' - } ); - - expect( result ).to.equal( true ); - } ); - - it( 'should resolves false when a team member is not allowed to trigger the given job', async () => { - // Who triggered. - stubs.getJobApprover.resolves( 'foo' ); - - // Who is allowed to trigger. - stubs.octokitRestInstance.request.resolves( { - data: [ - { login: 'bar' } - ] - } ); - - const result = await isJobTriggeredByMember( { - circleToken: 'circle-token', - circleWorkflowId: 'abc-123-abc-456', - circleApprovalJobName: 'approval-job', - githubOrganization: 'ckeditor', - githubTeamSlug: 'team-slug', - githubToken: 'github-token' - } ); - - expect( result ).to.equal( false ); - } ); -} ); diff --git a/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.test.js b/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.test.js new file mode 100644 index 000000000..f51ec35b5 --- /dev/null +++ b/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.test.js @@ -0,0 +1,115 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import isJobTriggeredByMember from '../lib/is-job-triggered-by-member'; +import getJobApprover from '../lib/utils/get-job-approver'; + +const { + octokitRequestMock, + octokitConstructorSpy +} = vi.hoisted( () => { + return { + octokitRequestMock: vi.fn(), + octokitConstructorSpy: vi.fn() + }; +} ); + +vi.mock( '../lib/utils/get-job-approver' ); + +vi.mock( '@octokit/rest', () => { + return { + Octokit: class { + constructor( ...args ) { + octokitConstructorSpy( ...args ); + + this.request = octokitRequestMock; + } + } + }; +} ); + +describe( 'lib/isJobTriggeredByMember', () => { + it( 'should pass given parameters to services', async () => { + vi.mocked( getJobApprover ).mockResolvedValue( 'foo' ); + vi.mocked( octokitRequestMock ).mockResolvedValue( { data: [] } ); + + await isJobTriggeredByMember( { + circleToken: 'circle-token', + circleWorkflowId: 'abc-123-abc-456', + circleApprovalJobName: 'approval-job', + githubOrganization: 'ckeditor', + githubTeamSlug: 'team-slug', + githubToken: 'github-token' + } ); + + expect( vi.mocked( octokitConstructorSpy ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( octokitConstructorSpy ) ).toHaveBeenCalledWith( { + 'auth': 'github-token' + } ); + + expect( vi.mocked( getJobApprover ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( getJobApprover ) ).toHaveBeenCalledWith( + 'circle-token', + 'abc-123-abc-456', + 'approval-job' + ); + + expect( vi.mocked( octokitRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( octokitRequestMock ) ).toHaveBeenCalledWith( + 'GET /orgs/{org}/teams/{team_slug}/members', + { + 'org': 'ckeditor', + 'team_slug': 'team-slug', + 'headers': { + 'X-GitHub-Api-Version': '2022-11-28' + } + } + ); + } ); + + it( 'should resolves true when a team member is allowed to trigger the given job', async () => { + // Who triggered. + vi.mocked( getJobApprover ).mockResolvedValue( 'foo' ); + + // Who is allowed to trigger. + vi.mocked( octokitRequestMock ).mockResolvedValue( { data: [ + { login: 'foo' }, + { login: 'bar' } + ] } ); + + const result = await isJobTriggeredByMember( { + circleToken: 'circle-token', + circleWorkflowId: 'abc-123-abc-456', + circleApprovalJobName: 'approval-job', + githubOrganization: 'ckeditor', + githubTeamSlug: 'team-slug', + githubToken: 'github-token' + } ); + + expect( result ).toEqual( true ); + } ); + + it( 'should resolves false when a team member is not allowed to trigger the given job', async () => { + // Who triggered. + vi.mocked( getJobApprover ).mockResolvedValue( 'foo' ); + + // Who is allowed to trigger. + vi.mocked( octokitRequestMock ).mockResolvedValue( { data: [ + { login: 'bar' } + ] } ); + + const result = await isJobTriggeredByMember( { + circleToken: 'circle-token', + circleWorkflowId: 'abc-123-abc-456', + circleApprovalJobName: 'approval-job', + githubOrganization: 'ckeditor', + githubTeamSlug: 'team-slug', + githubToken: 'github-token' + } ); + + expect( result ).toEqual( false ); + } ); +} ); diff --git a/packages/ckeditor5-dev-ci/tests/process-job-statuses.js b/packages/ckeditor5-dev-ci/tests/process-job-statuses.test.js similarity index 92% rename from packages/ckeditor5-dev-ci/tests/process-job-statuses.js rename to packages/ckeditor5-dev-ci/tests/process-job-statuses.test.js index 12bc51027..d25a32d97 100644 --- a/packages/ckeditor5-dev-ci/tests/process-job-statuses.js +++ b/packages/ckeditor5-dev-ci/tests/process-job-statuses.test.js @@ -5,17 +5,10 @@ /* eslint-env node */ -'use strict'; - -const expect = require( 'chai' ).expect; +import { describe, expect, it } from 'vitest'; +import processJobStatuses from '../lib/process-job-statuses'; describe( 'lib/process-job-statuses', () => { - let processJobStatuses; - - beforeEach( () => { - processJobStatuses = require( '../lib/process-job-statuses' ); - } ); - describe( 'processJobStatuses()', () => { it( 'should be a function', () => { expect( processJobStatuses ).to.be.a( 'function' ); @@ -38,7 +31,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -69,7 +62,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id1' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -97,7 +90,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id1' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -125,7 +118,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id1' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -169,7 +162,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id3' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -223,7 +216,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id_2', 'id_3' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -277,7 +270,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id_2', 'id_3' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -351,7 +344,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id_2', 'id_3', 'id_4', 'id_5' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); // Workflow: @@ -425,7 +418,7 @@ describe( 'lib/process-job-statuses', () => { dependencies: [ 'id_2', 'id_3', 'id_4', 'id_5' ] } ]; - expect( processJobStatuses( jobs ) ).to.deep.equal( expectedOutput ); + expect( processJobStatuses( jobs ) ).toEqual( expectedOutput ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.test.js similarity index 52% rename from packages/ckeditor5-dev-ci/tests/trigger-circle-build.js rename to packages/ckeditor5-dev-ci/tests/trigger-circle-build.test.js index 3afb4dd42..5d99bb219 100644 --- a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.test.js @@ -3,37 +3,15 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import nodeFetch from 'node-fetch'; +import triggerCircleBuild from '../lib/trigger-circle-build'; -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'node-fetch' ); describe( 'lib/triggerCircleBuild', () => { - let stubs, triggerCircleBuild; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - fetch: sinon.stub() - }; - - mockery.registerMock( 'node-fetch', stubs.fetch ); - - triggerCircleBuild = require( '../lib/trigger-circle-build' ); - } ); - - afterEach( () => { - mockery.disable(); - } ); - it( 'should send a POST request to the CircleCI service', async () => { - stubs.fetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -46,24 +24,28 @@ describe( 'lib/triggerCircleBuild', () => { repositorySlug: 'ckeditor/ckeditor5-dev' } ); - expect( stubs.fetch.callCount ).to.equal( 1 ); - - const [ url, options ] = stubs.fetch.firstCall.args; - - expect( url ).to.equal( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline' ); - expect( options ).to.have.property( 'method', 'post' ); - expect( options ).to.have.property( 'headers' ); - expect( options.headers ).to.have.property( 'Circle-Token', 'circle-token' ); - expect( options ).to.have.property( 'body' ); - - const body = JSON.parse( options.body ); - expect( body ).to.have.property( 'branch', 'master' ); - expect( body ).to.have.property( 'parameters' ); - expect( body.parameters ).to.have.property( 'triggerCommitHash', 'abcd1234' ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', + { + method: 'post', + headers: { + Accept: 'application/json', + 'Circle-Token': 'circle-token', + 'Content-Type': 'application/json' + }, + body: JSON.stringify( { + branch: 'master', + parameters: { + triggerCommitHash: 'abcd1234' + } + } ) + } + ); } ); it( 'should include the "isRelease=true" parameter when passing the `releaseBranch` option (the same release branch)', async () => { - stubs.fetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -77,19 +59,29 @@ describe( 'lib/triggerCircleBuild', () => { releaseBranch: 'master' } ); - expect( stubs.fetch.callCount ).to.equal( 1 ); - - const [ , options ] = stubs.fetch.firstCall.args; - - expect( options ).to.have.property( 'body' ); - - const body = JSON.parse( options.body ); - expect( body ).to.have.property( 'parameters' ); - expect( body.parameters ).to.have.property( 'isRelease', true ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', + { + method: 'post', + headers: { + Accept: 'application/json', + 'Circle-Token': 'circle-token', + 'Content-Type': 'application/json' + }, + body: JSON.stringify( { + branch: 'master', + parameters: { + triggerCommitHash: 'abcd1234', + isRelease: true + } + } ) + } + ); } ); it( 'should include the "isRelease=false" parameter when passing the `releaseBranch` option', async () => { - stubs.fetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -103,19 +95,29 @@ describe( 'lib/triggerCircleBuild', () => { releaseBranch: 'release' } ); - expect( stubs.fetch.callCount ).to.equal( 1 ); - - const [ , options ] = stubs.fetch.firstCall.args; - - expect( options ).to.have.property( 'body' ); - - const body = JSON.parse( options.body ); - expect( body ).to.have.property( 'parameters' ); - expect( body.parameters ).to.have.property( 'isRelease', false ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', + { + method: 'post', + headers: { + Accept: 'application/json', + 'Circle-Token': 'circle-token', + 'Content-Type': 'application/json' + }, + body: JSON.stringify( { + branch: 'master', + parameters: { + triggerCommitHash: 'abcd1234', + isRelease: false + } + } ) + } + ); } ); it( 'should include the "triggerRepositorySlug" parameter when passing the `releaseBranch` option', async () => { - stubs.fetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -129,19 +131,29 @@ describe( 'lib/triggerCircleBuild', () => { triggerRepositorySlug: 'ckeditor/ckeditor5' } ); - expect( stubs.fetch.callCount ).to.equal( 1 ); - - const [ , options ] = stubs.fetch.firstCall.args; - - expect( options ).to.have.property( 'body' ); - - const body = JSON.parse( options.body ); - expect( body ).to.have.property( 'parameters' ); - expect( body.parameters ).to.have.property( 'triggerRepositorySlug', 'ckeditor/ckeditor5' ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', + { + method: 'post', + headers: { + Accept: 'application/json', + 'Circle-Token': 'circle-token', + 'Content-Type': 'application/json' + }, + body: JSON.stringify( { + branch: 'master', + parameters: { + triggerCommitHash: 'abcd1234', + triggerRepositorySlug: 'ckeditor/ckeditor5' + } + } ) + } + ); } ); it( 'should reject a promise when CircleCI responds with an error containing error_message property', async () => { - stubs.fetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValue( { json: () => Promise.resolve( { error_message: 'HTTP 404' } ) @@ -164,7 +176,7 @@ describe( 'lib/triggerCircleBuild', () => { } ); it( 'should reject a promise when CircleCI responds with an error containing message property', async () => { - stubs.fetch.resolves( { + vi.mocked( nodeFetch ).mockResolvedValue( { json: () => Promise.resolve( { message: 'HTTP 404' } ) diff --git a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.test.js similarity index 51% rename from packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js rename to packages/ckeditor5-dev-ci/tests/utils/get-job-approver.test.js index a54918660..48093b910 100644 --- a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.test.js @@ -3,65 +3,43 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import getJobApprover from '../../lib/utils/get-job-approver'; +import nodeFetch from 'node-fetch'; -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'node-fetch' ); describe( 'lib/utils/getJobApprover', () => { - let stubs, getJobApprover; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - fetch: sinon.stub() - }; - - mockery.registerMock( 'node-fetch', stubs.fetch ); - - getJobApprover = require( '../../lib/utils/get-job-approver' ); - } ); - - afterEach( () => { - mockery.disable(); - } ); - it( 'should return a GitHub login name of a user who approved a job in given workflow', async () => { - stubs.fetch.onCall( 0 ).resolves( { - json: () => Promise.resolve( { - items: [ - { name: 'job-1' }, - { name: 'job-2', approved_by: 'foo-unique-id' } - ] - } ) - } ); - - stubs.fetch.onCall( 1 ).resolves( { - json: () => Promise.resolve( { - login: 'foo' + vi.mocked( nodeFetch ) + .mockResolvedValueOnce( { + json: () => Promise.resolve( { + items: [ + { name: 'job-1' }, + { name: 'job-2', approved_by: 'foo-unique-id' } + ] + } ) } ) - } ); + .mockResolvedValueOnce( { + json: () => Promise.resolve( { + login: 'foo' + } ) + } ); const login = await getJobApprover( 'circle-token', 'abc-123-abc-456', 'job-2' ); expect( login ).to.equal( 'foo' ); - expect( stubs.fetch.callCount ).to.equal( 2 ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 2 ); - const [ firstUrl, firstOptions ] = stubs.fetch.firstCall.args; + const [ firstUrl, firstOptions ] = vi.mocked( nodeFetch ).mock.calls[ 0 ]; expect( firstUrl ).to.equal( 'https://circleci.com/api/v2/workflow/abc-123-abc-456/job' ); expect( firstOptions ).to.have.property( 'method', 'get' ); expect( firstOptions ).to.have.property( 'headers' ); expect( firstOptions.headers ).to.have.property( 'Circle-Token', 'circle-token' ); - const [ secondUrl, secondOptions ] = stubs.fetch.getCall( 1 ).args; + const [ secondUrl, secondOptions ] = vi.mocked( nodeFetch ).mock.calls[ 1 ]; expect( secondUrl ).to.equal( 'https://circleci.com/api/v2/user/foo-unique-id' ); expect( secondOptions ).to.have.property( 'method', 'get' ); diff --git a/packages/ckeditor5-dev-ci/vitest.config.js b/packages/ckeditor5-dev-ci/vitest.config.js new file mode 100644 index 000000000..5cf966978 --- /dev/null +++ b/packages/ckeditor5-dev-ci/vitest.config.js @@ -0,0 +1,20 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + testTimeout: 10000, + restoreMocks: true, + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From ee84e7c908c56d30689b8eb42eac7e97e78f9526 Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:06:45 +0200 Subject: [PATCH 006/172] Cleanup. --- packages/ckeditor5-dev-ci/package.json | 5 ---- .../circle-update-auto-cancel-builds.test.js | 30 +++++++++++-------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 502cac9b6..99de3abb5 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -40,11 +40,6 @@ "slack-notify": "^2.0.6" }, "devDependencies": { - "chai": "^4.2.0", - "mocha": "^7.1.2", - "mockery": "^2.1.0", - "proxyquire": "^2.1.3", - "sinon": "^9.2.4", "vitest": "^2.0.5" }, "scripts": { diff --git a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js index 7f2f21b66..266104aed 100644 --- a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js +++ b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js @@ -11,7 +11,7 @@ vi.mock( 'node-fetch' ); describe( 'lib/circleUpdateAutoCancelBuilds', () => { it( 'should send a request to CircleCI to update the redundant workflows option', async () => { - const response = {}; + const response = { foo: 'bar' }; vi.mocked( nodeFetch ) .mockResolvedValue( { @@ -25,19 +25,23 @@ describe( 'lib/circleUpdateAutoCancelBuilds', () => { newValue: true } ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); expect( results ).to.deep.equal( response ); - const [ url, options ] = vi.mocked( nodeFetch ).mock.calls[ 0 ]; - - expect( url ).to.equal( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-foo/settings' ); - expect( options ).to.have.property( 'method', 'patch' ); - expect( options ).to.have.property( 'headers' ); - expect( options.headers ).to.have.property( 'Circle-Token', 'circle-token' ); - expect( options ).to.have.property( 'body' ); - - const body = JSON.parse( options.body ); - expect( body ).to.have.property( 'advanced' ); - expect( body.advanced ).to.have.property( 'autocancel_builds', true ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-foo/settings', + { + method: 'patch', + headers: { + 'Circle-Token': 'circle-token', + 'Content-Type': 'application/json' + }, + body: JSON.stringify( { + advanced: { + 'autocancel_builds': true + } + } ) + } + ); } ); } ); From 6d15156a8a4d070616f9b2f1773f61ccea3720fe Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 4 Sep 2024 18:51:14 +0200 Subject: [PATCH 007/172] Fixed a vitest config path for the coverage script. --- packages/ckeditor5-dev-ci/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 99de3abb5..3863a715d 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -44,6 +44,6 @@ }, "scripts": { "test": "vitest run --config vitest.config.js", - "coverage": "vitest run --config vitest.config.ts --coverage" + "coverage": "vitest run --config vitest.config.js --coverage" } } From 8aa0ab9c63b403f1b4eff82c4cebb741ec467326 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 5 Sep 2024 08:56:13 +0200 Subject: [PATCH 008/172] More tests. --- .../lib/tasks/creategithubrelease.js | 4 +- .../lib/tasks/preparerepository.js | 13 +- ...nuppackages.test.js => cleanuppackages.js} | 2 +- .../{commitandtag.test.js => commitandtag.js} | 0 .../tests/tasks/creategithubrelease.js | 212 ++++--- .../tests/tasks/preparerepository.js | 524 +++++++++--------- 6 files changed, 372 insertions(+), 383 deletions(-) rename packages/ckeditor5-dev-release-tools/tests/tasks/{cleanuppackages.test.js => cleanuppackages.js} (99%) rename packages/ckeditor5-dev-release-tools/tests/tasks/{commitandtag.test.js => commitandtag.js} (100%) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js index af846352c..e772d5cc4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js @@ -5,7 +5,9 @@ import { Octokit } from '@octokit/rest'; import semver from 'semver'; -import { getRepositoryUrl } from '../utils/transformcommitutils'; +import transformCommitUtils from '../utils/transformcommitutils'; + +const { getRepositoryUrl } = transformCommitUtils; /** * Create a GitHub release. diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js index ece147e37..cce9f1d08 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js @@ -4,7 +4,7 @@ */ import fs from 'fs-extra'; -import glob from 'glob'; +import { glob } from 'glob'; import upath from 'upath'; /** @@ -100,12 +100,13 @@ async function processRootPackage( { cwd, rootPackageJson, outputDirectoryPath } await fs.ensureDir( rootPackageOutputPath ); await fs.writeJson( pkgJsonOutputPath, rootPackageJson, { spaces: 2, EOL: '\n' } ); - return glob.sync( rootPackageJson.files ).map( absoluteFilePath => { - const relativeFilePath = upath.relative( cwd, absoluteFilePath ); - const absoluteFileOutputPath = upath.join( rootPackageOutputPath, relativeFilePath ); + return ( await glob( rootPackageJson.files ) ) + .map( absoluteFilePath => { + const relativeFilePath = upath.relative( cwd, absoluteFilePath ); + const absoluteFileOutputPath = upath.join( rootPackageOutputPath, relativeFilePath ); - return fs.copy( absoluteFilePath, absoluteFileOutputPath ); - } ); + return fs.copy( absoluteFilePath, absoluteFileOutputPath ); + } ); } /** diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js similarity index 99% rename from packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js rename to packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js index 59359ba8b..f75779b06 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.test.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js @@ -11,7 +11,7 @@ import upath from 'upath'; import { glob } from 'glob'; import mockFs from 'mock-fs'; -describe( 'dev-release-tools/tasks/cleanUpPackages()', () => { +describe( 'cleanUpPackages()', () => { let cleanUpPackages, stubs; beforeEach( async () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.test.js b/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js similarity index 100% rename from packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.test.js rename to packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js index f8ebb7fee..f80765594 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js @@ -3,132 +3,128 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); - -describe( 'dev-release-tools/tasks', () => { - describe( 'createGithubRelease()', () => { - let options, stubs, constructorOptionsCapture, createGithubRelease; - - beforeEach( () => { - options = { - token: 'abc123', - version: '1.3.5', - repositoryOwner: 'ckeditor', - repositoryName: 'ckeditor5-dev', - description: 'Very important release.' - }; - - stubs = { - octokit: { - repos: { - createRelease: sinon.stub().resolves(), - getLatestRelease: sinon.stub().rejects( { - status: 404 - } ) - } - } - }; - - class Octokit { - constructor( options ) { - constructorOptionsCapture = options; - - this.repos = stubs.octokit.repos; - } - } +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { Octokit } from '@octokit/rest'; +import createGithubRelease from '../../lib/tasks/creategithubrelease'; +import transformCommitUtils from '../../lib/utils/transformcommitutils'; + +vi.mock( '@octokit/rest', () => { + const stubs = { + getLatestRelease: vi.fn(), + createRelease: vi.fn() + }; + + const octokitMock = vi.fn( () => ( { + repos: { + getLatestRelease: stubs.getLatestRelease, + createRelease: stubs.createRelease + } + } ) ); + + octokitMock.__stubs = stubs; + + return { + Octokit: octokitMock + }; +} ); +vi.mock( '../../lib/utils/transformcommitutils' ); - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); +describe( 'createGithubRelease()', () => { + let options; - mockery.registerMock( '@octokit/rest', { Octokit } ); + beforeEach( () => { + options = { + token: 'abc123', + version: '1.3.5', + repositoryOwner: 'ckeditor', + repositoryName: 'ckeditor5-dev', + description: 'Very important release.' + }; - createGithubRelease = require( '../../lib/tasks/creategithubrelease' ); - } ); + vi.mocked( Octokit ).__stubs.getLatestRelease.mockRejectedValue( { status: 404 } ); + vi.mocked( Octokit ).__stubs.createRelease.mockResolvedValue(); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sinon.restore(); - } ); + vi.mocked( transformCommitUtils.getRepositoryUrl ).mockReturnValue( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); - it( 'should be a function', () => { - expect( createGithubRelease ).to.be.a( 'function' ); - } ); + // afterEach( () => { + // } ); - it( 'creates new Octokit instance with correct arguments', async () => { - await createGithubRelease( options ); + it( 'should be a function', () => { + expect( createGithubRelease ).to.be.a( 'function' ); + } ); - expect( constructorOptionsCapture ).to.deep.equal( { - version: '3.0.0', - auth: 'token abc123' - } ); - } ); + it( 'creates new Octokit instance with correct arguments', async () => { + await createGithubRelease( options ); - it( 'resolves a url to the created page', async () => { - const url = await createGithubRelease( options ); + expect( vi.mocked( Octokit ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( Octokit ) ).toHaveBeenCalledWith( expect.objectContaining( { + version: '3.0.0', + auth: 'token abc123' + } ) ); + } ); - expect( url ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev/releases/tag/v1.3.5' ); - } ); + it( 'resolves a url to the created page', async () => { + const url = await createGithubRelease( options ); - it( 'creates a non-prerelease page when passing a major.minor.patch version', async () => { - await createGithubRelease( options ); - - expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 1 ); - expect( stubs.octokit.repos.createRelease.getCall( 0 ).args.length ).to.equal( 1 ); - expect( stubs.octokit.repos.createRelease.getCall( 0 ).args[ 0 ] ).to.deep.equal( { - tag_name: 'v1.3.5', - owner: 'ckeditor', - repo: 'ckeditor5-dev', - body: 'Very important release.', - prerelease: false - } ); - } ); + expect( url ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev/releases/tag/v1.3.5' ); + } ); - it( 'creates a prerelease page when passing a major.minor.patch-prerelease version', async () => { - options.version = '1.3.5-alpha.0'; - await createGithubRelease( options ); - - expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 1 ); - expect( stubs.octokit.repos.createRelease.getCall( 0 ).args.length ).to.equal( 1 ); - expect( stubs.octokit.repos.createRelease.getCall( 0 ).args[ 0 ] ).to.deep.equal( { - tag_name: 'v1.3.5-alpha.0', - owner: 'ckeditor', - repo: 'ckeditor5-dev', - body: 'Very important release.', - prerelease: true - } ); - } ); + it( 'creates a non-prerelease page when passing a major.minor.patch version', async () => { + await createGithubRelease( options ); + + const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; + + expect( createReleaseMock ).toHaveBeenCalledOnce(); + expect( createReleaseMock ).toHaveBeenCalledWith( expect.objectContaining( { + tag_name: 'v1.3.5', + owner: 'ckeditor', + repo: 'ckeditor5-dev', + body: 'Very important release.', + prerelease: false + } ) ); + } ); + + it( 'creates a prerelease page when passing a major.minor.patch-prerelease version', async () => { + options.version = '1.3.5-alpha.0'; + await createGithubRelease( options ); - it( 'creates a new release if the previous release version are different', async () => { - stubs.octokit.repos.getLatestRelease.resolves( { - data: { - tag_name: 'v1.3.4' - } - } ); + const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; - await createGithubRelease( options ); + expect( createReleaseMock ).toHaveBeenCalledOnce(); + expect( createReleaseMock ).toHaveBeenCalledWith( expect.objectContaining( { + tag_name: 'v1.3.5-alpha.0', + owner: 'ckeditor', + repo: 'ckeditor5-dev', + body: 'Very important release.', + prerelease: true + } ) ); + } ); - expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 1 ); + it( 'creates a new release if the previous release version are different', async () => { + vi.mocked( Octokit ).__stubs.getLatestRelease.mockResolvedValue( { + data: { + tag_name: 'v1.3.4' + } } ); - it( 'does not create a new release if the previous release version are the same', async () => { - stubs.octokit.repos.getLatestRelease.resolves( { - data: { - tag_name: 'v1.3.5' - } - } ); + await createGithubRelease( options ); + const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; - const url = await createGithubRelease( options ); + expect( createReleaseMock ).toHaveBeenCalledOnce(); + } ); - expect( url ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev/releases/tag/v1.3.5' ); - expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 0 ); + it( 'does not create a new release if the previous release version are the same', async () => { + vi.mocked( Octokit ).__stubs.getLatestRelease.mockResolvedValue( { + data: { + tag_name: 'v1.3.5' + } } ); + + const url = await createGithubRelease( options ); + const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; + + expect( url ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev/releases/tag/v1.3.5' ); + expect( createReleaseMock ).not.toHaveBeenCalled(); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js index a40b64e47..5f779e564 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js @@ -3,115 +3,183 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import prepareRepository from '../../lib/tasks/preparerepository'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'fs-extra' ); +vi.mock( 'glob' ); -describe( 'dev-release-tools/tasks', () => { - describe( 'prepareRepository()', () => { - const packages = [ - 'ckeditor5-core', - 'ckeditor5-utils' - ]; +describe( 'prepareRepository()', () => { + const packages = [ + 'ckeditor5-core', + 'ckeditor5-utils' + ]; - let options, stubs, prepareRepository; + let options; - beforeEach( () => { - options = { - outputDirectory: 'release' - }; + beforeEach( () => { + options = { + outputDirectory: 'release' + }; - function stubReject( stubName, args ) { - if ( args.length === 0 ) { - throw new Error( `Stub "${ stubName }" expected to receive an argument.` ); - } + vi.spyOn( process, 'cwd' ).mockReturnValue( 'current/working/dir' ); + } ); - throw new Error( `No output configured for stub "${ stubName }" with the following args: ${ JSON.stringify( args ) }` ); + it( 'should be a function', () => { + expect( prepareRepository ).to.be.a( 'function' ); + } ); + + it( 'should do nothing if neither "rootPackage" or "packagesDirectory" options are defined', async () => { + await prepareRepository( options ); + + expect( vi.mocked( fs ).copy ).not.toHaveBeenCalled(); + expect( vi.mocked( fs ).ensureDir ).not.toHaveBeenCalled(); + expect( vi.mocked( fs ).writeJson ).not.toHaveBeenCalled(); + } ); + + it( 'should ensure the existence of the output directory', async () => { + vi.mocked( fs ).readdir.mockResolvedValue( [] ); + options.packagesDirectory = 'packages'; + + await prepareRepository( options ); + + expect( vi.mocked( fs ).ensureDir ).toHaveBeenCalled(); + expect( vi.mocked( fs ).ensureDir ).toHaveBeenCalledWith( 'current/working/dir/release' ); + } ); + + it( 'should throw if the output directory is not empty', async () => { + vi.mocked( fs ).readdir.mockImplementation( input => { + if ( input === 'current/working/dir/release' ) { + return Promise.resolve( [ 'someFile.txt' ] ); } - stubs = { - fs: { - copy: sinon.stub().resolves(), - ensureDir: sinon.stub().resolves(), - writeJson: sinon.stub().resolves(), + return Promise.resolve( [] ); + } ); - // These stubs will reject calls without predefined arguments. - lstat: sinon.stub().callsFake( ( ...args ) => stubReject( 'fs.lstat', args ) ), - exists: sinon.stub().callsFake( ( ...args ) => stubReject( 'fs.exists', args ) ), - readdir: sinon.stub().callsFake( ( ...args ) => stubReject( 'fs.readdir', args ) ) - }, - glob: { - sync: sinon.stub().callsFake( ( ...args ) => stubReject( 'glob.sync', args ) ) - }, - lstat: { - isDir: { - isDirectory: sinon.stub().returns( true ) - }, - isNotDir: { - isDirectory: sinon.stub().returns( false ) - } + options.packagesDirectory = 'packages'; + + await prepareRepository( options ) + .then( + () => { + throw new Error( 'Expected to throw.' ); }, - process: { - cwd: sinon.stub( process, 'cwd' ).returns( 'current/working/dir' ) + err => { + expect( err.message ).to.equal( 'Output directory is not empty: "current/working/dir/release".' ); } - }; + ); + + expect( vi.mocked( fs ).readdir ).toHaveBeenCalled(); + expect( vi.mocked( fs ).readdir ).toHaveBeenCalledWith( 'current/working/dir/release' ); + } ); - stubs.fs.readdir.withArgs( 'current/working/dir/release' ).resolves( [] ); - stubs.fs.readdir.withArgs( 'current/working/dir/packages' ).resolves( packages ); + it( 'should use the "cwd" option if provided instead of the default "process.cwd()" value', async () => { + vi.mocked( fs ).readdir.mockResolvedValue( [] ); + options.cwd = 'something/different/than/process/cwd'; + options.packagesDirectory = 'packages'; + + await prepareRepository( options ); + + expect( vi.mocked( fs ).ensureDir ).toHaveBeenCalled(); + expect( vi.mocked( fs ).ensureDir ).toHaveBeenCalledWith( 'something/different/than/process/cwd/release' ); + } ); + + it( 'should normalize Windows slashes "\\" from "process.cwd()"', async () => { + vi.mocked( fs ).readdir.mockResolvedValue( [] ); + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\windows\\working\\dir' ); + options.packagesDirectory = 'packages'; + + await prepareRepository( options ); + + expect( vi.mocked( fs ).ensureDir ).toHaveBeenCalled(); + expect( vi.mocked( fs ).ensureDir ).toHaveBeenCalledWith( 'C:/windows/working/dir/release' ); + } ); - stubs.glob.sync.withArgs( [ 'src/*.js', 'CHANGELOG.md' ] ).returns( [ + describe( 'root package processing', () => { + beforeEach( () => { + vi.mocked( fs ).readdir.mockResolvedValue( [] ); + + vi.mocked( glob ).mockResolvedValue( [ 'current/working/dir/src/core.js', 'current/working/dir/src/utils.js', 'current/working/dir/CHANGELOG.md' ] ); + } ); - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( 'glob', stubs.glob ); + it( 'should create "package.json" file in the root package with provided values', async () => { + options.rootPackageJson = { + name: 'ckeditor5', + description: 'Description.', + keywords: [ 'foo', 'bar', 'baz' ], + files: [ 'src/*.js', 'CHANGELOG.md' ] + }; - prepareRepository = require( '../../lib/tasks/preparerepository' ); - } ); + await prepareRepository( options ); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sinon.restore(); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledOnce(); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + 'current/working/dir/release/ckeditor5/package.json', + expect.objectContaining( { + name: 'ckeditor5', + description: 'Description.', + keywords: [ 'foo', 'bar', 'baz' ], + files: [ 'src/*.js', 'CHANGELOG.md' ] + } ), + expect.objectContaining( { + spaces: 2, + EOL: '\n' + } ) + ); } ); - it( 'should be a function', () => { - expect( prepareRepository ).to.be.a( 'function' ); - } ); + it( 'should create a flat output file structure for a scoped package', async () => { + options.rootPackageJson = { + name: '@ckeditor/ckeditor5-example', + files: [ 'src/*.js', 'CHANGELOG.md' ] + }; - it( 'should do nothing if neither "rootPackage" or "packagesDirectory" options are defined', async () => { await prepareRepository( options ); - expect( stubs.fs.copy.callCount ).to.equal( 0 ); - expect( stubs.fs.ensureDir.callCount ).to.equal( 0 ); - expect( stubs.fs.writeJson.callCount ).to.equal( 0 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledOnce(); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + 'current/working/dir/release/ckeditor5-example/package.json', + expect.any( Object ), + expect.any( Object ) + ); } ); - it( 'should ensure the existence of the output directory', async () => { - stubs.fs.readdir.withArgs( 'current/working/dir/packages' ).resolves( [] ); - options.packagesDirectory = 'packages'; + it( 'should copy specified files of the root package', async () => { + options.rootPackageJson = { + name: 'ckeditor5', + description: '', + keywords: [ 'foo', 'bar', 'baz' ], + files: [ 'src/*.js', 'CHANGELOG.md' ] + }; await prepareRepository( options ); - expect( stubs.fs.ensureDir.callCount ).to.equal( 1 ); - expect( stubs.fs.ensureDir.getCall( 0 ).args.length ).to.equal( 1 ); - expect( stubs.fs.ensureDir.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/release' ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/src/core.js', + 'current/working/dir/release/ckeditor5/src/core.js' + ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/src/utils.js', + 'current/working/dir/release/ckeditor5/src/utils.js' + ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/CHANGELOG.md', + 'current/working/dir/release/ckeditor5/CHANGELOG.md' + ); } ); - it( 'should throw if the output directory is not empty', async () => { - stubs.fs.readdir.withArgs( 'current/working/dir/release' ).resolves( [ 'someFile.txt' ] ); - stubs.fs.readdir.withArgs( 'current/working/dir/packages' ).resolves( [] ); - options.packagesDirectory = 'packages'; + it( 'should throw if "rootPackageJson" is missing the "name" field', async () => { + options.rootPackageJson = { + description: '', + keywords: [ 'foo', 'bar', 'baz' ], + files: [ 'src/*.js', 'CHANGELOG.md' ] + }; await prepareRepository( options ) .then( @@ -119,237 +187,159 @@ describe( 'dev-release-tools/tasks', () => { throw new Error( 'Expected to throw.' ); }, err => { - expect( err.message ).to.equal( 'Output directory is not empty: "current/working/dir/release".' ); + expect( err.message ).to.equal( '"rootPackageJson" option object must have a "name" field.' ); } ); - expect( stubs.fs.ensureDir.callCount ).to.equal( 1 ); - expect( stubs.fs.readdir.callCount ).to.equal( 1 ); - expect( stubs.fs.copy.callCount ).to.equal( 0 ); - } ); - - it( 'should use the "cwd" option if provided instead of the default "process.cwd()" value', async () => { - stubs.fs.readdir.withArgs( 'custom/working/dir/release' ).resolves( [] ); - stubs.fs.readdir.withArgs( 'custom/working/dir/packages' ).resolves( [] ); - options.cwd = 'custom/working/dir'; - options.packagesDirectory = 'packages'; - - await prepareRepository( options ); - - expect( stubs.fs.ensureDir.callCount ).to.equal( 1 ); - expect( stubs.fs.ensureDir.getCall( 0 ).args.length ).to.equal( 1 ); - expect( stubs.fs.ensureDir.getCall( 0 ).args[ 0 ] ).to.equal( 'custom/working/dir/release' ); + expect( vi.mocked( fs ).writeJson ).not.toHaveBeenCalled(); + expect( vi.mocked( fs ).copy ).not.toHaveBeenCalled(); } ); - it( 'should normalize Windows slashes "\\" from "process.cwd()"', async () => { - stubs.fs.readdir.withArgs( 'windows/working/dir/release' ).resolves( [] ); - stubs.fs.readdir.withArgs( 'windows/working/dir/packages' ).resolves( [] ); - stubs.process.cwd.returns( 'windows\\working\\dir' ); - options.packagesDirectory = 'packages'; + it( 'should throw if "rootPackageJson" is missing the "files" field', async () => { + options.rootPackageJson = { + name: 'ckeditor5', + description: '', + keywords: [ 'foo', 'bar', 'baz' ] + }; - await prepareRepository( options ); + await prepareRepository( options ) + .then( + () => { + throw new Error( 'Expected to throw.' ); + }, + err => { + expect( err.message ).to.equal( '"rootPackageJson" option object must have a "files" field.' ); + } + ); - expect( stubs.fs.ensureDir.callCount ).to.equal( 1 ); - expect( stubs.fs.ensureDir.getCall( 0 ).args.length ).to.equal( 1 ); - expect( stubs.fs.ensureDir.getCall( 0 ).args[ 0 ] ).to.equal( 'windows/working/dir/release' ); + expect( vi.mocked( fs ).writeJson ).not.toHaveBeenCalled(); + expect( vi.mocked( fs ).copy ).not.toHaveBeenCalled(); } ); + } ); - describe( 'root package processing', () => { - it( 'should create "package.json" file in the root package with provided values', async () => { - options.rootPackageJson = { - name: 'CKEditor5', - description: 'Foo bar baz.', - keywords: [ 'foo', 'bar', 'baz' ], - files: [ 'src/*.js', 'CHANGELOG.md' ] - }; - - await prepareRepository( options ); + describe( 'monorepository packages processing', () => { + beforeEach( () => { + vi.mocked( fs ).readdir.mockImplementation( input => { + if ( input.endsWith( 'release' ) ) { + return Promise.resolve( [] ); + } - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); - expect( stubs.fs.writeJson.getCall( 0 ).args.length ).to.equal( 3 ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/release/CKEditor5/package.json' ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 1 ] ).to.deep.equal( { - name: 'CKEditor5', - description: 'Foo bar baz.', - keywords: [ 'foo', 'bar', 'baz' ], - files: [ 'src/*.js', 'CHANGELOG.md' ] - } ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 2 ] ).to.deep.equal( { spaces: 2, EOL: '\n' } ); + return Promise.resolve( packages ); } ); + } ); - it( 'should create a flat output file structure for a scoped package', async () => { - options.rootPackageJson = { - name: '@ckeditor/CKEditor5', - files: [ 'src/*.js', 'CHANGELOG.md' ] - }; - - await prepareRepository( options ); + it( 'should copy files of all packages', async () => { + options.packagesDirectory = 'packages'; - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/release/CKEditor5/package.json' ); + vi.mocked( fs ).lstat.mockResolvedValue( { + isDirectory: () => true } ); + vi.mocked( fs ).exists.mockResolvedValue( true ); - it( 'should copy specified files of the root package', async () => { - options.rootPackageJson = { - name: 'CKEditor5', - description: 'Foo bar baz.', - keywords: [ 'foo', 'bar', 'baz' ], - files: [ 'src/*.js', 'CHANGELOG.md' ] - }; - - await prepareRepository( options ); + await prepareRepository( options ); - expect( stubs.fs.copy.callCount ).to.equal( 3 ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/packages/ckeditor5-core', + 'current/working/dir/release/ckeditor5-core' + ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/packages/ckeditor5-utils', + 'current/working/dir/release/ckeditor5-utils' + ); + } ); - expect( stubs.fs.copy.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/src/core.js' ); - expect( stubs.fs.copy.getCall( 0 ).args[ 1 ] ).to.equal( 'current/working/dir/release/CKEditor5/src/core.js' ); + it( 'should not copy non-directories', async () => { + packages.push( 'textFile.txt' ); + options.packagesDirectory = 'packages'; - expect( stubs.fs.copy.getCall( 1 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 1 ).args[ 0 ] ).to.equal( 'current/working/dir/src/utils.js' ); - expect( stubs.fs.copy.getCall( 1 ).args[ 1 ] ).to.equal( 'current/working/dir/release/CKEditor5/src/utils.js' ); + vi.mocked( fs ).lstat.mockImplementation( input => { + // Paths looking like a file are treated as files. + if ( input.match( /\.[a-z]+$/ ) ) { + return Promise.resolve( { + isDirectory: () => false + } ); + } - expect( stubs.fs.copy.getCall( 2 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 2 ).args[ 0 ] ).to.equal( 'current/working/dir/CHANGELOG.md' ); - expect( stubs.fs.copy.getCall( 2 ).args[ 1 ] ).to.equal( 'current/working/dir/release/CKEditor5/CHANGELOG.md' ); + return Promise.resolve( { + isDirectory: () => true + } ); } ); + vi.mocked( fs ).exists.mockResolvedValue( true ); - it( 'should throw if "rootPackageJson" is missing the "name" field', async () => { - options.rootPackageJson = { - description: 'Foo bar baz.', - keywords: [ 'foo', 'bar', 'baz' ], - files: [ 'src/*.js', 'CHANGELOG.md' ] - }; - - await prepareRepository( options ) - .then( - () => { - throw new Error( 'Expected to throw.' ); - }, - err => { - expect( err.message ).to.equal( '"rootPackageJson" option object must have a "name" field.' ); - } - ); - - expect( stubs.fs.writeJson.callCount ).to.equal( 0 ); - expect( stubs.fs.copy.callCount ).to.equal( 0 ); - } ); + await prepareRepository( options ); - it( 'should throw if "rootPackageJson" is missing the "files" field', async () => { - options.rootPackageJson = { - name: 'CKEditor5', - description: 'Foo bar baz.', - keywords: [ 'foo', 'bar', 'baz' ] - }; - - await prepareRepository( options ) - .then( - () => { - throw new Error( 'Expected to throw.' ); - }, - err => { - expect( err.message ).to.equal( '"rootPackageJson" option object must have a "files" field.' ); - } - ); - - expect( stubs.fs.writeJson.callCount ).to.equal( 0 ); - expect( stubs.fs.copy.callCount ).to.equal( 0 ); - } ); + expect( vi.mocked( fs ).lstat ).toHaveBeenCalledWith( 'current/working/dir/packages/textFile.txt' ); + + expect( vi.mocked( fs ).copy ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/packages/ckeditor5-core', + 'current/working/dir/release/ckeditor5-core' + ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/packages/ckeditor5-utils', + 'current/working/dir/release/ckeditor5-utils' + ); } ); - describe( 'monorepository packages processing', () => { - it( 'should copy files of all packages', async () => { - options.packagesDirectory = 'packages'; - - stubs.fs.lstat.withArgs( 'current/working/dir/packages/ckeditor5-core' ).resolves( stubs.lstat.isDir ); - stubs.fs.lstat.withArgs( 'current/working/dir/packages/ckeditor5-utils' ).resolves( stubs.lstat.isDir ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/ckeditor5-core/package.json' ).resolves( true ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/ckeditor5-utils/package.json' ).resolves( true ); - - await prepareRepository( options ); - - expect( stubs.fs.copy.callCount ).to.equal( 2 ); - - expect( stubs.fs.copy.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/packages/ckeditor5-core' ); - expect( stubs.fs.copy.getCall( 0 ).args[ 1 ] ).to.equal( 'current/working/dir/release/ckeditor5-core' ); + it( 'should not copy directories that do not have the "package.json" file', async () => { + options.packagesDirectory = 'packages'; - expect( stubs.fs.copy.getCall( 1 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 1 ).args[ 0 ] ).to.equal( 'current/working/dir/packages/ckeditor5-utils' ); - expect( stubs.fs.copy.getCall( 1 ).args[ 1 ] ).to.equal( 'current/working/dir/release/ckeditor5-utils' ); + vi.mocked( fs ).lstat.mockResolvedValue( { + isDirectory: () => true } ); - it( 'should not copy non-directories', async () => { - options.packagesDirectory = 'packages'; - - stubs.fs.readdir.withArgs( 'current/working/dir/packages' ).resolves( [ ...packages, 'textFile.txt' ] ); - - stubs.fs.lstat.withArgs( 'current/working/dir/packages/ckeditor5-core' ).resolves( stubs.lstat.isDir ); - stubs.fs.lstat.withArgs( 'current/working/dir/packages/ckeditor5-utils' ).resolves( stubs.lstat.isDir ); - stubs.fs.lstat.withArgs( 'current/working/dir/packages/textFile.txt' ).resolves( stubs.lstat.isNotDir ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/ckeditor5-core/package.json' ).resolves( true ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/ckeditor5-utils/package.json' ).resolves( true ); - - await prepareRepository( options ); - - expect( stubs.fs.copy.callCount ).to.equal( 2 ); - - expect( stubs.fs.copy.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/packages/ckeditor5-core' ); - expect( stubs.fs.copy.getCall( 0 ).args[ 1 ] ).to.equal( 'current/working/dir/release/ckeditor5-core' ); + vi.mocked( fs ).exists.mockImplementation( input => { + if ( input === 'current/working/dir/packages/ckeditor5-core/package.json' ) { + return Promise.resolve( true ); + } - expect( stubs.fs.copy.getCall( 1 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 1 ).args[ 0 ] ).to.equal( 'current/working/dir/packages/ckeditor5-utils' ); - expect( stubs.fs.copy.getCall( 1 ).args[ 1 ] ).to.equal( 'current/working/dir/release/ckeditor5-utils' ); + return Promise.resolve( false ); } ); - it( 'should not copy directories that do not have the "package.json" file', async () => { - options.packagesDirectory = 'packages'; - - stubs.fs.lstat.withArgs( 'current/working/dir/packages/ckeditor5-core' ).resolves( stubs.lstat.isDir ); - stubs.fs.lstat.withArgs( 'current/working/dir/packages/ckeditor5-utils' ).resolves( stubs.lstat.isDir ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/ckeditor5-core/package.json' ).resolves( true ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/ckeditor5-utils/package.json' ).resolves( false ); + await prepareRepository( options ); - await prepareRepository( options ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/packages/ckeditor5-core', + 'current/working/dir/release/ckeditor5-core' + ); + } ); - expect( stubs.fs.copy.callCount ).to.equal( 1 ); + it( 'should copy only the specified packages if the "packagesToCopy" option is provided', async () => { + options.packagesDirectory = 'packages'; + options.packagesToCopy = [ 'ckeditor5-core' ]; - expect( stubs.fs.copy.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/packages/ckeditor5-core' ); - expect( stubs.fs.copy.getCall( 0 ).args[ 1 ] ).to.equal( 'current/working/dir/release/ckeditor5-core' ); + vi.mocked( fs ).lstat.mockResolvedValue( { + isDirectory: () => true } ); + vi.mocked( fs ).exists.mockResolvedValue( true ); - it( 'should copy only the specified packages if the "packagesToCopy" option is provided', async () => { - options.packagesDirectory = 'packages'; - options.packagesToCopy = [ 'ckeditor5-core' ]; - - stubs.fs.lstat.withArgs( 'current/working/dir/packages/ckeditor5-core' ).resolves( stubs.lstat.isDir ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/ckeditor5-core/package.json' ).resolves( true ); + await prepareRepository( options ); - await prepareRepository( options ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/packages/ckeditor5-core', + 'current/working/dir/release/ckeditor5-core' + ); + } ); - expect( stubs.fs.copy.callCount ).to.equal( 1 ); + it( 'should allow copying nested packages via the "packagesToCopy" option', async () => { + options.packagesDirectory = 'packages'; + options.packagesToCopy = [ 'nested/ckeditor5-nested' ]; - expect( stubs.fs.copy.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/packages/ckeditor5-core' ); - expect( stubs.fs.copy.getCall( 0 ).args[ 1 ] ).to.equal( 'current/working/dir/release/ckeditor5-core' ); + vi.mocked( fs ).lstat.mockResolvedValue( { + isDirectory: () => true } ); + vi.mocked( fs ).exists.mockResolvedValue( true ); - it( 'should allow copying nested packages via the "packagesToCopy" option', async () => { - options.packagesDirectory = 'packages'; - options.packagesToCopy = [ 'nested/ckeditor5-nested' ]; - - stubs.fs.lstat.withArgs( 'current/working/dir/packages/nested/ckeditor5-nested' ).resolves( stubs.lstat.isDir ); - stubs.fs.exists.withArgs( 'current/working/dir/packages/nested/ckeditor5-nested/package.json' ).resolves( true ); - - await prepareRepository( options ); - - expect( stubs.fs.copy.callCount ).to.equal( 1 ); + await prepareRepository( options ); - expect( stubs.fs.copy.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.fs.copy.getCall( 0 ).args[ 0 ] ).to.equal( 'current/working/dir/packages/nested/ckeditor5-nested' ); - expect( stubs.fs.copy.getCall( 0 ).args[ 1 ] ).to.equal( 'current/working/dir/release/nested/ckeditor5-nested' ); - } ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).copy ).toHaveBeenCalledWith( + 'current/working/dir/packages/nested/ckeditor5-nested', + 'current/working/dir/release/nested/ckeditor5-nested' + ); } ); } ); } ); From 110d33ac5342cffe5af05594d47e9d74ebadef57 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 5 Sep 2024 08:56:30 +0200 Subject: [PATCH 009/172] Renamed tests paths (vitest). --- packages/ckeditor5-dev-release-tools/vitest.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/ckeditor5-dev-release-tools/vitest.config.js b/packages/ckeditor5-dev-release-tools/vitest.config.js index 5cf966978..5ad784a28 100644 --- a/packages/ckeditor5-dev-release-tools/vitest.config.js +++ b/packages/ckeditor5-dev-release-tools/vitest.config.js @@ -9,6 +9,9 @@ export default defineConfig( { test: { testTimeout: 10000, restoreMocks: true, + include: [ + 'tests/**/*.js' + ], coverage: { provider: 'v8', include: [ From 2273dfbba2d0bc74fd2008bbb18156aedc63c9b9 Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:25:33 +0200 Subject: [PATCH 010/172] Renamed tests. --- ...ncel-builds.test.js => circle-update-auto-cancel-builds.js} | 0 .../tests/data/{members.test.js => members.js} | 0 .../tests/{format-message.test.js => format-message.js} | 0 ...iggered-by-member.test.js => is-job-triggered-by-member.js} | 0 .../{process-job-statuses.test.js => process-job-statuses.js} | 0 .../{trigger-circle-build.test.js => trigger-circle-build.js} | 0 .../utils/{get-job-approver.test.js => get-job-approver.js} | 0 packages/ckeditor5-dev-ci/vitest.config.js | 3 +++ 8 files changed, 3 insertions(+) rename packages/ckeditor5-dev-ci/tests/{circle-update-auto-cancel-builds.test.js => circle-update-auto-cancel-builds.js} (100%) rename packages/ckeditor5-dev-ci/tests/data/{members.test.js => members.js} (100%) rename packages/ckeditor5-dev-ci/tests/{format-message.test.js => format-message.js} (100%) rename packages/ckeditor5-dev-ci/tests/{is-job-triggered-by-member.test.js => is-job-triggered-by-member.js} (100%) rename packages/ckeditor5-dev-ci/tests/{process-job-statuses.test.js => process-job-statuses.js} (100%) rename packages/ckeditor5-dev-ci/tests/{trigger-circle-build.test.js => trigger-circle-build.js} (100%) rename packages/ckeditor5-dev-ci/tests/utils/{get-job-approver.test.js => get-job-approver.js} (100%) diff --git a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js similarity index 100% rename from packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.test.js rename to packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js diff --git a/packages/ckeditor5-dev-ci/tests/data/members.test.js b/packages/ckeditor5-dev-ci/tests/data/members.js similarity index 100% rename from packages/ckeditor5-dev-ci/tests/data/members.test.js rename to packages/ckeditor5-dev-ci/tests/data/members.js diff --git a/packages/ckeditor5-dev-ci/tests/format-message.test.js b/packages/ckeditor5-dev-ci/tests/format-message.js similarity index 100% rename from packages/ckeditor5-dev-ci/tests/format-message.test.js rename to packages/ckeditor5-dev-ci/tests/format-message.js diff --git a/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.test.js b/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js similarity index 100% rename from packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.test.js rename to packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js diff --git a/packages/ckeditor5-dev-ci/tests/process-job-statuses.test.js b/packages/ckeditor5-dev-ci/tests/process-job-statuses.js similarity index 100% rename from packages/ckeditor5-dev-ci/tests/process-job-statuses.test.js rename to packages/ckeditor5-dev-ci/tests/process-job-statuses.js diff --git a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.test.js b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js similarity index 100% rename from packages/ckeditor5-dev-ci/tests/trigger-circle-build.test.js rename to packages/ckeditor5-dev-ci/tests/trigger-circle-build.js diff --git a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.test.js b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js similarity index 100% rename from packages/ckeditor5-dev-ci/tests/utils/get-job-approver.test.js rename to packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js diff --git a/packages/ckeditor5-dev-ci/vitest.config.js b/packages/ckeditor5-dev-ci/vitest.config.js index 5cf966978..5ad784a28 100644 --- a/packages/ckeditor5-dev-ci/vitest.config.js +++ b/packages/ckeditor5-dev-ci/vitest.config.js @@ -9,6 +9,9 @@ export default defineConfig( { test: { testTimeout: 10000, restoreMocks: true, + include: [ + 'tests/**/*.js' + ], coverage: { provider: 'v8', include: [ From 1287821c75ef9382030c297fbaadd6de02e98b8a Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 5 Sep 2024 12:56:27 +0200 Subject: [PATCH 011/172] Mocks are now hoisted to avoid using a non-stable API (constructions). --- .../tests/tasks/creategithubrelease.js | 60 +++++++++---------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js index f80765594..a43c5a793 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js @@ -4,29 +4,28 @@ */ import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { Octokit } from '@octokit/rest'; import createGithubRelease from '../../lib/tasks/creategithubrelease'; import transformCommitUtils from '../../lib/utils/transformcommitutils'; -vi.mock( '@octokit/rest', () => { - const stubs = { - getLatestRelease: vi.fn(), - createRelease: vi.fn() - }; - - const octokitMock = vi.fn( () => ( { - repos: { - getLatestRelease: stubs.getLatestRelease, - createRelease: stubs.createRelease +const stubs = vi.hoisted( () => ( { + constructor: vi.fn(), + getLatestRelease: vi.fn(), + createRelease: vi.fn() +} ) ); + +vi.mock( '@octokit/rest', () => ( { + Octokit: class { + constructor( ...args ) { + stubs.constructor( ...args ); + + this.repos = { + getLatestRelease: stubs.getLatestRelease, + createRelease: stubs.createRelease + }; } - } ) ); - - octokitMock.__stubs = stubs; + } +} ) ); - return { - Octokit: octokitMock - }; -} ); vi.mock( '../../lib/utils/transformcommitutils' ); describe( 'createGithubRelease()', () => { @@ -41,15 +40,12 @@ describe( 'createGithubRelease()', () => { description: 'Very important release.' }; - vi.mocked( Octokit ).__stubs.getLatestRelease.mockRejectedValue( { status: 404 } ); - vi.mocked( Octokit ).__stubs.createRelease.mockResolvedValue(); + stubs.getLatestRelease.mockRejectedValue( { status: 404 } ); + stubs.createRelease.mockResolvedValue(); vi.mocked( transformCommitUtils.getRepositoryUrl ).mockReturnValue( 'https://github.com/ckeditor/ckeditor5-dev' ); } ); - // afterEach( () => { - // } ); - it( 'should be a function', () => { expect( createGithubRelease ).to.be.a( 'function' ); } ); @@ -57,8 +53,8 @@ describe( 'createGithubRelease()', () => { it( 'creates new Octokit instance with correct arguments', async () => { await createGithubRelease( options ); - expect( vi.mocked( Octokit ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( Octokit ) ).toHaveBeenCalledWith( expect.objectContaining( { + expect( stubs.constructor ).toHaveBeenCalledOnce(); + expect( stubs.constructor ).toHaveBeenCalledWith( expect.objectContaining( { version: '3.0.0', auth: 'token abc123' } ) ); @@ -73,7 +69,7 @@ describe( 'createGithubRelease()', () => { it( 'creates a non-prerelease page when passing a major.minor.patch version', async () => { await createGithubRelease( options ); - const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; + const createReleaseMock = stubs.createRelease; expect( createReleaseMock ).toHaveBeenCalledOnce(); expect( createReleaseMock ).toHaveBeenCalledWith( expect.objectContaining( { @@ -89,7 +85,7 @@ describe( 'createGithubRelease()', () => { options.version = '1.3.5-alpha.0'; await createGithubRelease( options ); - const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; + const createReleaseMock = stubs.createRelease; expect( createReleaseMock ).toHaveBeenCalledOnce(); expect( createReleaseMock ).toHaveBeenCalledWith( expect.objectContaining( { @@ -102,29 +98,27 @@ describe( 'createGithubRelease()', () => { } ); it( 'creates a new release if the previous release version are different', async () => { - vi.mocked( Octokit ).__stubs.getLatestRelease.mockResolvedValue( { + stubs.getLatestRelease.mockResolvedValue( { data: { tag_name: 'v1.3.4' } } ); await createGithubRelease( options ); - const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; - expect( createReleaseMock ).toHaveBeenCalledOnce(); + expect( stubs.createRelease ).toHaveBeenCalledOnce(); } ); it( 'does not create a new release if the previous release version are the same', async () => { - vi.mocked( Octokit ).__stubs.getLatestRelease.mockResolvedValue( { + stubs.getLatestRelease.mockResolvedValue( { data: { tag_name: 'v1.3.5' } } ); const url = await createGithubRelease( options ); - const createReleaseMock = vi.mocked( Octokit ).__stubs.createRelease; expect( url ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev/releases/tag/v1.3.5' ); - expect( createReleaseMock ).not.toHaveBeenCalled(); + expect( stubs.createRelease ).not.toHaveBeenCalled(); } ); } ); From 316672d5c6b627a3de5799aaf63f457d04690bb4 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 5 Sep 2024 14:11:31 +0200 Subject: [PATCH 012/172] More tests. --- .../tests/tasks/cleanuppackages.js | 2 - .../tests/tasks/publishpackages.js | 667 +++++++++--------- 2 files changed, 317 insertions(+), 352 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js index f75779b06..5c83147a0 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; import fs from 'fs-extra'; import upath from 'upath'; diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js index 99d0c507e..5a7a11e43 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js @@ -5,410 +5,377 @@ 'use strict'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); -const upath = require( 'upath' ); - -describe( 'dev-release-tools/tasks', () => { - describe( 'publishPackages()', () => { - let publishPackages, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - glob: { - glob: sandbox.stub().resolves( [] ) - }, - assertNpmAuthorization: sandbox.stub().resolves(), - assertPackages: sandbox.stub().resolves(), - assertNpmTag: sandbox.stub().resolves(), - assertFilesToPublish: sandbox.stub().resolves(), - executeInParallel: sandbox.stub().resolves(), - publishPackageOnNpmCallback: sandbox.stub().resolves() - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'glob', stubs.glob ); - mockery.registerMock( '../utils/assertnpmauthorization', stubs.assertNpmAuthorization ); - mockery.registerMock( '../utils/assertpackages', stubs.assertPackages ); - mockery.registerMock( '../utils/assertnpmtag', stubs.assertNpmTag ); - mockery.registerMock( '../utils/assertfilestopublish', stubs.assertFilesToPublish ); - mockery.registerMock( '../utils/executeinparallel', stubs.executeInParallel ); - mockery.registerMock( '../utils/publishpackageonnpmcallback', stubs.publishPackageOnNpmCallback ); - - publishPackages = require( '../../lib/tasks/publishpackages' ); - } ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import upath from 'upath'; +import { glob } from 'glob'; +import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization'; +import assertPackages from '../../lib/utils/assertpackages'; +import assertNpmTag from '../../lib/utils/assertnpmtag'; +import assertFilesToPublish from '../../lib/utils/assertfilestopublish'; +import executeInParallel from '../../lib/utils/executeinparallel'; +import publishPackageOnNpmCallback from '../../lib/utils/publishpackageonnpmcallback'; +import publishPackages from '../../lib/tasks/publishpackages'; + +vi.mock( 'glob' ); +vi.mock( '../../lib/utils/assertnpmauthorization' ); +vi.mock( '../../lib/utils/assertpackages' ); +vi.mock( '../../lib/utils/assertnpmtag' ); +vi.mock( '../../lib/utils/assertfilestopublish' ); +vi.mock( '../../lib/utils/executeinparallel' ); +vi.mock( '../../lib/utils/publishpackageonnpmcallback' ); + +describe( 'publishPackages()', () => { + beforeEach( () => { + vi.mocked( glob ).mockResolvedValue( [] ); + vi.mocked( assertNpmAuthorization ).mockResolvedValue(); + vi.mocked( assertPackages ).mockResolvedValue(); + vi.mocked( assertNpmTag ).mockResolvedValue(); + vi.mocked( assertFilesToPublish ).mockResolvedValue(); + vi.mocked( executeInParallel ).mockResolvedValue(); + vi.mocked( publishPackageOnNpmCallback ).mockResolvedValue(); + } ); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); + it( 'should not throw if all assertion passes', async () => { + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' } ); + } ); - it( 'should not throw if all assertion passes', () => { - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ); + it( 'should read the package directory (default `cwd`)', async () => { + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' } ); - it( 'should read the package directory (default `cwd`)', () => { - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.equal( '*/' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'cwd', upath.join( process.cwd(), 'packages' ) ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'absolute', true ); - } ); - } ); + expect( vi.mocked( glob ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + '*/', + expect.objectContaining( { + cwd: upath.join( process.cwd(), 'packages' ), + absolute: true + } ) ); + } ); + + it( 'should read the package directory (custom `cwd`)', async () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/project' ); - it( 'should read the package directory (custom `cwd`)', () => { - sandbox.stub( process, 'cwd' ).returns( '/work/project' ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.equal( '*/' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'cwd', '/work/project/packages' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'absolute', true ); - } ); + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' } ); - it( 'should assert npm authorization', () => { - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( () => { - expect( stubs.assertNpmAuthorization.callCount ).to.equal( 1 ); - expect( stubs.assertNpmAuthorization.firstCall.args[ 0 ] ).to.equal( 'pepe' ); - } ); + expect( vi.mocked( glob ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + '*/', + expect.objectContaining( { + cwd: '/work/project/packages', + absolute: true + } ) ); + } ); + + it( 'should assert npm authorization', async () => { + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' } ); - it( 'should throw if npm authorization assertion failed', () => { - stubs.assertNpmAuthorization.throws( new Error( 'You must be logged to npm as "pepe" to execute this release step.' ) ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'You must be logged to npm as "pepe" to execute this release step.' - ); - } ); + expect( vi.mocked( assertNpmAuthorization ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( assertNpmAuthorization ) ).toHaveBeenCalledWith( 'pepe' ); + } ); + + it( 'should throw if npm authorization assertion failed', async () => { + vi.mocked( assertNpmAuthorization ).mockRejectedValue( + new Error( 'You must be logged to npm as "pepe" to execute this release step.' ) + ); + + await expect( publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'fake-pepe' + } ) ).rejects.toThrow( 'You must be logged to npm as "pepe" to execute this release step.' ); + } ); + + it( 'should assert that each found directory is a package', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' } ); - it( 'should assert that each found directory is a package', () => { - stubs.glob.glob.resolves( [ + expect( vi.mocked( assertPackages ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( assertPackages ) ).toHaveBeenCalledWith( + [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' - ] ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( () => { - expect( stubs.assertPackages.callCount ).to.equal( 1 ); - expect( stubs.assertPackages.firstCall.args[ 0 ] ).to.deep.equal( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); - expect( stubs.assertPackages.firstCall.args[ 1 ] ).to.deep.equal( { - requireEntryPoint: false, - optionalEntryPointPackages: [] - } ); - } ); + ], + { + requireEntryPoint: false, + optionalEntryPointPackages: [] + } + ); + } ); + + // See: https://github.com/ckeditor/ckeditor5/issues/15127. + it( 'should allow enabling the "package entry point" validator', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + requireEntryPoint: true, + optionalEntryPointPackages: [ + 'ckeditor5-foo' + ] } ); - // See: https://github.com/ckeditor/ckeditor5/issues/15127. - it( 'should allow enabling the "package entry point" validator', () => { - stubs.glob.glob.resolves( [ + expect( vi.mocked( assertPackages ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( assertPackages ) ).toHaveBeenCalledWith( + [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' - ] ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', + ], + { requireEntryPoint: true, optionalEntryPointPackages: [ 'ckeditor5-foo' ] - } ).then( () => { - expect( stubs.assertPackages.callCount ).to.equal( 1 ); - expect( stubs.assertPackages.firstCall.args[ 0 ] ).to.deep.equal( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); - expect( stubs.assertPackages.firstCall.args[ 1 ] ).to.deep.equal( { - requireEntryPoint: true, - optionalEntryPointPackages: [ - 'ckeditor5-foo' - ] - } ); - } ); - } ); + } + ); + } ); + + it( 'should throw if package assertion failed', async () => { + vi.mocked( assertPackages ).mockRejectedValue( + new Error( 'The "package.json" file is missing in the "ckeditor5-foo" package.' ) + ); + + await expect( publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ) ).rejects.toThrow( 'The "package.json" file is missing in the "ckeditor5-foo" package.' ); + } ); + + it( 'should assert that each required file exists in the package directory (no optional entries)', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ); - it( 'should throw if package assertion failed', () => { - stubs.assertPackages.throws( new Error( 'The "package.json" file is missing in the "ckeditor5-foo" package.' ) ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The "package.json" file is missing in the "ckeditor5-foo" package.' - ); - } ); + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' } ); - it( 'should assert that each required file exists in the package directory (no optional entries)', () => { - stubs.glob.glob.resolves( [ + expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledWith( + [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' - ] ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( () => { - expect( stubs.assertFilesToPublish.callCount ).to.equal( 1 ); - expect( stubs.assertFilesToPublish.firstCall.args[ 0 ] ).to.deep.equal( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); - expect( stubs.assertFilesToPublish.firstCall.args[ 1 ] ).to.equal( null ); - } ); + ], + null + ); + } ); + + it( 'should assert that each required file exists in the package directory (with optional entries)', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + optionalEntries: { + 'ckeditor5-foo': [ 'src' ] + } } ); - it( 'should assert that each required file exists in the package directory (with optional entries)', () => { - stubs.glob.glob.resolves( [ + expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledWith( + [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' - ] ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - optionalEntries: { - 'ckeditor5-foo': [ 'src' ] - } - } ).then( () => { - expect( stubs.assertFilesToPublish.callCount ).to.equal( 1 ); - expect( stubs.assertFilesToPublish.firstCall.args[ 0 ] ).to.deep.equal( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); - expect( stubs.assertFilesToPublish.firstCall.args[ 1 ] ).to.deep.equal( { - 'ckeditor5-foo': [ 'src' ] - } ); - } ); - } ); + ], + { + 'ckeditor5-foo': [ 'src' ] + } + ); + } ); + + it( 'should throw if not all required files exist in the package directory', async () => { + vi.mocked( assertFilesToPublish ).mockRejectedValue( + new Error( 'Missing files in "ckeditor5-foo" package for entries: "src"' ) + ); - it( 'should throw if not all required files exist in the package directory', () => { - stubs.assertFilesToPublish.throws( new Error( 'Missing files in "ckeditor5-foo" package for entries: "src"' ) ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src"' - ); - } ); + await expect( publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ) ).rejects.toThrow( 'Missing files in "ckeditor5-foo" package for entries: "src"' ); + } ); + + it( 'should assert that version tag matches the npm tag (default npm tag)', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' } ); - it( 'should assert that version tag matches the npm tag (default npm tag)', () => { - stubs.glob.glob.resolves( [ + expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledWith( + [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' - ] ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( () => { - expect( stubs.assertNpmTag.callCount ).to.equal( 1 ); - expect( stubs.assertNpmTag.firstCall.args[ 0 ] ).to.deep.equal( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); - expect( stubs.assertNpmTag.firstCall.args[ 1 ] ).to.equal( 'staging' ); - } ); + ], + 'staging' + ); + } ); + + it( 'should assert that version tag matches the npm tag (custom npm tag)', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + npmTag: 'nightly' } ); - it( 'should assert that version tag matches the npm tag (custom npm tag)', () => { - stubs.glob.glob.resolves( [ + expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledWith( + [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' - ] ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - npmTag: 'nightly' - } ).then( () => { - expect( stubs.assertNpmTag.callCount ).to.equal( 1 ); - expect( stubs.assertNpmTag.firstCall.args[ 0 ] ).to.deep.equal( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); - expect( stubs.assertNpmTag.firstCall.args[ 1 ] ).to.equal( 'nightly' ); - } ); - } ); + ], + 'nightly' + ); + } ); + + it( 'should throw if version tag does not match the npm tag', async () => { + vi.mocked( assertNpmTag ).mockRejectedValue( + new Error( 'The version tag "rc" from "ckeditor5-foo" package does not match the npm tag "staging".' ) + ); + + await expect( publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ) ).rejects.toThrow( 'The version tag "rc" from "ckeditor5-foo" package does not match the npm tag "staging".' ); + } ); - it( 'should throw if version tag does not match the npm tag', () => { - stubs.assertNpmTag.throws( - new Error( 'The version tag "rc" from "ckeditor5-foo" package does not match the npm tag "staging".' ) - ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The version tag "rc" from "ckeditor5-foo" package does not match the npm tag "staging".' - ); - } ); + it( 'should pass parameters for publishing packages', async () => { + const listrTask = {}; + const abortController = new AbortController(); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + npmTag: 'nightly', + listrTask, + signal: abortController.signal, + concurrency: 3, + cwd: '/home/cwd' } ); - it( 'should pass parameters for publishing packages', () => { - const listrTask = {}; - const abortController = new AbortController(); - const taskToExecute = stubs.publishPackageOnNpmCallback; - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - npmTag: 'nightly', - listrTask, - signal: abortController.signal, - concurrency: 3, - cwd: '/home/cwd' - } ).then( () => { - expect( stubs.executeInParallel.callCount ).to.equal( 1 ); - expect( stubs.executeInParallel.firstCall.args[ 0 ] ).to.have.property( 'packagesDirectory', 'packages' ); - expect( stubs.executeInParallel.firstCall.args[ 0 ] ).to.have.property( 'listrTask', listrTask ); - expect( stubs.executeInParallel.firstCall.args[ 0 ] ).to.have.property( 'taskToExecute', taskToExecute ); - expect( stubs.executeInParallel.firstCall.args[ 0 ] ).to.have.deep.property( 'taskOptions', { npmTag: 'nightly' } ); - expect( stubs.executeInParallel.firstCall.args[ 0 ] ).to.have.property( 'signal', abortController.signal ); - expect( stubs.executeInParallel.firstCall.args[ 0 ] ).to.have.property( 'concurrency', 3 ); - expect( stubs.executeInParallel.firstCall.args[ 0 ] ).to.have.property( 'cwd', '/home/cwd' ); - } ); + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledWith( { + packagesDirectory: 'packages', + listrTask, + taskToExecute: publishPackageOnNpmCallback, + taskOptions: { npmTag: 'nightly' }, + signal: abortController.signal, + concurrency: 3, + cwd: '/home/cwd' } ); + } ); - it( 'should publish packages on npm if confirmation callback is not set', () => { - const listrTask = {}; + it( 'should publish packages on npm if confirmation callback is not set', async () => { + const listrTask = {}; - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - listrTask - } ).then( () => { - expect( stubs.executeInParallel.callCount ).to.equal( 1 ); - } ); + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + listrTask } ); - it( 'should publish packages on npm if synchronous confirmation callback returns truthy value', () => { - const confirmationCallback = sandbox.stub().returns( true ); - const listrTask = {}; - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - confirmationCallback, - listrTask - } ).then( () => { - expect( stubs.executeInParallel.callCount ).to.equal( 1 ); - expect( confirmationCallback.callCount ).to.equal( 1 ); - } ); - } ); + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledOnce(); + } ); - it( 'should publish packages on npm if asynchronous confirmation callback returns truthy value', () => { - const confirmationCallback = sandbox.stub().resolves( true ); - const listrTask = {}; - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - confirmationCallback, - listrTask - } ).then( () => { - expect( stubs.executeInParallel.callCount ).to.equal( 1 ); - expect( confirmationCallback.callCount ).to.equal( 1 ); - } ); + it( 'should publish packages on npm if synchronous confirmation callback returns truthy value', async () => { + const confirmationCallback = vi.fn().mockReturnValue( true ); + const listrTask = {}; + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + confirmationCallback, + listrTask } ); - it( 'should not publish packages on npm if synchronous confirmation callback returns falsy value', () => { - const confirmationCallback = sandbox.stub().returns( false ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - confirmationCallback - } ).then( () => { - expect( stubs.executeInParallel.callCount ).to.equal( 0 ); - expect( confirmationCallback.callCount ).to.equal( 1 ); - } ); + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledOnce(); + } ); + + it( 'should publish packages on npm if asynchronous confirmation callback returns truthy value', async () => { + const confirmationCallback = vi.fn().mockResolvedValue( true ); + const listrTask = {}; + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + confirmationCallback, + listrTask } ); - it( 'should not publish packages on npm if asynchronous confirmation callback returns falsy value', () => { - const confirmationCallback = sandbox.stub().resolves( false ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe', - confirmationCallback - } ).then( () => { - expect( stubs.executeInParallel.callCount ).to.equal( 0 ); - expect( confirmationCallback.callCount ).to.equal( 1 ); - } ); + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledOnce(); + } ); + + it( 'should not publish packages on npm if synchronous confirmation callback returns falsy value', async () => { + const confirmationCallback = vi.fn().mockReturnValue( false ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + confirmationCallback } ); - it( 'should throw if publishing packages on npm failed', () => { - stubs.executeInParallel.throws( new Error( 'Unable to publish "ckeditor5-foo" package.' ) ); - - return publishPackages( { - packagesDirectory: 'packages', - npmOwner: 'pepe' - } ).then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( 'Unable to publish "ckeditor5-foo" package.' ); - } ); + expect( vi.mocked( executeInParallel ) ).not.toHaveBeenCalledOnce(); + expect( confirmationCallback ).toHaveBeenCalledOnce(); + } ); + + it( 'should not publish packages on npm if asynchronous confirmation callback returns falsy value', async () => { + const confirmationCallback = vi.fn().mockResolvedValue( false ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + confirmationCallback } ); + + expect( vi.mocked( executeInParallel ) ).not.toHaveBeenCalledOnce(); + expect( confirmationCallback ).toHaveBeenCalledOnce(); + } ); + + it( 'should throw if publishing packages on npm failed', async () => { + vi.mocked( executeInParallel ).mockRejectedValue( + new Error( 'Unable to publish "ckeditor5-foo" package.' ) + ); + + await expect( publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ) ).rejects.toThrow( + 'Unable to publish "ckeditor5-foo" package.' + ); } ); -} ); +} ) +; From 22ddb4e6c201b6ac0422a9c651e1d64010a09c48 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 5 Sep 2024 14:23:17 +0200 Subject: [PATCH 013/172] More and more tests. --- .../tests/tasks/push.js | 123 +++++++----------- 1 file changed, 49 insertions(+), 74 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/push.js b/packages/ckeditor5-dev-release-tools/tests/tasks/push.js index 8caff4b79..1a299d6a7 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/push.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/push.js @@ -5,92 +5,67 @@ 'use strict'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); - -describe( 'dev-release-tools/tasks', () => { - describe( 'push()', () => { - let options, stubs, push; - - beforeEach( () => { - options = { - releaseBranch: 'release', - version: '1.3.5', - cwd: 'custom/working/dir' - }; - - stubs = { - devUtils: { - tools: { - shExec: sinon.stub() - } - }, - process: { - cwd: sinon.stub( process, 'cwd' ).returns( 'current/working/dir' ) - }, - shellEscape: sinon.stub().callsFake( v => v[ 0 ] ) - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', stubs.devUtils ); - mockery.registerMock( 'shell-escape', stubs.shellEscape ); - - push = require( '../../lib/tasks/push' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sinon.restore(); - } ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import shellEscape from 'shell-escape'; +import push from '../../lib/tasks/push'; + +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'shell-escape' ); + +describe( 'push()', () => { + let options; + beforeEach( () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'current/working/dir' ); + vi.mocked( shellEscape ).mockImplementation( v => v[ 0 ] ); + vi.mocked( tools.shExec ).mockResolvedValue(); + + options = { + releaseBranch: 'release', + version: '1.3.5', + cwd: 'custom-modified/working/dir' + }; + } ); - it( 'should be a function', () => { - expect( push ).to.be.a( 'function' ); - } ); + it( 'should be a function', () => { + expect( push ).to.be.a( 'function' ); + } ); - it( 'should execute command with correct arguments', async () => { - stubs.devUtils.tools.shExec.resolves(); - await push( options ); + it( 'should execute command with correct arguments', async () => { + await push( options ); - expect( stubs.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stubs.devUtils.tools.shExec.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.devUtils.tools.shExec.getCall( 0 ).args[ 0 ] ).to.equal( 'git push origin release v1.3.5' ); - expect( stubs.devUtils.tools.shExec.getCall( 0 ).args[ 1 ] ).to.deep.equal( { - cwd: 'custom/working/dir', + expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledWith( + 'git push origin release v1.3.5', + { + cwd: 'custom-modified/working/dir', verbosity: 'error', async: true - } ); - } ); + } + ); + } ); - it( 'should use "process.cwd()" if the "cwd" option was not used', async () => { - delete options.cwd; + it( 'should use "process.cwd()" if the "cwd" option was not used', async () => { + delete options.cwd; - stubs.devUtils.tools.shExec.resolves(); - await push( options ); + await push( options ); - expect( stubs.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stubs.devUtils.tools.shExec.getCall( 0 ).args.length ).to.equal( 2 ); - expect( stubs.devUtils.tools.shExec.getCall( 0 ).args[ 0 ] ).to.equal( 'git push origin release v1.3.5' ); - expect( stubs.devUtils.tools.shExec.getCall( 0 ).args[ 1 ] ).to.deep.equal( { + expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledWith( + 'git push origin release v1.3.5', + { cwd: 'current/working/dir', verbosity: 'error', async: true - } ); - } ); + } + ); + } ); - it( 'should escape arguments passed to a shell command', async () => { - stubs.devUtils.tools.shExec.resolves(); - await push( options ); + it( 'should escape arguments passed to a shell command', async () => { + await push( options ); - expect( stubs.shellEscape.callCount ).to.equal( 2 ); - expect( stubs.shellEscape.firstCall.firstArg ).to.deep.equal( [ 'release' ] ); - expect( stubs.shellEscape.secondCall.firstArg ).to.deep.equal( [ '1.3.5' ] ); - } ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledWith( [ 'release' ] ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledWith( [ '1.3.5' ] ); } ); } ); From 052789be16463d77a6536e6fec4137b0796729ee Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 6 Sep 2024 08:51:20 +0200 Subject: [PATCH 014/172] Added more tests and introduced a helper for more assertions. --- .../ckeditor5-dev-release-tools/package.json | 1 + .../tests/_utils/testsetup.js | 9 + .../tests/tasks/cleanuppackages.js | 15 +- .../tests/tasks/creategithubrelease.js | 9 +- .../tests/tasks/preparerepository.js | 6 +- .../tests/tasks/publishpackages.js | 30 +-- .../tests/tasks/push.js | 3 +- .../tests/tasks/reassignnpmtags.js | 240 +++++++++--------- .../vitest.config.js | 4 + 9 files changed, 154 insertions(+), 163 deletions(-) create mode 100644 packages/ckeditor5-dev-release-tools/tests/_utils/testsetup.js diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 6240928dc..380595be8 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -43,6 +43,7 @@ "upath": "^2.0.1" }, "devDependencies": { + "jest-extended": "^4.0.2", "vitest": "^2.0.5", "handlebars": "^4.7.6", "mock-fs": "^5.1.2" diff --git a/packages/ckeditor5-dev-release-tools/tests/_utils/testsetup.js b/packages/ckeditor5-dev-release-tools/tests/_utils/testsetup.js new file mode 100644 index 000000000..52e540b96 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/_utils/testsetup.js @@ -0,0 +1,9 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { expect } from 'vitest'; +import * as matchers from 'jest-extended'; + +expect.extend( matchers ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js index 5c83147a0..64e1a6495 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js @@ -51,8 +51,7 @@ describe( 'cleanUpPackages()', () => { cwd: '/work/another/project' } ); - expect( stubs.glob ).toHaveBeenCalledOnce(); - expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + expect( stubs.glob ).toHaveBeenCalledExactlyOnceWith( expect.any( String ), expect.objectContaining( { cwd: '/work/another/project/release' } ) ); } ); @@ -64,8 +63,7 @@ describe( 'cleanUpPackages()', () => { packagesDirectory: 'release' } ); - expect( stubs.glob ).toHaveBeenCalledOnce(); - expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + expect( stubs.glob ).toHaveBeenCalledExactlyOnceWith( expect.any( String ), expect.objectContaining( { cwd: '/work/project/release' } ) ); } ); @@ -75,8 +73,7 @@ describe( 'cleanUpPackages()', () => { packagesDirectory: 'release' } ); - expect( stubs.glob ).toHaveBeenCalledOnce(); - expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + expect( stubs.glob ).toHaveBeenCalledExactlyOnceWith( expect.any( String ), expect.objectContaining( { nodir: true } ) ); } ); @@ -86,8 +83,7 @@ describe( 'cleanUpPackages()', () => { packagesDirectory: 'release' } ); - expect( stubs.glob ).toHaveBeenCalledOnce(); - expect( stubs.glob ).toHaveBeenCalledWith( expect.any( String ), expect.objectContaining( { + expect( stubs.glob ).toHaveBeenCalledExactlyOnceWith( expect.any( String ), expect.objectContaining( { absolute: true } ) ); } ); @@ -97,8 +93,7 @@ describe( 'cleanUpPackages()', () => { packagesDirectory: 'release' } ); - expect( stubs.glob ).toHaveBeenCalledOnce(); - expect( stubs.glob ).toHaveBeenCalledWith( '*/package.json', expect.any( Object ) ); + expect( stubs.glob ).toHaveBeenCalledExactlyOnceWith( '*/package.json', expect.any( Object ) ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js index a43c5a793..4c28d38a5 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js @@ -53,8 +53,7 @@ describe( 'createGithubRelease()', () => { it( 'creates new Octokit instance with correct arguments', async () => { await createGithubRelease( options ); - expect( stubs.constructor ).toHaveBeenCalledOnce(); - expect( stubs.constructor ).toHaveBeenCalledWith( expect.objectContaining( { + expect( stubs.constructor ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { version: '3.0.0', auth: 'token abc123' } ) ); @@ -71,8 +70,7 @@ describe( 'createGithubRelease()', () => { const createReleaseMock = stubs.createRelease; - expect( createReleaseMock ).toHaveBeenCalledOnce(); - expect( createReleaseMock ).toHaveBeenCalledWith( expect.objectContaining( { + expect( createReleaseMock ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { tag_name: 'v1.3.5', owner: 'ckeditor', repo: 'ckeditor5-dev', @@ -87,8 +85,7 @@ describe( 'createGithubRelease()', () => { const createReleaseMock = stubs.createRelease; - expect( createReleaseMock ).toHaveBeenCalledOnce(); - expect( createReleaseMock ).toHaveBeenCalledWith( expect.objectContaining( { + expect( createReleaseMock ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { tag_name: 'v1.3.5-alpha.0', owner: 'ckeditor', repo: 'ckeditor5-dev', diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js index 5f779e564..e379c8647 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js @@ -117,8 +117,7 @@ describe( 'prepareRepository()', () => { await prepareRepository( options ); - expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledOnce(); - expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledExactlyOnceWith( 'current/working/dir/release/ckeditor5/package.json', expect.objectContaining( { name: 'ckeditor5', @@ -141,8 +140,7 @@ describe( 'prepareRepository()', () => { await prepareRepository( options ); - expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledOnce(); - expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledExactlyOnceWith( 'current/working/dir/release/ckeditor5-example/package.json', expect.any( Object ), expect.any( Object ) diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js index 5a7a11e43..c5fbd6588 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js @@ -48,8 +48,7 @@ describe( 'publishPackages()', () => { npmOwner: 'pepe' } ); - expect( vi.mocked( glob ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( '*/', expect.objectContaining( { cwd: upath.join( process.cwd(), 'packages' ), @@ -65,8 +64,7 @@ describe( 'publishPackages()', () => { npmOwner: 'pepe' } ); - expect( vi.mocked( glob ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( '*/', expect.objectContaining( { cwd: '/work/project/packages', @@ -80,8 +78,7 @@ describe( 'publishPackages()', () => { npmOwner: 'pepe' } ); - expect( vi.mocked( assertNpmAuthorization ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( assertNpmAuthorization ) ).toHaveBeenCalledWith( 'pepe' ); + expect( vi.mocked( assertNpmAuthorization ) ).toHaveBeenCalledExactlyOnceWith( 'pepe' ); } ); it( 'should throw if npm authorization assertion failed', async () => { @@ -106,8 +103,7 @@ describe( 'publishPackages()', () => { npmOwner: 'pepe' } ); - expect( vi.mocked( assertPackages ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( assertPackages ) ).toHaveBeenCalledWith( + expect( vi.mocked( assertPackages ) ).toHaveBeenCalledExactlyOnceWith( [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' @@ -135,8 +131,7 @@ describe( 'publishPackages()', () => { ] } ); - expect( vi.mocked( assertPackages ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( assertPackages ) ).toHaveBeenCalledWith( + expect( vi.mocked( assertPackages ) ).toHaveBeenCalledExactlyOnceWith( [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' @@ -172,8 +167,7 @@ describe( 'publishPackages()', () => { npmOwner: 'pepe' } ); - expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledWith( + expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledExactlyOnceWith( [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' @@ -196,8 +190,7 @@ describe( 'publishPackages()', () => { } } ); - expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledWith( + expect( vi.mocked( assertFilesToPublish ) ).toHaveBeenCalledExactlyOnceWith( [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' @@ -230,8 +223,7 @@ describe( 'publishPackages()', () => { npmOwner: 'pepe' } ); - expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledWith( + expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledExactlyOnceWith( [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' @@ -252,8 +244,7 @@ describe( 'publishPackages()', () => { npmTag: 'nightly' } ); - expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledWith( + expect( vi.mocked( assertNpmTag ) ).toHaveBeenCalledExactlyOnceWith( [ '/work/project/packages/ckeditor5-foo', '/work/project/packages/ckeditor5-bar' @@ -287,8 +278,7 @@ describe( 'publishPackages()', () => { cwd: '/home/cwd' } ); - expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledWith( { + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledExactlyOnceWith( { packagesDirectory: 'packages', listrTask, taskToExecute: publishPackageOnNpmCallback, diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/push.js b/packages/ckeditor5-dev-release-tools/tests/tasks/push.js index 1a299d6a7..7f9fd5111 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/push.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/push.js @@ -50,8 +50,7 @@ describe( 'push()', () => { await push( options ); - expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledOnce(); - expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledWith( + expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledExactlyOnceWith( 'git push origin release v1.3.5', { cwd: 'current/working/dir', diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js b/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js index 774bb1a52..f5e8fb636 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js @@ -5,200 +5,198 @@ 'use strict'; -const { expect } = require( 'chai' ); -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import columns from 'cli-columns'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import shellEscape from 'shell-escape'; +import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization'; +import reassignNpmTags from '../../lib/tasks/reassignnpmtags'; + +const stubs = vi.hoisted( () => { + const values = { + spinner: { + start: vi.fn(), + increase: vi.fn(), + finish: vi.fn() + }, + exec: vi.fn(), + chalk: { + bold: vi.fn( () => stubs.chalk ), + green: vi.fn( input => input ), + yellow: vi.fn( input => input ), + red: vi.fn( input => input ) + } + }; -describe( 'reassignNpmTags()', () => { - let stubs, reassignNpmTags; + // To make `chalk.bold.yellow.red()` working. + for ( const rootKey of Object.keys( values.chalk ) ) { + for ( const nestedKey of Object.keys( values.chalk ) ) { + values.chalk[ rootKey ][ nestedKey ] = values.chalk[ nestedKey ]; + } + } - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); + return values; +} ); - stubs = { - tools: { - createSpinner: sinon.stub().callsFake( () => { - return stubs.spinner; - } ) - }, - assertNpmAuthorization: sinon.stub().resolves( true ), - spinner: { - start: sinon.stub(), - increase: sinon.stub(), - finish: sinon.stub() - }, - chalk: { - get bold() { - return stubs.chalk; - }, - green: sinon.stub().callsFake( str => str ), - yellow: sinon.stub().callsFake( str => str ), - red: sinon.stub().callsFake( str => str ) - }, - columns: sinon.stub(), - console: { - log: sinon.stub( console, 'log' ) - }, - util: { - promisify: sinon.stub().callsFake( () => stubs.exec ) - }, - exec: sinon.stub(), - shellEscape: sinon.stub().callsFake( v => v[ 0 ] ) - }; - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { tools: stubs.tools } ); - mockery.registerMock( '../utils/assertnpmauthorization', stubs.assertNpmAuthorization ); - mockery.registerMock( 'cli-columns', stubs.columns ); - mockery.registerMock( 'chalk', stubs.chalk ); - mockery.registerMock( 'util', stubs.util ); - mockery.registerMock( 'shell-escape', stubs.shellEscape ); - - reassignNpmTags = require( '../../lib/tasks/reassignnpmtags' ); - } ); +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} ); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sinon.restore(); +vi.mock( '@ckeditor/ckeditor5-dev-utils', () => ( { + tools: { + createSpinner: vi.fn( () => stubs.spinner ) + } +} ) ); +vi.mock( 'util', () => ( { + default: { + promisify: vi.fn( () => stubs.exec ) + } +} ) ); +vi.mock( 'shell-escape' ); +vi.mock( 'cli-columns' ); +vi.mock( 'chalk', () => ( { + default: stubs.chalk +} ) ); +vi.mock( 'shell-escape' ); +vi.mock( '../../lib/utils/assertnpmauthorization' ); + +describe( 'reassignNpmTags()', () => { + beforeEach( () => { + vi.mocked( shellEscape ).mockImplementation( v => v[ 0 ] ); + vi.mocked( assertNpmAuthorization ).mockResolvedValue( true ); } ); it( 'should throw an error when assertNpmAuthorization throws error', async () => { - stubs.assertNpmAuthorization.throws( new Error( 'User not logged in error' ) ); - const npmDistTagAdd = stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ); - - try { - await reassignNpmTags( { npmOwner: 'correct-npm-user', version: '1.0.1', packages: [ 'package1' ] } ); - throw new Error( 'Expected to throw' ); - } catch ( e ) { - expect( e.message ).to.equal( 'User not logged in error' ); - } + vi.mocked( assertNpmAuthorization ).mockRejectedValue( + new Error( 'User not logged in error' ) + ); + await expect( reassignNpmTags( { npmOwner: 'correct-npm-user', version: '1.0.1', packages: [ 'package1' ] } ) ) + .rejects.toThrow( 'User not logged in error' ); - expect( npmDistTagAdd.callCount ).to.equal( 0 ); + expect( stubs.exec ).not.toHaveBeenCalled(); } ); it( 'should skip updating tags when provided version matches existing version for tag latest', async () => { - stubs.columns.returns( 'package1 | package2' ); - stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ).throws( new Error( 'is already set to version' ) ); + vi.mocked( columns ).mockReturnValue( 'package1 | package2' ); + stubs.exec.mockRejectedValue( new Error( 'is already set to version' ) ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.0', packages: [ 'package1', 'package2' ] } ); - expect( stubs.console.log.firstCall.args[ 0 ] ).to.equal( 'âŦ‡ī¸ Packages skipped:' ); - expect( stubs.console.log.secondCall.args[ 0 ] ).to.deep.equal( 'package1 | package2' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( 'âŦ‡ī¸ Packages skipped:' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( 'package1 | package2' ); } ); it( 'should update tags when tag latest for provided version does not yet exist', async () => { - const npmDistTagAdd = stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ).resolves( { stdout: '+latest' } ); + stubs.exec.mockResolvedValue( { stdout: '+latest' } ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1', 'package2' ] } ); - expect( npmDistTagAdd.firstCall.args[ 0 ] ).to.equal( 'npm dist-tag add package1@1.0.1 latest' ); - expect( npmDistTagAdd.secondCall.args[ 0 ] ).to.equal( 'npm dist-tag add package2@1.0.1 latest' ); + expect( stubs.exec ).toHaveBeenCalledTimes( 2 ); + expect( stubs.exec ).toHaveBeenCalledWith( 'npm dist-tag add package1@1.0.1 latest' ); + expect( stubs.exec ).toHaveBeenCalledWith( 'npm dist-tag add package2@1.0.1 latest' ); } ); it( 'should continue updating packages even if first package update fails', async () => { - const npmDistTagAdd = stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ); - npmDistTagAdd.onFirstCall().throws( new Error( 'Npm error while updating tag.' ) ); + stubs.exec + .mockRejectedValueOnce( new Error( 'is already set to version' ) ) + .mockResolvedValueOnce( { stdout: '+latest' } ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1', 'package2' ] } ); - expect( npmDistTagAdd.firstCall.args[ 0 ] ).to.equal( 'npm dist-tag add package1@1.0.1 latest' ); - expect( npmDistTagAdd.secondCall.args[ 0 ] ).to.equal( 'npm dist-tag add package2@1.0.1 latest' ); + expect( stubs.exec ).toHaveBeenCalledWith( 'npm dist-tag add package1@1.0.1 latest' ); + expect( stubs.exec ).toHaveBeenCalledWith( 'npm dist-tag add package2@1.0.1 latest' ); } ); it( 'should escape arguments passed to a shell command', async () => { - stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ).resolves( { stdout: '+latest' } ); + stubs.exec.mockResolvedValue( { stdout: '+latest' } ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1' ] } ); - expect( stubs.shellEscape.callCount ).to.equal( 2 ); - expect( stubs.shellEscape.firstCall.firstArg ).to.deep.equal( [ 'package1' ] ); - expect( stubs.shellEscape.secondCall.firstArg ).to.deep.equal( [ '1.0.1' ] ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledWith( [ 'package1' ] ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledWith( [ '1.0.1' ] ); } ); describe( 'UX', () => { it( 'should create a spinner before starting processing packages', async () => { await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [] } ); - expect( stubs.tools.createSpinner.callCount ).to.equal( 1 ); - expect( stubs.tools.createSpinner.firstCall.args[ 0 ] ).to.equal( 'Reassigning npm tags...' ); - expect( stubs.tools.createSpinner.firstCall.args[ 1 ] ).to.be.an( 'object' ); - expect( stubs.tools.createSpinner.firstCall.args[ 1 ] ).to.have.property( 'total', 0 ); - - expect( stubs.spinner.start.callCount ).to.equal( 1 ); + expect( vi.mocked( tools ).createSpinner ).toHaveBeenCalledExactlyOnceWith( + 'Reassigning npm tags...', + { + total: 0 + } + ); + expect( stubs.spinner.start ).toHaveBeenCalledOnce(); } ); it( 'should increase the spinner counter after successfully processing a package', async () => { await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1' ] } ); - expect( stubs.spinner.increase.callCount ).to.equal( 1 ); + expect( stubs.spinner.increase ).toHaveBeenCalledTimes( 1 ); } ); it( 'should increase the spinner counter after failure processing a package', async () => { - const npmDistTagAdd = stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ); - npmDistTagAdd.onFirstCall().throws( new Error( 'Npm error while updating tag.' ) ); + stubs.exec.mockRejectedValue( new Error( 'is already set to version' ) ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1' ] } ); - expect( stubs.spinner.increase.callCount ).to.equal( 1 ); + expect( stubs.spinner.increase ).toHaveBeenCalledTimes( 1 ); } ); it( 'should finish the spinner once all packages have been processed', async () => { - const npmDistTagAdd = stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ); - npmDistTagAdd.onFirstCall().throws( new Error( 'Npm error while updating tag.' ) ); + stubs.exec + .mockRejectedValueOnce( new Error( 'is already set to version' ) ) + .mockResolvedValueOnce( { stdout: '+latest' } ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1', 'package2' ] } ); - sinon.assert.callOrder( - stubs.spinner.start, - stubs.spinner.increase, - stubs.spinner.increase, - stubs.spinner.finish - ); + expect( stubs.spinner.start ).toHaveBeenCalledTimes( 1 ); + expect( stubs.spinner.increase ).toHaveBeenCalledTimes( 2 ); + expect( stubs.spinner.finish ).toHaveBeenCalledTimes( 1 ); + + expect( stubs.spinner.start ).toHaveBeenCalledBefore( stubs.spinner.increase ); + expect( stubs.spinner.start ).toHaveBeenCalledBefore( stubs.spinner.finish ); + expect( stubs.spinner.increase ).toHaveBeenCalledBefore( stubs.spinner.finish ); } ); it( 'should display skipped packages in a column', async () => { - stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ).throws( new Error( 'is already set to version' ) ); - stubs.columns.returns( '1 | 2 | 3' ); + stubs.exec.mockRejectedValue( new Error( 'is already set to version' ) ); + vi.mocked( columns ).mockReturnValue( '1 | 2 | 3' ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.0', packages: [ 'package1', 'package2' ] } ); - expect( stubs.columns.callCount ).to.equal( 1 ); - expect( stubs.columns.firstCall.args[ 0 ] ).to.be.an( 'array' ); - expect( stubs.columns.firstCall.args[ 0 ] ).to.include( 'package1' ); - expect( stubs.columns.firstCall.args[ 0 ] ).to.include( 'package2' ); - expect( stubs.console.log.callCount ).to.equal( 2 ); - expect( stubs.console.log.firstCall.args[ 0 ] ).to.equal( 'âŦ‡ī¸ Packages skipped:' ); - expect( stubs.console.log.secondCall.args[ 0 ] ).to.equal( '1 | 2 | 3' ); + expect( vi.mocked( columns ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( columns ) ).toHaveBeenCalledWith( [ 'package1', 'package2' ] ); + expect( vi.mocked( console ).log ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( 'âŦ‡ī¸ Packages skipped:' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( '1 | 2 | 3' ); } ); it( 'should display processed packages in a column', async () => { - stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ).resolves( { stdout: '+latest' } ); - stubs.columns.returns( '1 | 2 | 3' ); + stubs.exec.mockResolvedValue( { stdout: '+latest' } ); + vi.mocked( columns ).mockReturnValue( '1 | 2 | 3' ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1', 'package2' ] } ); - expect( stubs.columns.callCount ).to.equal( 1 ); - expect( stubs.columns.firstCall.args[ 0 ] ).to.be.an( 'array' ); - expect( stubs.columns.firstCall.args[ 0 ] ).to.include( 'package1' ); - expect( stubs.columns.firstCall.args[ 0 ] ).to.include( 'package2' ); - expect( stubs.console.log.callCount ).to.equal( 2 ); - expect( stubs.console.log.firstCall.args[ 0 ] ).to.equal( '✨ Tags updated:' ); - expect( stubs.console.log.secondCall.args[ 0 ] ).to.equal( '1 | 2 | 3' ); + expect( vi.mocked( columns ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( columns ) ).toHaveBeenCalledWith( [ 'package1', 'package2' ] ); + expect( vi.mocked( console ).log ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( '✨ Tags updated:' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( '1 | 2 | 3' ); } ); it( 'should display errors found during processing a package', async () => { - const npmDistTagAdd = stubs.exec.withArgs( sinon.match( 'npm dist-tag add' ) ); - npmDistTagAdd.throws( new Error( 'Npm error while updating tag.' ) ); + stubs.exec.mockRejectedValue( new Error( 'Npm error while updating tag.' ) ); await reassignNpmTags( { npmOwner: 'authorized-user', version: '1.0.1', packages: [ 'package1' ] } ); - expect( stubs.console.log.callCount ).to.equal( 2 ); - expect( stubs.console.log.firstCall.args[ 0 ] ).to.equal( '🐛 Errors found:' ); - expect( stubs.console.log.secondCall.args[ 0 ] ).to.equal( '* Npm error while updating tag.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( '🐛 Errors found:' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledWith( '* Npm error while updating tag.' ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/vitest.config.js b/packages/ckeditor5-dev-release-tools/vitest.config.js index 5ad784a28..0f187ad82 100644 --- a/packages/ckeditor5-dev-release-tools/vitest.config.js +++ b/packages/ckeditor5-dev-release-tools/vitest.config.js @@ -7,7 +7,11 @@ import { defineConfig } from 'vitest/config'; export default defineConfig( { test: { + setupFiles: [ + './tests/_utils/testsetup.js' + ], testTimeout: 10000, + mockReset: true, restoreMocks: true, include: [ 'tests/**/*.js' From 671f9a8ad569e5b7915a29c64b6ee17969e07fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Fri, 6 Sep 2024 11:51:54 +0200 Subject: [PATCH 015/172] Converted `ckeditor5-dev-docs` to ESM. --- packages/ckeditor5-dev-docs/lib/build.js | 10 +-- packages/ckeditor5-dev-docs/lib/buildjsdoc.js | 15 ++-- .../ckeditor5-dev-docs/lib/buildtypedoc.js | 14 ++- packages/ckeditor5-dev-docs/lib/index.js | 8 +- .../lib/validators/fires-validator/index.js | 10 +-- .../lib/validators/index.js | 16 ++-- .../lib/validators/link-validator/index.js | 10 +-- .../lib/validators/module-validator/index.js | 10 +-- .../validators/overloads-validator/index.js | 10 +-- .../lib/validators/see-validator/index.js | 10 +-- packages/ckeditor5-dev-docs/package.json | 11 +-- packages/ckeditor5-dev-docs/tests/utils.js | 6 +- .../tests/validators/fires-validator/index.js | 75 +++++++++------- .../tests/validators/link-validator/index.js | 79 ++++++++-------- .../validators/module-validator/index.js | 71 ++++++++------- .../validators/overloads-validator/index.js | 89 +++++++++++-------- .../tests/validators/see-validator/index.js | 72 ++++++++------- packages/ckeditor5-dev-docs/vitest.config.js | 23 +++++ 18 files changed, 292 insertions(+), 247 deletions(-) create mode 100644 packages/ckeditor5-dev-docs/vitest.config.js diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index 2249bd0d9..f7987baac 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -3,25 +3,23 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Builds CKEditor 5 documentation. * * @param {JSDocConfig|TypedocConfig} config * @returns {Promise} */ -module.exports = async function build( config ) { +export default async function build( config ) { const type = config.type || 'jsdoc'; if ( type === 'jsdoc' ) { - return require( './buildjsdoc' )( config ); + return import( './buildjsdoc' )( config ); } else if ( type === 'typedoc' ) { - return require( './buildtypedoc' )( config ); + return import( './buildtypedoc' )( config ); } else { throw new Error( `Unknown documentation tool (${ type }).` ); } -}; +} /** * @typedef {Object} JSDocConfig diff --git a/packages/ckeditor5-dev-docs/lib/buildjsdoc.js b/packages/ckeditor5-dev-docs/lib/buildjsdoc.js index 8c657751e..def803965 100644 --- a/packages/ckeditor5-dev-docs/lib/buildjsdoc.js +++ b/packages/ckeditor5-dev-docs/lib/buildjsdoc.js @@ -3,12 +3,13 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { createRequire } from 'module'; +import fs from 'fs-extra'; +import tmp from 'tmp'; +import glob from 'fast-glob'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; -const fs = require( 'fs-extra' ); -const tmp = require( 'tmp' ); -const glob = require( 'fast-glob' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +const require = createRequire( import.meta.url ); /** * Builds CKEditor 5 documentation using `jsdoc`. @@ -16,7 +17,7 @@ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); * @param {JSDocConfig} config * @returns {Promise} */ -module.exports = async function build( config ) { +export default async function build( config ) { const sourceFilePatterns = [ config.readmePath, ...config.sourceFiles @@ -90,4 +91,4 @@ module.exports = async function build( config ) { } console.log( `Documented ${ files.length } files!` ); -}; +} diff --git a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js b/packages/ckeditor5-dev-docs/lib/buildtypedoc.js index e830979e6..33f25fc88 100644 --- a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js +++ b/packages/ckeditor5-dev-docs/lib/buildtypedoc.js @@ -3,13 +3,11 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import glob from 'fast-glob'; +import TypeDoc from 'typedoc'; +import { plugins } from '@ckeditor/typedoc-plugins'; -const glob = require( 'fast-glob' ); -const TypeDoc = require( 'typedoc' ); -const { plugins } = require( '@ckeditor/typedoc-plugins' ); - -const validators = require( './validators' ); +import validators from './validators'; /** * Builds CKEditor 5 documentation using `typedoc`. @@ -17,7 +15,7 @@ const validators = require( './validators' ); * @param {TypedocConfig} config * @returns {Promise} */ -module.exports = async function build( config ) { +export default async function build( config ) { const sourceFilePatterns = config.sourceFiles.filter( Boolean ); const strictMode = config.strict || false; const extraPlugins = config.extraPlugins || []; @@ -89,4 +87,4 @@ module.exports = async function build( config ) { } console.log( `Documented ${ files.length } files!` ); -}; +} diff --git a/packages/ckeditor5-dev-docs/lib/index.js b/packages/ckeditor5-dev-docs/lib/index.js index abe4f686e..17abd1091 100644 --- a/packages/ckeditor5-dev-docs/lib/index.js +++ b/packages/ckeditor5-dev-docs/lib/index.js @@ -3,10 +3,4 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const build = require( './build' ); - -module.exports = { - build -}; +export { default as build } from './build'; diff --git a/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js index 125eb1e46..2c55b2528 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { ReflectionKind } = require( 'typedoc' ); -const { utils } = require( '@ckeditor/typedoc-plugins' ); +import { ReflectionKind } from 'typedoc'; +import { utils } from '@ckeditor/typedoc-plugins'; /** * Validates the output produced by TypeDoc. @@ -16,7 +14,7 @@ const { utils } = require( '@ckeditor/typedoc-plugins' ); * @param {Object} project Generated output from TypeDoc to validate. * @param {Function} onError A callback that is executed when a validation error is detected. */ -module.exports = function validate( project, onError ) { +export default function validate( project, onError ) { const reflections = project .getReflectionsByKind( ReflectionKind.Class | ReflectionKind.CallSignature ) .filter( utils.isReflectionValid ); @@ -38,7 +36,7 @@ module.exports = function validate( project, onError ) { } } } -}; +} function getIdentifiersFromFiresTag( reflection ) { if ( !reflection.comment ) { diff --git a/packages/ckeditor5-dev-docs/lib/validators/index.js b/packages/ckeditor5-dev-docs/lib/validators/index.js index 315313e9a..0e5f76b02 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/index.js @@ -3,14 +3,12 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { utils } = require( '@ckeditor/typedoc-plugins' ); -const seeValidator = require( './see-validator' ); -const linkValidator = require( './link-validator' ); -const firesValidator = require( './fires-validator' ); -const moduleValidator = require( './module-validator' ); -const overloadsValidator = require( './overloads-validator' ); +import { utils } from '@ckeditor/typedoc-plugins'; +import seeValidator from './see-validator'; +import linkValidator from './link-validator'; +import firesValidator from './fires-validator'; +import moduleValidator from './module-validator'; +import overloadsValidator from './overloads-validator'; /** * Validates the CKEditor 5 documentation. @@ -20,7 +18,7 @@ const overloadsValidator = require( './overloads-validator' ); * @param {TypedocValidator} [options={}] A configuration object. * @returns {Boolean} */ -module.exports = { +export default { validate( project, typeDoc, options = {} ) { const validators = [ seeValidator, diff --git a/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js index 0c7dd4692..02d71a74f 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { ReflectionKind } = require( 'typedoc' ); -const { utils } = require( '@ckeditor/typedoc-plugins' ); +import { ReflectionKind } from 'typedoc'; +import { utils } from '@ckeditor/typedoc-plugins'; /** * Validates the output produced by TypeDoc. @@ -16,7 +14,7 @@ const { utils } = require( '@ckeditor/typedoc-plugins' ); * @param {Object} project Generated output from TypeDoc to validate. * @param {Function} onError A callback that is executed when a validation error is detected. */ -module.exports = function validate( project, onError ) { +export default function validate( project, onError ) { const reflections = project.getReflectionsByKind( ReflectionKind.All ).filter( utils.isReflectionValid ); for ( const reflection of reflections ) { @@ -34,7 +32,7 @@ module.exports = function validate( project, onError ) { } } } -}; +} function getIdentifiersFromLinkTag( reflection ) { if ( !reflection.comment ) { diff --git a/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js index 4f13f1cc4..28586eca5 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { ReflectionKind } = require( 'typedoc' ); -const { utils } = require( '@ckeditor/typedoc-plugins' ); +import { ReflectionKind } from 'typedoc'; +import { utils } from '@ckeditor/typedoc-plugins'; const AUGMENTATION_MODULE_REGEXP = /[^\\/]+[\\/]src[\\/]augmentation/; @@ -18,7 +16,7 @@ const AUGMENTATION_MODULE_REGEXP = /[^\\/]+[\\/]src[\\/]augmentation/; * @param {Object} project Generated output from TypeDoc to validate. * @param {Function} onError A callback that is executed when a validation error is detected. */ -module.exports = function validate( project, onError ) { +export default function validate( project, onError ) { const reflections = project.getReflectionsByKind( ReflectionKind.Module ); for ( const reflection of reflections ) { @@ -53,4 +51,4 @@ module.exports = function validate( project, onError ) { onError( `Invalid module name: "${ reflection.name }"`, reflection ); } } -}; +} diff --git a/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js index d4c00537e..51949f619 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { ReflectionKind } = require( 'typedoc' ); -const { utils } = require( '@ckeditor/typedoc-plugins' ); +import { ReflectionKind } from 'typedoc'; +import { utils } from '@ckeditor/typedoc-plugins'; // The `@label` validator is currently not used. // See: https://github.com/ckeditor/ckeditor5/issues/13591. @@ -21,7 +19,7 @@ const { utils } = require( '@ckeditor/typedoc-plugins' ); * @param {Object} project Generated output from TypeDoc to validate. * @param {Function} onError A callback that is executed when a validation error is detected. */ -module.exports = function validate( project, onError ) { +export default function validate( project, onError ) { const kinds = ReflectionKind.Method | ReflectionKind.Constructor | ReflectionKind.Function; const reflections = project.getReflectionsByKind( kinds ).filter( utils.isReflectionValid ); @@ -52,4 +50,4 @@ module.exports = function validate( project, onError ) { } } } -}; +} diff --git a/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js index a86661ab7..fcc9164e3 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { ReflectionKind } = require( 'typedoc' ); -const { utils } = require( '@ckeditor/typedoc-plugins' ); +import { ReflectionKind } from 'typedoc'; +import { utils } from '@ckeditor/typedoc-plugins'; /** * Validates the output produced by TypeDoc. @@ -16,7 +14,7 @@ const { utils } = require( '@ckeditor/typedoc-plugins' ); * @param {Object} project Generated output from TypeDoc to validate. * @param {Function} onError A callback that is executed when a validation error is detected. */ -module.exports = function validate( project, onError ) { +export default function validate( project, onError ) { const reflections = project.getReflectionsByKind( ReflectionKind.All ).filter( utils.isReflectionValid ); for ( const reflection of reflections ) { @@ -34,7 +32,7 @@ module.exports = function validate( project, onError ) { } } } -}; +} function getIdentifiersFromSeeTag( reflection ) { if ( !reflection.comment ) { diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index 781c67278..c8cf36faf 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -16,6 +16,7 @@ "node": ">=18.0.0", "npm": ">=5.7.1" }, + "type": "module", "main": "lib/index.js", "files": [ "lib" @@ -32,15 +33,11 @@ "typedoc-plugin-rename-defaults": "0.6.6" }, "devDependencies": { - "chai": "^4.2.0", - "mocha": "^7.1.2", - "proxyquire": "^2.1.3", - "sinon": "^9.2.4", - "sinon-chai": "^3.7.0" + "vitest": "^2.0.5" }, "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.js --coverage" }, "depcheckIgnore": [ "typedoc-plugin-rename-defaults" diff --git a/packages/ckeditor5-dev-docs/tests/utils.js b/packages/ckeditor5-dev-docs/tests/utils.js index 537be756f..02563b3b5 100644 --- a/packages/ckeditor5-dev-docs/tests/utils.js +++ b/packages/ckeditor5-dev-docs/tests/utils.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Replaces Windows style paths to Unix. * @@ -18,7 +16,7 @@ function normalizePath( ...value ) { /** * Returns the source file path with line number from a reflection. * - * @param {require('typedoc').Reflection} reflection + * @param {import('typedoc').Reflection} reflection * @returns {String} */ function getSource( reflection ) { @@ -31,7 +29,7 @@ function getSource( reflection ) { return getSource( reflection.parent ); } -module.exports = { +export default { normalizePath, getSource }; diff --git a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js index 17f3428b1..960192a8b 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js @@ -3,37 +3,37 @@ * For licensing, see LICENSE.md. */ -const chai = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const testUtils = require( '../../utils' ); +import { describe, it, expect, vi } from 'vitest'; +import testUtils from '../../utils'; -const { expect } = chai; -chai.use( require( 'sinon-chai' ) ); +import build from '../../../lib/buildtypedoc'; -describe( 'dev-docs/validators/fires-validator', function() { - this.timeout( 10 * 1000 ); - - const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); - const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); - const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); +const stubs = vi.hoisted( () => { + return { + onErrorCallback: vi.fn() + }; +} ); - const onErrorCallback = sinon.stub(); +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} ); - before( async () => { - const validators = proxyquire( '../../../lib/validators', { - './fires-validator': project => { - return require( '../../../lib/validators/fires-validator' )( project, onErrorCallback ); - }, - './module-validator': sinon.spy() - } ); +vi.mock( '../../../lib/validators/fires-validator', async () => { + const { default: validator } = await vi.importActual( '../../../lib/validators/fires-validator' ); - const build = proxyquire( '../../../lib/buildtypedoc', { - './validators': validators - } ); + return { + default: project => validator( project, ( ...args ) => stubs.onErrorCallback( ...args ) ) + }; +} ); - const logStub = sinon.stub( console, 'log' ); +describe( 'dev-docs/validators/fires-validator', function() { + const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); + const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); + const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); + it( 'should warn if fired event does not exist', async () => { await build( { type: 'typedoc', cwd: FIXTURES_PATH, @@ -42,10 +42,6 @@ describe( 'dev-docs/validators/fires-validator', function() { strict: false } ); - logStub.restore(); - } ); - - it( 'should warn if fired event does not exist', () => { const expectedErrors = [ { identifier: 'event-non-existing', @@ -81,13 +77,24 @@ describe( 'dev-docs/validators/fires-validator', function() { } ]; - expect( onErrorCallback.callCount ).to.equal( expectedErrors.length ); + expect( stubs.onErrorCallback ).toHaveBeenCalledTimes( expectedErrors.length ); + + for ( const call of stubs.onErrorCallback.mock.calls ) { + expect( call ).toSatisfy( call => { + const [ message, reflection ] = call; + + return expectedErrors.some( error => { + if ( message !== `Incorrect event name: "${ error.identifier }" in the @fires tag` ) { + return false; + } + + if ( testUtils.getSource( reflection ) !== error.source ) { + return false; + } - for ( const error of expectedErrors ) { - expect( onErrorCallback ).to.be.calledWith( - `Incorrect event name: "${ error.identifier }" in the @fires tag`, - sinon.match( reflection => error.source === testUtils.getSource( reflection ) ) - ); + return true; + } ); + } ); } } ); } ); diff --git a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js index b9b112804..cf594d4dd 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js @@ -3,42 +3,36 @@ * For licensing, see LICENSE.md. */ -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const testUtils = require( '../../utils' ); +import { describe, it, expect, vi } from 'vitest'; +import testUtils from '../../utils'; -describe( 'dev-docs/validators/link-validator', function() { - this.timeout( 10 * 1000 ); - - const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); - const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '*.ts' ); - const DERIVED_FILE = testUtils.normalizePath( FIXTURES_PATH, 'inheritance', 'derivedclass.ts' ); - const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); +import build from '../../../lib/buildtypedoc'; - let build, logStub, warnStub, onErrorCallback; +const stubs = vi.hoisted( () => { + return { + onErrorCallback: vi.fn() + }; +} ); - beforeEach( async () => { - const validators = proxyquire( '../../../lib/validators', { - './link-validator': project => { - return require( '../../../lib/validators/link-validator' )( project, onErrorCallback ); - }, - './module-validator': sinon.spy() - } ); +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} ); - build = proxyquire( '../../../lib/buildtypedoc', { - './validators': validators - } ); +vi.mock( '../../../lib/validators/link-validator', async () => { + const { default: validator } = await vi.importActual( '../../../lib/validators/link-validator' ); - logStub = sinon.stub( console, 'log' ); - warnStub = sinon.stub( console, 'warn' ); - onErrorCallback = sinon.stub(); - } ); + return { + default: project => validator( project, ( ...args ) => stubs.onErrorCallback( ...args ) ) + }; +} ); - afterEach( () => { - logStub.restore(); - warnStub.restore(); - } ); +describe( 'dev-docs/validators/link-validator', function() { + const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); + const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '*.ts' ); + const DERIVED_FILE = testUtils.normalizePath( FIXTURES_PATH, 'inheritance', 'derivedclass.ts' ); + const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); it( 'should warn if link is not valid', async () => { await build( { @@ -112,13 +106,24 @@ describe( 'dev-docs/validators/link-validator', function() { } ]; - expect( onErrorCallback.callCount ).to.equal( expectedErrors.length ); + expect( stubs.onErrorCallback ).toHaveBeenCalledTimes( expectedErrors.length ); + + for ( const call of stubs.onErrorCallback.mock.calls ) { + expect( call ).toSatisfy( call => { + const [ message, reflection ] = call; + + return expectedErrors.some( error => { + if ( message !== `Incorrect link: "${ error.identifier }"` ) { + return false; + } + + if ( testUtils.getSource( reflection ) !== error.source ) { + return false; + } - for ( const error of expectedErrors ) { - expect( onErrorCallback ).to.be.calledWith( - `Incorrect link: "${ error.identifier }"`, - sinon.match( reflection => error.source === testUtils.getSource( reflection ) ) - ); + return true; + } ); + } ); } } ); @@ -131,6 +136,6 @@ describe( 'dev-docs/validators/link-validator', function() { strict: false } ); - expect( onErrorCallback.callCount ).to.equal( 0 ); + expect( stubs.onErrorCallback ).toHaveBeenCalledTimes( 0 ); } ); } ); diff --git a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js index 28d864a6a..23c15cd37 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js @@ -3,33 +3,37 @@ * For licensing, see LICENSE.md. */ -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const testUtils = require( '../../utils' ); +import { describe, it, expect, vi } from 'vitest'; +import testUtils from '../../utils'; -describe( 'dev-docs/validators/module-validator', function() { - this.timeout( 10 * 1000 ); +import build from '../../../lib/buildtypedoc'; - const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); - const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); - const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); +const stubs = vi.hoisted( () => { + return { + onErrorCallback: vi.fn() + }; +} ); - const onErrorCallback = sinon.stub(); +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} ); - before( async () => { - const validators = proxyquire( '../../../lib/validators', { - './module-validator': project => { - return require( '../../../lib/validators/module-validator' )( project, onErrorCallback ); - } - } ); +vi.mock( '../../../lib/validators/module-validator', async () => { + const { default: validator } = await vi.importActual( '../../../lib/validators/module-validator' ); - const build = proxyquire( '../../../lib/buildtypedoc', { - './validators': validators - } ); + return { + default: project => validator( project, ( ...args ) => stubs.onErrorCallback( ...args ) ) + }; +} ); - const logStub = sinon.stub( console, 'log' ); +describe( 'dev-docs/validators/module-validator', function() { + const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); + const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); + const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); + it( 'should warn if module name is not valid', async () => { await build( { type: 'typedoc', cwd: FIXTURES_PATH, @@ -38,10 +42,6 @@ describe( 'dev-docs/validators/module-validator', function() { strict: false } ); - logStub.restore(); - } ); - - it( 'should warn if module name is not valid', () => { const expectedErrors = [ { source: 'ckeditor5-example/src/modulerootinvalid1.ts:10', @@ -69,13 +69,24 @@ describe( 'dev-docs/validators/module-validator', function() { } ]; - expect( onErrorCallback.callCount ).to.equal( expectedErrors.length ); + expect( stubs.onErrorCallback ).toHaveBeenCalledTimes( expectedErrors.length ); + + for ( const call of stubs.onErrorCallback.mock.calls ) { + expect( call ).toSatisfy( call => { + const [ message, reflection ] = call; + + return expectedErrors.some( error => { + if ( message !== `Invalid module name: "${ error.name }"` ) { + return false; + } + + if ( testUtils.getSource( reflection ) !== error.source ) { + return false; + } - for ( const error of expectedErrors ) { - expect( onErrorCallback ).to.be.calledWith( - `Invalid module name: "${ error.name }"`, - sinon.match( reflection => error.source === testUtils.getSource( reflection ) ) - ); + return true; + } ); + } ); } } ); } ); diff --git a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js index b327bc28f..176bfacd0 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js @@ -3,34 +3,37 @@ * For licensing, see LICENSE.md. */ -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const testUtils = require( '../../utils' ); +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import testUtils from '../../utils'; -describe( 'dev-docs/validators/overloads-validator', function() { - this.timeout( 10 * 1000 ); +import build from '../../../lib/buildtypedoc'; - const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); - const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); - const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); +const stubs = vi.hoisted( () => { + return { + onErrorCallback: vi.fn() + }; +} ); - const onErrorCallback = sinon.stub(); +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} ); - before( async () => { - const validators = proxyquire( '../../../lib/validators', { - './overloads-validator': project => { - return require( '../../../lib/validators/overloads-validator' )( project, onErrorCallback ); - }, - './module-validator': sinon.spy() - } ); +vi.mock( '../../../lib/validators/overloads-validator', async () => { + const { default: validator } = await vi.importActual( '../../../lib/validators/overloads-validator' ); - const build = proxyquire( '../../../lib/buildtypedoc', { - './validators': validators - } ); + return { + default: project => validator( project, ( ...args ) => stubs.onErrorCallback( ...args ) ) + }; +} ); - const logStub = sinon.stub( console, 'log' ); +describe( 'dev-docs/validators/overloads-validator', function() { + const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); + const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); + const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); + beforeEach( async () => { await build( { type: 'typedoc', cwd: FIXTURES_PATH, @@ -41,8 +44,6 @@ describe( 'dev-docs/validators/overloads-validator', function() { enableOverloadValidator: true } } ); - - logStub.restore(); } ); it( 'should warn if overloaded signature does not have "@label" tag', () => { @@ -53,17 +54,21 @@ describe( 'dev-docs/validators/overloads-validator', function() { { source: 'overloadsinvalid.ts:24' } ]; - const errorCalls = onErrorCallback.getCalls().filter( call => { - return call.args[ 0 ] === 'Overloaded signature misses the @label tag'; + const errorCalls = stubs.onErrorCallback.mock.calls.filter( ( [ message ] ) => { + return message === 'Overloaded signature misses the @label tag'; } ); expect( errorCalls.length ).to.equal( expectedErrors.length ); - expectedErrors.forEach( ( { source }, index ) => { - const currentValue = testUtils.getSource( errorCalls[ index ].args[ 1 ] ); + for ( const call of errorCalls ) { + expect( call ).toSatisfy( call => { + const [ , reflection ] = call; - expect( currentValue ).to.equal( source ); - } ); + return expectedErrors.some( error => { + return testUtils.getSource( reflection ) === error.source; + } ); + } ); + } } ); it( 'should warn if overloaded signatures use the same identifier', () => { @@ -71,18 +76,28 @@ describe( 'dev-docs/validators/overloads-validator', function() { { source: 'overloadsinvalid.ts:51', error: 'Duplicated name: "NOT_SO_UNIQUE" in the @label tag' } ]; - const errorCalls = onErrorCallback.getCalls().filter( call => { - return call.args[ 0 ].startsWith( 'Duplicated name' ); + const errorCalls = stubs.onErrorCallback.mock.calls.filter( ( [ message ] ) => { + return message.startsWith( 'Duplicated name' ); } ); expect( errorCalls.length ).to.equal( expectedErrors.length ); - expectedErrors.forEach( ( { source, error }, index ) => { - const [ message, reflection ] = errorCalls[ index ].args; - const currentValue = testUtils.getSource( reflection ); + for ( const call of errorCalls ) { + expect( call ).toSatisfy( call => { + const [ message, reflection ] = call; - expect( message ).to.equal( error ); - expect( currentValue ).to.equal( source ); - } ); + return expectedErrors.some( ( { source, error } ) => { + if ( message !== error ) { + return false; + } + + if ( testUtils.getSource( reflection ) !== source ) { + return false; + } + + return true; + } ); + } ); + } } ); } ); diff --git a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js index e6ae545b3..352847267 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js @@ -3,34 +3,37 @@ * For licensing, see LICENSE.md. */ -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const testUtils = require( '../../utils' ); +import { describe, it, expect, vi } from 'vitest'; +import testUtils from '../../utils'; -describe( 'dev-docs/validators/see-validator', function() { - this.timeout( 10 * 1000 ); +import build from '../../../lib/buildtypedoc'; - const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); - const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); - const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); +const stubs = vi.hoisted( () => { + return { + onErrorCallback: vi.fn() + }; +} ); - const onErrorCallback = sinon.stub(); +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} ); - before( async () => { - const validators = proxyquire( '../../../lib/validators', { - './see-validator': project => { - return require( '../../../lib/validators/see-validator' )( project, onErrorCallback ); - }, - './module-validator': sinon.spy() - } ); +vi.mock( '../../../lib/validators/see-validator', async () => { + const { default: validator } = await vi.importActual( '../../../lib/validators/see-validator' ); - const build = proxyquire( '../../../lib/buildtypedoc', { - './validators': validators - } ); + return { + default: project => validator( project, ( ...args ) => stubs.onErrorCallback( ...args ) ) + }; +} ); - const logStub = sinon.stub( console, 'log' ); +describe( 'dev-docs/validators/see-validator', function() { + const FIXTURES_PATH = testUtils.normalizePath( __dirname, 'fixtures' ); + const SOURCE_FILES = testUtils.normalizePath( FIXTURES_PATH, '**', '*.ts' ); + const TSCONFIG_PATH = testUtils.normalizePath( FIXTURES_PATH, 'tsconfig.json' ); + it( 'should warn if link is not valid', async () => { await build( { type: 'typedoc', cwd: FIXTURES_PATH, @@ -39,10 +42,6 @@ describe( 'dev-docs/validators/see-validator', function() { strict: false } ); - logStub.restore(); - } ); - - it( 'should warn if link is not valid', () => { const expectedErrors = [ { identifier: '.property', @@ -98,13 +97,24 @@ describe( 'dev-docs/validators/see-validator', function() { } ]; - expect( onErrorCallback.callCount ).to.equal( expectedErrors.length ); + expect( stubs.onErrorCallback ).toHaveBeenCalledTimes( expectedErrors.length ); + + for ( const call of stubs.onErrorCallback.mock.calls ) { + expect( call ).toSatisfy( call => { + const [ message, reflection ] = call; + + return expectedErrors.some( error => { + if ( message !== `Incorrect link: "${ error.identifier }"` ) { + return false; + } + + if ( testUtils.getSource( reflection ) !== error.source ) { + return false; + } - for ( const error of expectedErrors ) { - expect( onErrorCallback ).to.be.calledWith( - `Incorrect link: "${ error.identifier }"`, - sinon.match( reflection => error.source === testUtils.getSource( reflection ) ) - ); + return true; + } ); + } ); } } ); } ); diff --git a/packages/ckeditor5-dev-docs/vitest.config.js b/packages/ckeditor5-dev-docs/vitest.config.js new file mode 100644 index 000000000..5ad784a28 --- /dev/null +++ b/packages/ckeditor5-dev-docs/vitest.config.js @@ -0,0 +1,23 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + testTimeout: 10000, + restoreMocks: true, + include: [ + 'tests/**/*.js' + ], + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From edde7633a3f6fd84fa6ba87fa72e2f1acb597a90 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 6 Sep 2024 11:52:33 +0200 Subject: [PATCH 016/172] More tests. --- .../tasks/verifypackagespublishedcorrectly.js | 2 +- .../tests/tasks/updatedependencies.js | 536 +++++++++--------- .../tests/tasks/updateversions.js | 359 ++++++------ .../tasks/verifypackagespublishedcorrectly.js | 222 +++----- 4 files changed, 545 insertions(+), 574 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js index 1ae3d850a..80eebf31b 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js @@ -6,7 +6,7 @@ import upath from 'upath'; import { glob } from 'glob'; import fs from 'fs-extra'; -import { checkVersionAvailability } from '../utils/checkversionavailability'; +import checkVersionAvailability from '../utils/checkversionavailability'; /** * Npm sometimes throws incorrect error 409 while publishing, while the package uploads correctly. diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js b/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js index 88ccccabb..aba41c0c2 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js @@ -3,338 +3,358 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const upath = require( 'upath' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); - -describe( 'dev-release-tools/tasks', () => { - describe( 'updateDependencies()', () => { - let updateDependencies, sandbox, stubs; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import upath from 'upath'; +import updateDependencies from '../../lib/tasks/updatedependencies'; + +vi.mock( 'fs-extra' ); +vi.mock( 'glob' ); + +describe( 'updateDependencies()', () => { + beforeEach( () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/project' ); + } ); + describe( 'preparing options', () => { beforeEach( () => { - sandbox = sinon.createSandbox(); + vi.mocked( glob ).mockResolvedValue( [] ); + } ); - stubs = { - fs: { - readJson: sandbox.stub(), - writeJson: sandbox.stub() - }, - glob: { - glob: sandbox.stub() - }, - process: { - cwd: sandbox.stub( process, 'cwd' ).returns( '/work/project' ) - } + it( 'should use provided `cwd` to search for packages', async () => { + const options = { + cwd: '/work/another/project' }; - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( 'glob', stubs.glob ); + await updateDependencies( options ); - updateDependencies = require( '../../lib/tasks/updatedependencies' ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'package.json' ], + expect.objectContaining( { + cwd: '/work/another/project' + } ) + ); } ); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); - } ); + it( 'should use `process.cwd()` to search for packages if `cwd` option is not provided', async () => { + await updateDependencies( {} ); - describe( 'preparing options', () => { - beforeEach( () => { - stubs.glob.glob.resolves( [] ); - } ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'package.json' ], + expect.objectContaining( { + cwd: '/work/project' + } ) + ); + } ); - it( 'should use provided `cwd` to search for packages', async () => { - const options = { - cwd: '/work/another/project' - }; + it( 'should match only files', async () => { + await updateDependencies( {} ); - await updateDependencies( options ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + expect.any( Array ), + expect.objectContaining( { + nodir: true + } ) + ); + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'cwd', '/work/another/project' ); - } ); + it( 'should always receive absolute paths for matched files', async () => { + await updateDependencies( {} ); - it( 'should use `process.cwd()` to search for packages if `cwd` option is not provided', async () => { - await updateDependencies( {} ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + expect.any( Array ), + expect.objectContaining( { + absolute: true + } ) + ); + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'cwd', '/work/project' ); + it( 'should use the `packagesDirectory` option for searching for packages in `cwd`', () => { + updateDependencies( { + packagesDirectory: 'packages' } ); - it( 'should match only files', async () => { - await updateDependencies( {} ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ + 'package.json', + 'packages/*/package.json' + ], + expect.any( Object ) + ); + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'nodir', true ); - } ); + it( 'should not search for packages if the `packagesDirectory` option is not provided', async () => { + await updateDependencies( {} ); - it( 'should always receive absolute paths for matched files', async () => { - await updateDependencies( {} ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'package.json' ], + expect.any( Object ) + ); + } ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 1 ] ).to.have.property( 'absolute', true ); + it( 'should convert backslashes to slashes from the `packagesDirectory` (Windows-like paths)', async () => { + await updateDependencies( { + packagesDirectory: 'path\\to\\packages\\' } ); - it( 'should search for packages in `cwd` and `packagesDirectory`', () => { - updateDependencies( { - packagesDirectory: 'packages' - } ); - - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 0 ] ).to.deep.equal( [ + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'package.json', - 'packages/*/package.json' - ] ); - } ); + 'path/to/packages/*/package.json' + ], + expect.any( Object ) + ); + } ); + } ); - it( 'should search for packages only in `cwd` if `packagesDirectory` option is not provided', async () => { - await updateDependencies( {} ); + describe( 'updating dependencies', () => { + let shouldUpdateVersionCallback; - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 0 ] ).to.deep.equal( [ - 'package.json' - ] ); + beforeEach( () => { + shouldUpdateVersionCallback = vi.fn( packageName => packageName.startsWith( '@ckeditor' ) ); + } ); + + it( 'should read and write `package.json` for each found package', async () => { + vi.mocked( glob ).mockImplementation( patterns => { + const paths = { + 'package.json': [ + '/work/project/package.json' + ], + 'packages/*/package.json': [ + '/work/project/packages/ckeditor5-foo/package.json', + '/work/project/packages/ckeditor5-bar/package.json' + ] + }; + + return Promise.resolve( + patterns.flatMap( pattern => paths[ pattern ] || [] ) + ); } ); - it( 'should convert backslashes to slashes from the `packagesDirectory`', async () => { - await updateDependencies( { - packagesDirectory: 'path\\to\\packages\\' - } ); + vi.mocked( fs ).readJson.mockResolvedValue( {} ); - expect( stubs.glob.glob.calledOnce ).to.equal( true ); - expect( stubs.glob.glob.getCall( 0 ).args[ 0 ] ).to.deep.equal( [ - 'package.json', - 'path/to/packages/*/package.json' - ] ); + await updateDependencies( { + packagesDirectory: 'packages' } ); + + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( '/work/project/package.json' ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( '/work/project/packages/ckeditor5-foo/package.json' ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( '/work/project/packages/ckeditor5-bar/package.json' ); + + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/package.json', + expect.any( Object ), + expect.any( Object ) + ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/packages/ckeditor5-foo/package.json', + expect.any( Object ), + expect.any( Object ) + ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/packages/ckeditor5-bar/package.json', + expect.any( Object ), + expect.any( Object ) + ); } ); - describe( 'updating dependencies', () => { - let shouldUpdateVersionCallback; + it( 'should allow filtering out packages that do not pass the `packagesDirectoryFilter` callback', async () => { + vi.mocked( glob ).mockImplementation( patterns => { + const paths = { + 'package.json': [ + '/work/project/package.json' + ], + 'packages/*/package.json': [ + '/work/project/packages/ckeditor5-ignore-me/package.json', + '/work/project/packages/ckeditor5-bar/package.json' + ] + }; - beforeEach( () => { - shouldUpdateVersionCallback = sandbox.stub().callsFake( packageName => packageName.startsWith( '@ckeditor' ) ); + return Promise.resolve( + patterns.flatMap( pattern => paths[ pattern ] || [] ) + ); } ); - it( 'should read and write `package.json` for each found package', async () => { - stubs.glob.glob.callsFake( patterns => { - const paths = { - 'package.json': [ - '/work/project/package.json' - ], - 'packages/*/package.json': [ - '/work/project/packages/ckeditor5-foo/package.json', - '/work/project/packages/ckeditor5-bar/package.json' - ] - }; - - return Promise.resolve( - patterns.flatMap( pattern => paths[ pattern ] || [] ) - ); - } ); - - stubs.fs.readJson.resolves( {} ); - - await updateDependencies( { - packagesDirectory: 'packages' - } ); - - expect( stubs.fs.readJson.callCount ).to.equal( 3 ); - expect( stubs.fs.readJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.readJson.getCall( 1 ).args[ 0 ] ).to.equal( '/work/project/packages/ckeditor5-foo/package.json' ); - expect( stubs.fs.readJson.getCall( 2 ).args[ 0 ] ).to.equal( '/work/project/packages/ckeditor5-bar/package.json' ); - - expect( stubs.fs.writeJson.callCount ).to.equal( 3 ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.writeJson.getCall( 1 ).args[ 0 ] ).to.equal( '/work/project/packages/ckeditor5-foo/package.json' ); - expect( stubs.fs.writeJson.getCall( 2 ).args[ 0 ] ).to.equal( '/work/project/packages/ckeditor5-bar/package.json' ); + vi.mocked( fs ).readJson.mockResolvedValue( {} ); + + const directoriesToSkip = [ + 'ckeditor5-ignore-me' + ]; + + await updateDependencies( { + version: '^38.0.0', + packagesDirectory: 'packages', + packagesDirectoryFilter: packageJsonPath => { + return !directoriesToSkip.some( item => { + return upath.dirname( packageJsonPath ).endsWith( item ); + } ); + } } ); - it( 'should allow filtering out packages that do not pass the `packagesDirectoryFilter` callback', async () => { - stubs.glob.glob.callsFake( patterns => { - const paths = { - 'package.json': [ - '/work/project/package.json' - ], - 'packages/*/package.json': [ - '/work/project/packages/ckeditor5-ignore-me/package.json', - '/work/project/packages/ckeditor5-bar/package.json' - ] - }; - - return Promise.resolve( - patterns.flatMap( pattern => paths[ pattern ] || [] ) - ); - } ); - - stubs.fs.readJson.resolves( {} ); - - const directoriesToSkip = [ - 'ckeditor5-ignore-me' - ]; - - await updateDependencies( { - version: '^38.0.0', - packagesDirectory: 'packages', - packagesDirectoryFilter: packageJsonPath => { - return !directoriesToSkip.some( item => { - return upath.dirname( packageJsonPath ).endsWith( item ); - } ); - } - } ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( '/work/project/package.json' ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( '/work/project/packages/ckeditor5-bar/package.json' ); + + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/package.json', + expect.any( Object ), + expect.any( Object ) + ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/packages/ckeditor5-bar/package.json', + expect.any( Object ), + expect.any( Object ) + ); + } ); - expect( stubs.fs.readJson.callCount ).to.equal( 2 ); - expect( stubs.fs.readJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.readJson.getCall( 1 ).args[ 0 ] ).to.equal( '/work/project/packages/ckeditor5-bar/package.json' ); + it( 'should update eligible dependencies from the `dependencies` key', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/work/project/package.json' ] ); - expect( stubs.fs.writeJson.callCount ).to.equal( 2 ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.writeJson.getCall( 1 ).args[ 0 ] ).to.equal( '/work/project/packages/ckeditor5-bar/package.json' ); + vi.mocked( fs ).readJson.mockResolvedValue( { + dependencies: { + '@ckeditor/ckeditor5-engine': '^37.0.0', + '@ckeditor/ckeditor5-enter': '^37.0.0', + '@ckeditor/ckeditor5-essentials': '^37.0.0', + 'lodash-es': '^4.17.15' + } } ); - it( 'should update eligible dependencies from the `dependencies` key', async () => { - stubs.glob.glob.resolves( [ '/work/project/package.json' ] ); + await updateDependencies( { + version: '^38.0.0', + shouldUpdateVersionCallback + } ); - stubs.fs.readJson.resolves( { - dependencies: { - '@ckeditor/ckeditor5-engine': '^37.0.0', - '@ckeditor/ckeditor5-enter': '^37.0.0', - '@ckeditor/ckeditor5-essentials': '^37.0.0', - 'lodash-es': '^4.17.15' - } - } ); - - await updateDependencies( { - version: '^38.0.0', - shouldUpdateVersionCallback - } ); - - expect( shouldUpdateVersionCallback.callCount ).to.equal( 4 ); - expect( shouldUpdateVersionCallback.getCall( 0 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-engine' ); - expect( shouldUpdateVersionCallback.getCall( 1 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-enter' ); - expect( shouldUpdateVersionCallback.getCall( 2 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-essentials' ); - expect( shouldUpdateVersionCallback.getCall( 3 ).args[ 0 ] ).to.equal( 'lodash-es' ); - - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 1 ] ).to.deep.equal( { + expect( shouldUpdateVersionCallback ).toHaveBeenCalledTimes( 4 ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-engine' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-enter' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-essentials' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( 'lodash-es' ); + + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/package.json', + { dependencies: { '@ckeditor/ckeditor5-engine': '^38.0.0', '@ckeditor/ckeditor5-enter': '^38.0.0', '@ckeditor/ckeditor5-essentials': '^38.0.0', 'lodash-es': '^4.17.15' } - } ); + }, + expect.any( Object ) + ); + } ); + + it( 'should update eligible dependencies from the `devDependencies` key', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/work/project/package.json' ] ); + + vi.mocked( fs ).readJson.mockResolvedValue( { + devDependencies: { + '@ckeditor/ckeditor5-engine': '^37.0.0', + '@ckeditor/ckeditor5-enter': '^37.0.0', + '@ckeditor/ckeditor5-essentials': '^37.0.0', + 'lodash-es': '^4.17.15' + } } ); - it( 'should update eligible dependencies from the `devDependencies` key', async () => { - stubs.glob.glob.resolves( [ '/work/project/package.json' ] ); + await updateDependencies( { + version: '^38.0.0', + shouldUpdateVersionCallback + } ); - stubs.fs.readJson.resolves( { - devDependencies: { - '@ckeditor/ckeditor5-engine': '^37.0.0', - '@ckeditor/ckeditor5-enter': '^37.0.0', - '@ckeditor/ckeditor5-essentials': '^37.0.0', - 'lodash-es': '^4.17.15' - } - } ); - - await updateDependencies( { - version: '^38.0.0', - shouldUpdateVersionCallback - } ); - - expect( shouldUpdateVersionCallback.callCount ).to.equal( 4 ); - expect( shouldUpdateVersionCallback.getCall( 0 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-engine' ); - expect( shouldUpdateVersionCallback.getCall( 1 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-enter' ); - expect( shouldUpdateVersionCallback.getCall( 2 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-essentials' ); - expect( shouldUpdateVersionCallback.getCall( 3 ).args[ 0 ] ).to.equal( 'lodash-es' ); - - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 1 ] ).to.deep.equal( { + expect( shouldUpdateVersionCallback ).toHaveBeenCalledTimes( 4 ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-engine' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-enter' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-essentials' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( 'lodash-es' ); + + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/package.json', + { devDependencies: { '@ckeditor/ckeditor5-engine': '^38.0.0', '@ckeditor/ckeditor5-enter': '^38.0.0', '@ckeditor/ckeditor5-essentials': '^38.0.0', 'lodash-es': '^4.17.15' } - } ); + }, + expect.any( Object ) + ); + } ); + + it( 'should update eligible dependencies from the `peerDependencies` key', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/work/project/package.json' ] ); + + vi.mocked( fs ).readJson.mockResolvedValue( { + peerDependencies: { + '@ckeditor/ckeditor5-engine': '^37.0.0', + '@ckeditor/ckeditor5-enter': '^37.0.0', + '@ckeditor/ckeditor5-essentials': '^37.0.0', + 'lodash-es': '^4.17.15' + } } ); - it( 'should update eligible dependencies from the `peerDependencies` key', async () => { - stubs.glob.glob.resolves( [ '/work/project/package.json' ] ); + await updateDependencies( { + version: '^38.0.0', + shouldUpdateVersionCallback + } ); - stubs.fs.readJson.resolves( { - peerDependencies: { - '@ckeditor/ckeditor5-engine': '^37.0.0', - '@ckeditor/ckeditor5-enter': '^37.0.0', - '@ckeditor/ckeditor5-essentials': '^37.0.0', - 'lodash-es': '^4.17.15' - } - } ); - - await updateDependencies( { - version: '^38.0.0', - shouldUpdateVersionCallback - } ); - - expect( shouldUpdateVersionCallback.callCount ).to.equal( 4 ); - expect( shouldUpdateVersionCallback.getCall( 0 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-engine' ); - expect( shouldUpdateVersionCallback.getCall( 1 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-enter' ); - expect( shouldUpdateVersionCallback.getCall( 2 ).args[ 0 ] ).to.equal( '@ckeditor/ckeditor5-essentials' ); - expect( shouldUpdateVersionCallback.getCall( 3 ).args[ 0 ] ).to.equal( 'lodash-es' ); - - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 1 ] ).to.deep.equal( { + expect( shouldUpdateVersionCallback ).toHaveBeenCalledTimes( 4 ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-engine' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-enter' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( '@ckeditor/ckeditor5-essentials' ); + expect( shouldUpdateVersionCallback ).toHaveBeenCalledWith( 'lodash-es' ); + + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/package.json', + { peerDependencies: { '@ckeditor/ckeditor5-engine': '^38.0.0', '@ckeditor/ckeditor5-enter': '^38.0.0', '@ckeditor/ckeditor5-essentials': '^38.0.0', 'lodash-es': '^4.17.15' } - } ); - } ); + }, + expect.any( Object ) + ); + } ); - it( 'should not update any package if `shouldUpdateVersionCallback` callback resolves falsy value', async () => { - stubs.glob.glob.resolves( [ '/work/project/package.json' ] ); + it( 'should not update any package if `shouldUpdateVersionCallback` callback resolves falsy value', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/work/project/package.json' ] ); - stubs.fs.readJson.resolves( { - dependencies: { - '@ckeditor/ckeditor5-engine': '^37.0.0', - '@ckeditor/ckeditor5-enter': '^37.0.0', - '@ckeditor/ckeditor5-essentials': '^37.0.0', - 'lodash-es': '^4.17.15' - } - } ); + vi.mocked( fs ).readJson.mockResolvedValue( { + dependencies: { + '@ckeditor/ckeditor5-engine': '^37.0.0', + '@ckeditor/ckeditor5-enter': '^37.0.0', + '@ckeditor/ckeditor5-essentials': '^37.0.0', + 'lodash-es': '^4.17.15' + } + } ); - await updateDependencies( { - version: '^38.0.0', - shouldUpdateVersionCallback: () => false - } ); + await updateDependencies( { + version: '^38.0.0', + shouldUpdateVersionCallback: () => false + } ); - expect( stubs.fs.writeJson.callCount ).to.equal( 1 ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 0 ] ).to.equal( '/work/project/package.json' ); - expect( stubs.fs.writeJson.getCall( 0 ).args[ 1 ] ).to.deep.equal( { + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/work/project/package.json', + { dependencies: { '@ckeditor/ckeditor5-engine': '^37.0.0', '@ckeditor/ckeditor5-enter': '^37.0.0', '@ckeditor/ckeditor5-essentials': '^37.0.0', 'lodash-es': '^4.17.15' } - } ); - } ); + }, + expect.any( Object ) + ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js b/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js index 58a52800d..bdd54508f 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js @@ -5,212 +5,213 @@ 'use strict'; -const upath = require( 'upath' ); -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); - -describe( 'dev-release-tools/release', () => { - let updateVersions, sandbox, stubs; - - describe( 'updateVersions()', () => { - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - outputJson: sandbox.stub(), - readJson: sandbox.stub().resolves( { version: '1.0.0' } ), - glob: sandbox.stub().resolves( [ '/ckeditor5-dev' ] ), - checkVersionAvailability: sandbox.stub().resolves( true ) - }; - - updateVersions = proxyquire( '../../lib/tasks/updateversions.js', { - 'fs-extra': { - writeJson: stubs.outputJson, - readJson: stubs.readJson - }, - 'glob': { glob: stubs.glob }, - '../utils/checkversionavailability': stubs.checkVersionAvailability - } ); - } ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { glob } from 'glob'; +import upath from 'upath'; +import fs from 'fs-extra'; +import checkVersionAvailability from '../../lib/utils/checkversionavailability'; +import updateVersions from '../../lib/tasks/updateversions.js'; + +vi.mock( 'fs-extra' ); +vi.mock( 'glob' ); +vi.mock( '../../lib/utils/checkversionavailability' ); + +describe( 'updateVersions()', () => { + beforeEach( () => { + vi.mocked( fs ).readJson.mockResolvedValue( { version: '1.0.0' } ); + vi.mocked( glob ).mockResolvedValue( [ '/ckeditor5-dev' ] ); + vi.mocked( checkVersionAvailability ).mockResolvedValue( true ); + vi.spyOn( process, 'cwd' ).mockReturnValue( '/ckeditor5-dev' ); + } ); - afterEach( () => { - sandbox.restore(); - } ); + it( 'should update the version field in all found packages including the root package', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/ckeditor5-dev/packages/package1/package.json', + '/ckeditor5-dev/packages/package2/package.json', + '/ckeditor5-dev/packages/package3/package.json', + '/ckeditor5-dev/package.json' + ] ); + + await updateVersions( { version: '1.0.1', packagesDirectory: 'packages' } ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'package.json', 'packages/*/package.json' ], + expect.any( Object ) + ); + + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledTimes( 4 ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/ckeditor5-dev/packages/package1/package.json', + { + version: '1.0.1' + }, + expect.any( Object ) + ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/ckeditor5-dev/packages/package2/package.json', + { + version: '1.0.1' + }, + expect.any( Object ) + ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/ckeditor5-dev/packages/package3/package.json', + { + version: '1.0.1' + }, + expect.any( Object ) + ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledWith( + '/ckeditor5-dev/package.json', + { + version: '1.0.1' + }, + expect.any( Object ) + ); + } ); - it( 'should update the version field in all found packages including the root package', async () => { - stubs.glob.resolves( [ - '/ckeditor5-dev/packages/package1/package.json', - '/ckeditor5-dev/packages/package2/package.json', - '/ckeditor5-dev/packages/package3/package.json', - '/ckeditor5-dev/package.json' - ] ); - - await updateVersions( { version: '1.0.1', packagesDirectory: 'packages' } ); - - expect( stubs.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'package.json', 'packages/*/package.json' ] ); - - expect( stubs.outputJson.callCount ).to.equal( 4 ); - expect( stubs.outputJson.getCall( 0 ).args[ 0 ] ).to.contain( '/ckeditor5-dev/packages/package1/package.json' ); - expect( stubs.outputJson.getCall( 0 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - expect( stubs.outputJson.getCall( 1 ).args[ 0 ] ).to.contain( '/ckeditor5-dev/packages/package2/package.json' ); - expect( stubs.outputJson.getCall( 1 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - expect( stubs.outputJson.getCall( 2 ).args[ 0 ] ).to.contain( '/ckeditor5-dev/packages/package3/package.json' ); - expect( stubs.outputJson.getCall( 2 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - expect( stubs.outputJson.getCall( 3 ).args[ 0 ] ).to.equal( '/ckeditor5-dev/package.json' ); - expect( stubs.outputJson.getCall( 3 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); + it( 'should allow filtering out packages that do not pass the `packagesDirectoryFilter` callback', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/ckeditor5-dev/packages/package1/package.json', + '/ckeditor5-dev/packages/package-bar/package.json', + '/ckeditor5-dev/packages/package-foo/package.json', + '/ckeditor5-dev/packages/package-number/package.json', + '/ckeditor5-dev/package.json' + ] ); + + const directoriesToSkip = [ + 'package-number' + ]; + + await updateVersions( { + version: '1.0.1', + packagesDirectory: 'packages', + packagesDirectoryFilter: packageJsonPath => { + return !directoriesToSkip.some( item => { + return upath.dirname( packageJsonPath ).endsWith( item ); + } ); + } } ); - it( 'should allow filtering out packages that do not pass the `packagesDirectoryFilter` callback', async () => { - stubs.glob.resolves( [ - '/ckeditor5-dev/packages/package1/package.json', - '/ckeditor5-dev/packages/package-bar/package.json', - '/ckeditor5-dev/packages/package-foo/package.json', - '/ckeditor5-dev/packages/package-number/package.json', - '/ckeditor5-dev/package.json' - ] ); - - const directoriesToSkip = [ - 'package-number' - ]; - - await updateVersions( { - version: '1.0.1', - packagesDirectory: 'packages', - packagesDirectoryFilter: packageJsonPath => { - return !directoriesToSkip.some( item => { - return upath.dirname( packageJsonPath ).endsWith( item ); - } ); - } - } ); - - expect( stubs.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'package.json', 'packages/*/package.json' ] ); - - expect( stubs.outputJson.callCount ).to.equal( 4 ); - expect( stubs.outputJson.getCall( 0 ).args[ 0 ] ).to.contain( '/ckeditor5-dev/packages/package1/package.json' ); - expect( stubs.outputJson.getCall( 0 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - expect( stubs.outputJson.getCall( 1 ).args[ 0 ] ).to.contain( '/ckeditor5-dev/packages/package-bar/package.json' ); - expect( stubs.outputJson.getCall( 1 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - expect( stubs.outputJson.getCall( 2 ).args[ 0 ] ).to.contain( '/ckeditor5-dev/packages/package-foo/package.json' ); - expect( stubs.outputJson.getCall( 2 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - expect( stubs.outputJson.getCall( 3 ).args[ 0 ] ).to.equal( '/ckeditor5-dev/package.json' ); - expect( stubs.outputJson.getCall( 3 ).args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - } ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'package.json', 'packages/*/package.json' ], + expect.any( Object ) + ); + + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalled(); + expect( vi.mocked( fs ).writeJson ).not.toHaveBeenCalledWith( + '/ckeditor5-dev/packages/package-number/package.json', + { + version: '1.0.1' + }, + expect.any( Object ) + ); + } ); - it( 'should update the version field in the root package when `packagesDirectory` is not provided', async () => { - stubs.glob.resolves( [ '/ckeditor5-dev' ] ); + it( 'should update the version field in the root package when `packagesDirectory` is not provided', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/ckeditor5-dev/package.json' ] ); - await updateVersions( { version: '1.0.1' } ); + await updateVersions( { version: '1.0.1' } ); - expect( stubs.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'package.json' ] ); + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'package.json' ], + expect.any( Object ) + ); - expect( stubs.outputJson.callCount ).to.equal( 1 ); - expect( stubs.outputJson.firstCall.args[ 0 ] ).to.contain( '/ckeditor5-dev' ); - expect( stubs.outputJson.firstCall.args[ 1 ] ).to.deep.equal( { version: '1.0.1' } ); - } ); + expect( vi.mocked( fs ).writeJson ).toHaveBeenCalledExactlyOnceWith( + '/ckeditor5-dev/package.json', + { + version: '1.0.1' + }, + expect.any( Object ) + ); + } ); - it( 'should throw an error when the version is already in use', async () => { - stubs.readJson.resolves( { version: '1.0.0', name: 'stub-package' } ); - stubs.checkVersionAvailability.resolves( false ); + it( 'should throw an error when the version is already in use', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { version: '1.0.0', name: 'stub-package' } ); + vi.mocked( checkVersionAvailability ).mockResolvedValue( false ); - try { - await updateVersions( { version: '1.0.1' } ); - throw new Error( 'Expected to throw.' ); - } catch ( err ) { - expect( err.message ).to.equal( 'The "stub-package@1.0.1" already exists in the npm registry.' ); - } - } ); + await expect( updateVersions( { version: '1.0.1' } ) ) + .rejects.toThrow( 'The "stub-package@1.0.1" already exists in the npm registry.' ); + } ); - it( 'should not throw an error when version is not in use', async () => { - stubs.readJson.resolves( { version: '1.0.0', name: 'stub-package' } ); - stubs.checkVersionAvailability.resolves( true ); + it( 'should not throw an error when version is not in use', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { version: '1.0.0', name: 'stub-package' } ); + vi.mocked( checkVersionAvailability ).mockResolvedValue( true ); - try { - await updateVersions( { version: '1.0.1' } ); - } catch ( err ) { - throw new Error( 'Expected not to throw.' ); - } - } ); + await expect( updateVersions( { version: '1.0.1' } ) ).resolves.toBeNil(); + } ); - it( 'should throw an error when it was not possible to check the version availability', async () => { - stubs.readJson.resolves( { version: '1.0.0', name: 'stub-package' } ); - stubs.checkVersionAvailability.rejects( new Error( 'Custom error.' ) ); + it( 'should throw an error when it was not possible to check the version availability', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { version: '1.0.0', name: 'stub-package' } ); + vi.mocked( checkVersionAvailability ).mockRejectedValue( new Error( 'Custom error.' ) ); - try { - await updateVersions( { version: '1.0.1' } ); - throw new Error( 'Expected to throw.' ); - } catch ( err ) { - expect( err.message ).to.equal( 'Custom error.' ); - } - } ); + await expect( updateVersions( { version: '1.0.1' } ) ) + .rejects.toThrow( 'Custom error.' ); + } ); - it( 'should not use the root package name when checking version availability if `packagesDirectory` is provided', async () => { - stubs.glob.resolves( [ - '/ckeditor5-dev/packages/package1/package.json', - '/ckeditor5-dev/packages/package2/package.json', - '/ckeditor5-dev/package.json' - ] ); - stubs.readJson.withArgs( '/ckeditor5-dev/packages/package1/package.json' ).resolves( { name: 'package1' } ); - stubs.readJson.withArgs( '/ckeditor5-dev/packages/package2/package.json' ).resolves( { name: 'package2' } ); - stubs.readJson.withArgs( '/ckeditor5-dev/package.json' ).resolves( { name: 'root-package' } ); + it( 'should not use the root package name when checking version availability if `packagesDirectory` is provided', async () => { + vi.mocked( glob ).mockResolvedValue( [ + '/ckeditor5-dev/packages/package1/package.json', + '/ckeditor5-dev/packages/package2/package.json', + '/ckeditor5-dev/package.json' + ] ); - await updateVersions( { version: '1.0.1', packagesDirectory: 'packages' } ); + vi.mocked( fs ).readJson.mockImplementation( input => { + if ( input === '/ckeditor5-dev/packages/package1/package.json' ) { + return Promise.resolve( { name: 'package1', version: '1.0.0' } ); + } + + if ( input === '/ckeditor5-dev/packages/package2/package.json' ) { + return Promise.resolve( { name: 'package2', version: '1.0.0' } ); + } - expect( stubs.checkVersionAvailability.callCount ).to.equal( 1 ); - expect( stubs.checkVersionAvailability.firstCall.args[ 1 ] ).to.not.equal( 'root-package' ); + return Promise.resolve( { name: 'root-package', version: '1.0.0' } ); } ); - it( 'should use the root package name when checking version availability if `packagesDirectory` is not provided', async () => { - stubs.glob.resolves( [ '/ckeditor5-dev/package.json' ] ); - stubs.readJson.withArgs( '/ckeditor5-dev/package.json' ).resolves( { name: 'root-package' } ); + await updateVersions( { version: '1.0.1', packagesDirectory: 'packages' } ); - await updateVersions( { version: '1.0.1' } ); + expect( vi.mocked( checkVersionAvailability ) ).toHaveBeenCalledExactlyOnceWith( + '1.0.1', + expect.not.stringContaining( 'root-package' ) + ); + } ); - expect( stubs.checkVersionAvailability.callCount ).to.equal( 1 ); - expect( stubs.checkVersionAvailability.firstCall.args[ 1 ] ).to.equal( 'root-package' ); - } ); + it( 'should use the root package name when checking version availability if `packagesDirectory` is not provided', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/ckeditor5-dev/package.json' ] ); + vi.mocked( fs ).readJson.mockResolvedValue( { name: 'root-package', version: '1.0.0' } ); - it( 'should accept `0.0.0-nightly*` version for nightly releases', async () => { - stubs.readJson.resolves( { version: '1.0.0', name: 'stub-package' } ); + await updateVersions( { version: '1.0.1' } ); - try { - await updateVersions( { version: '0.0.0-nightly-20230510.0' } ); - } catch ( err ) { - throw new Error( 'Expected not to throw.' ); - } - } ); + expect( vi.mocked( checkVersionAvailability ) ).toHaveBeenCalledExactlyOnceWith( '1.0.1', 'root-package' ); + } ); - it( 'should throw when new version is not greater than the current one', async () => { - stubs.readJson.resolves( { version: '1.0.1' } ); + it( 'should accept `0.0.0-nightly*` version for nightly releases', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { version: '1.0.0', name: 'stub-package' } ); - try { - await updateVersions( { version: '1.0.0' } ); - throw new Error( 'Expected to throw.' ); - } catch ( err ) { - expect( err.message ).to.equal( 'Provided version 1.0.0 must be greater than 1.0.1 or match pattern 0.0.0-nightly.' ); - } - } ); + await expect( updateVersions( { version: '0.0.0-nightly-20230510.0' } ) ).resolves.toBeNil(); + } ); - it( 'should throw an error when new version is not a valid semver version', async () => { - try { - await updateVersions( { version: 'x.y.z' } ); - throw new Error( 'Expected to throw.' ); - } catch ( err ) { - expect( err.message ).to.equal( 'Invalid Version: x.y.z' ); - } - } ); + it( 'should throw when new version is not greater than the current one', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { version: '1.0.1' } ); - it( 'should be able to provide custom cwd', async () => { - await updateVersions( { version: '1.0.1', cwd: 'Users/username/ckeditor5-dev/custom-dir' } ); + await expect( updateVersions( { version: '1.0.0' } ) ) + .rejects.toThrow( 'Provided version 1.0.0 must be greater than 1.0.1 or match pattern 0.0.0-nightly.' ); + } ); - expect( stubs.glob.firstCall.args[ 1 ] ).to.deep.equal( { - cwd: 'Users/username/ckeditor5-dev/custom-dir', - absolute: true, - nodir: true - } ); - } ); + it( 'should throw an error when new version is not a valid semver version', async () => { + await expect( updateVersions( { version: 'x.y.z' } ) ) + .rejects.toThrow( 'Invalid Version: x.y.z' ); + } ); + + it( 'should be able to provide custom cwd', async () => { + await updateVersions( { version: '1.0.1', cwd: 'Users/username/ckeditor5-dev/custom-dir' } ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + expect.any( Array ), + expect.objectContaining( { + cwd: 'Users/username/ckeditor5-dev/custom-dir' + } ) + ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js b/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js index c2dfb8ec5..2d701fcd5 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js @@ -5,141 +5,91 @@ 'use strict'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); - -describe( 'dev-release-tools/utils', () => { - describe( 'verifyPackagesPublishedCorrectly()', () => { - let verifyPackagesPublishedCorrectly, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - fs: { - remove: sandbox.stub().resolves(), - readJson: sandbox.stub().resolves() - }, - devUtils: { - checkVersionAvailability: sandbox.stub().resolves() - }, - glob: { - glob: sandbox.stub().resolves( [] ) - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( '../utils/checkversionavailability', stubs.devUtils ); - mockery.registerMock( 'glob', stubs.glob ); - - verifyPackagesPublishedCorrectly = require( '../../lib/tasks/verifypackagespublishedcorrectly' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); - } ); - - it( 'should not verify packages if there are no packages in the release directory', async () => { - stubs.glob.glob.resolves( [] ); - - const packagesDirectory = '/workspace/ckeditor5/release/npm'; - const version = 'latest'; - const onSuccess = sandbox.stub(); - - await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ); - - expect( onSuccess.firstCall.args[ 0 ] ).to.equal( 'No packages found to check for upload error 409.' ); - expect( stubs.devUtils.checkVersionAvailability.callCount ).to.equal( 0 ); - } ); - - it( 'should verify packages and remove them from the release directory on "npm show" command success', async () => { - stubs.glob.glob.resolves( [ 'package1', 'package2' ] ); - stubs.fs.readJson - .onCall( 0 ).resolves( { name: '@namespace/package1' } ) - .onCall( 1 ).resolves( { name: '@namespace/package2' } ); - - const packagesDirectory = '/workspace/ckeditor5/release/npm'; - const version = 'latest'; - const onSuccess = sandbox.stub(); - - await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ); - - expect( stubs.devUtils.checkVersionAvailability.firstCall.args[ 0 ] ).to.equal( 'latest' ); - expect( stubs.devUtils.checkVersionAvailability.firstCall.args[ 1 ] ).to.equal( '@namespace/package1' ); - expect( stubs.fs.remove.firstCall.args[ 0 ] ).to.equal( 'package1' ); - - expect( stubs.devUtils.checkVersionAvailability.secondCall.args[ 0 ] ).to.equal( 'latest' ); - expect( stubs.devUtils.checkVersionAvailability.secondCall.args[ 1 ] ).to.equal( '@namespace/package2' ); - expect( stubs.fs.remove.secondCall.args[ 0 ] ).to.equal( 'package2' ); - - expect( onSuccess.firstCall.args[ 0 ] ).to.equal( 'All packages that returned 409 were uploaded correctly.' ); - } ); - - it( 'should not remove package from release directory when package is not available on npm', async () => { - stubs.glob.glob.resolves( [ 'package1', 'package2' ] ); - stubs.fs.readJson - .onCall( 0 ).resolves( { name: '@namespace/package1' } ) - .onCall( 1 ).resolves( { name: '@namespace/package2' } ); - stubs.devUtils.checkVersionAvailability - .onCall( 0 ).resolves( true ) - .onCall( 1 ).resolves( false ); - - const packagesDirectory = '/workspace/ckeditor5/release/npm'; - const version = 'latest'; - const onSuccess = sandbox.stub(); - - await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ) - .then( - () => { - throw new Error( 'this should not be thrown!' ); - }, - e => { - expect( e.message ).to.equal( - 'Packages that were uploaded incorrectly, and need manual verification:\n@namespace/package1' - ); - } - ); - - expect( stubs.fs.remove.callCount ).to.equal( 1 ); - expect( stubs.fs.remove.firstCall.args[ 0 ] ).to.equal( 'package2' ); - } ); - - it( 'should not remove package from release directory when checking version on npm throws error', async () => { - stubs.glob.glob.resolves( [ 'package1', 'package2' ] ); - stubs.fs.readJson - .onCall( 0 ).resolves( { name: '@namespace/package1' } ) - .onCall( 1 ).resolves( { name: '@namespace/package2' } ); - stubs.devUtils.checkVersionAvailability - .onCall( 0 ).rejects() - .onCall( 1 ).resolves(); - - const packagesDirectory = '/workspace/ckeditor5/release/npm'; - const version = 'latest'; - const onSuccess = sandbox.stub(); - - await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ) - .then( - () => { - throw new Error( 'this should not be thrown!' ); - }, - e => { - expect( e.message ).to.equal( - 'Packages that were uploaded incorrectly, and need manual verification:\n@namespace/package1' - ); - } - ); - - expect( stubs.fs.remove.callCount ).to.equal( 1 ); - expect( stubs.fs.remove.firstCall.args[ 0 ] ).to.equal( 'package2' ); - } ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { glob } from 'glob'; +import fs from 'fs-extra'; +import verifyPackagesPublishedCorrectly from '../../lib/tasks/verifypackagespublishedcorrectly'; +import checkVersionAvailability from '../../lib/utils/checkversionavailability'; + +vi.mock( 'fs-extra' ); +vi.mock( '../../lib/utils/checkversionavailability' ); +vi.mock( 'glob' ); + +describe( 'verifyPackagesPublishedCorrectly()', () => { + beforeEach( () => { + vi.mocked( fs ).remove.mockResolvedValue(); + vi.mocked( fs ).readJson.mockResolvedValue(); + vi.mocked( glob ).mockResolvedValue( [] ); + vi.mocked( checkVersionAvailability ).mockResolvedValue(); + } ); + + it( 'should not verify packages if there are no packages in the release directory', async () => { + const packagesDirectory = '/workspace/ckeditor5/release/npm'; + const version = 'latest'; + const onSuccess = vi.fn(); + + await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ); + + expect( onSuccess ).toHaveBeenCalledExactlyOnceWith( 'No packages found to check for upload error 409.' ); + expect( vi.mocked( checkVersionAvailability ) ).not.toHaveBeenCalled(); + } ); + + it( 'should verify packages and remove them from the release directory on "npm show" command success', async () => { + vi.mocked( glob ).mockResolvedValue( [ 'package1', 'package2' ] ); + vi.mocked( fs ).readJson + .mockResolvedValueOnce( { name: '@namespace/package1' } ) + .mockResolvedValueOnce( { name: '@namespace/package2' } ); + + const packagesDirectory = '/workspace/ckeditor5/release/npm'; + const version = 'latest'; + const onSuccess = vi.fn(); + + await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ); + + expect( vi.mocked( checkVersionAvailability ) ).toHaveBeenCalledWith( 'latest', '@namespace/package1' ); + expect( vi.mocked( checkVersionAvailability ) ).toHaveBeenCalledWith( 'latest', '@namespace/package2' ); + expect( vi.mocked( fs ).remove ).toHaveBeenCalledWith( 'package1' ); + expect( vi.mocked( fs ).remove ).toHaveBeenCalledWith( 'package2' ); + + expect( onSuccess ).toHaveBeenCalledExactlyOnceWith( 'All packages that returned 409 were uploaded correctly.' ); + } ); + + it( 'should not remove package from release directory when package is not available on npm', async () => { + vi.mocked( glob ).mockResolvedValue( [ 'package1', 'package2' ] ); + vi.mocked( fs ).readJson + .mockResolvedValueOnce( { name: '@namespace/package1' } ) + .mockResolvedValueOnce( { name: '@namespace/package2' } ); + vi.mocked( checkVersionAvailability ) + .mockResolvedValueOnce( true ) + .mockResolvedValueOnce( false ); + + const packagesDirectory = '/workspace/ckeditor5/release/npm'; + const version = 'latest'; + const onSuccess = vi.fn(); + + await expect( verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ) ) + .rejects.toThrow( 'Packages that were uploaded incorrectly, and need manual verification:\n@namespace/package1' ); + + expect( vi.mocked( fs ).remove ).toHaveBeenCalledExactlyOnceWith( 'package2' ); + } ); + + it( 'should not remove package from release directory when checking version on npm throws error', async () => { + vi.mocked( glob ).mockResolvedValue( [ 'package1', 'package2' ] ); + vi.mocked( fs ).readJson + .mockResolvedValueOnce( { name: '@namespace/package1' } ) + .mockResolvedValueOnce( { name: '@namespace/package2' } ); + vi.mocked( checkVersionAvailability ) + .mockRejectedValueOnce( ) + .mockResolvedValueOnce( false ); + + const packagesDirectory = '/workspace/ckeditor5/release/npm'; + const version = 'latest'; + const onSuccess = vi.fn(); + + await expect( verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } ) ) + .rejects + .toThrow( 'Packages that were uploaded incorrectly, and need manual verification:\n@namespace/package1' ); + + expect( vi.mocked( fs ).remove ).toHaveBeenCalledExactlyOnceWith( 'package2' ); } ); } ); From d7dee9f8c7175c47c706bfe9b58063ec4a9f45ba Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 6 Sep 2024 12:19:26 +0200 Subject: [PATCH 017/172] Util modules are now exported directly instead of wrapping it in an object. --- .../lib/utils/changelog.js | 187 +++--- .../lib/utils/cli.js | 590 +++++++++--------- .../lib/utils/getnpmtagfromversion.js | 2 +- .../lib/utils/versions.js | 240 ++++--- .../tests/index.js | 221 ++++--- 5 files changed, 614 insertions(+), 626 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js index b7b80feec..07c9e1a48 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js @@ -8,100 +8,97 @@ import path from 'path'; import { format } from 'date-fns'; import { getRepositoryUrl } from './transformcommitutils'; -const utils = { - /** - * Changelog file name. - */ - changelogFile: 'CHANGELOG.md', - - /** - * Changelog header. - */ - changelogHeader: 'Changelog\n=========\n\n', - - /** - * Retrieves changes from the changelog for the given version (tag). - * - * @param {String} version - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} - */ - getChangesForVersion( version, cwd = process.cwd() ) { - version = version.replace( /^v/, '' ); - - const changelog = utils.getChangelog( cwd ).replace( utils.changelogHeader, '\n' ); - const match = changelog.match( new RegExp( `\\n(## \\[?${ version }\\]?[\\s\\S]+?)(?:\\n## \\[?|$)` ) ); - - if ( !match || !match[ 1 ] ) { - return null; - } - - return match[ 1 ].replace( /##[^\n]+\n/, '' ).trim(); - }, - - /** - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} - */ - getChangelog( cwd = process.cwd() ) { - const changelogFile = path.join( cwd, utils.changelogFile ); - - if ( !fs.existsSync( changelogFile ) ) { - return null; - } - - return fs.readFileSync( changelogFile, 'utf-8' ); - }, - - /** - * @param {String} content - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - */ - saveChangelog( content, cwd = process.cwd() ) { - const changelogFile = path.join( cwd, utils.changelogFile ); - - fs.writeFileSync( changelogFile, content, 'utf-8' ); - }, - - /** - * @param {Number} length - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - */ - truncateChangelog( length, cwd = process.cwd() ) { - const changelog = utils.getChangelog( cwd ); - - if ( !changelog ) { - return; - } - - const entryHeader = '## [\\s\\S]+?'; - const entryHeaderRegexp = new RegExp( `\\n(${ entryHeader })(?=\\n${ entryHeader }|$)`, 'g' ); - - const entries = [ ...changelog.matchAll( entryHeaderRegexp ) ] - .filter( match => match && match[ 1 ] ) - .map( match => match[ 1 ] ); - - if ( !entries.length ) { - return; - } - - const truncatedEntries = entries.slice( 0, length ); - - const changelogFooter = entries.length > truncatedEntries.length ? - `\n\n---\n\nTo see all releases, visit the [release page](${ getRepositoryUrl( cwd ) }/releases).\n` : - '\n'; - - const truncatedChangelog = utils.changelogHeader + truncatedEntries.join( '\n' ).trim() + changelogFooter; - - utils.saveChangelog( truncatedChangelog, cwd ); - }, - - /** - * @returns {String} - */ - getFormattedDate() { - return format( new Date(), 'yyyy-MM-dd' ); +/** + * Changelog file name. + */ +export const CHANGELOG_FILE = 'CHANGELOG.md'; + +/** + * Changelog header. + */ +export const CHANGELOG_HEADER = 'Changelog\n=========\n\n'; + +/** + * Retrieves changes from the changelog for the given version (tag). + * + * @param {String} version + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {String|null} + */ +export function getChangesForVersion( version, cwd = process.cwd() ) { + version = version.replace( /^v/, '' ); + + const changelog = getChangelog( cwd ).replace( CHANGELOG_HEADER, '\n' ); + const match = changelog.match( new RegExp( `\\n(## \\[?${ version }\\]?[\\s\\S]+?)(?:\\n## \\[?|$)` ) ); + + if ( !match || !match[ 1 ] ) { + return null; } -}; -export default utils; + return match[ 1 ].replace( /##[^\n]+\n/, '' ).trim(); +} + +/** + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {String|null} + */ +export function getChangelog( cwd = process.cwd() ) { + const changelogFile = path.join( cwd, CHANGELOG_FILE ); + + if ( !fs.existsSync( changelogFile ) ) { + return null; + } + + return fs.readFileSync( changelogFile, 'utf-8' ); +} + +/** + * @param {String} content + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + */ +export function saveChangelog( content, cwd = process.cwd() ) { + const changelogFile = path.join( cwd, CHANGELOG_FILE ); + + fs.writeFileSync( changelogFile, content, 'utf-8' ); +} + +/** + * @param {Number} length + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + */ +export function truncateChangelog( length, cwd = process.cwd() ) { + const changelog = getChangelog( cwd ); + + if ( !changelog ) { + return; + } + + const entryHeader = '## [\\s\\S]+?'; + const entryHeaderRegexp = new RegExp( `\\n(${ entryHeader })(?=\\n${ entryHeader }|$)`, 'g' ); + + const entries = [ ...changelog.matchAll( entryHeaderRegexp ) ] + .filter( match => match && match[ 1 ] ) + .map( match => match[ 1 ] ); + + if ( !entries.length ) { + return; + } + + const truncatedEntries = entries.slice( 0, length ); + + const changelogFooter = entries.length > truncatedEntries.length ? + `\n\n---\n\nTo see all releases, visit the [release page](${ getRepositoryUrl( cwd ) }/releases).\n` : + '\n'; + + const truncatedChangelog = CHANGELOG_HEADER + truncatedEntries.join( '\n' ).trim() + changelogFooter; + + saveChangelog( truncatedChangelog, cwd ); +} + +/** + * @returns {String} + */ +export function getFormattedDate() { + return format( new Date(), 'yyyy-MM-dd' ); +} + diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/cli.js b/packages/ckeditor5-dev-release-tools/lib/utils/cli.js index 8db21a515..4c333da62 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/cli.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/cli.js @@ -9,339 +9,335 @@ import chalk from 'chalk'; const QUESTION_MARK = chalk.cyan( '?' ); -const cli = { - /** - * A size of default indent for a log. - */ - INDENT_SIZE: 3, - - /** - * A size of indent for a second and next lines in a log. The number is equal to length of the log string: - * '* 1234567 ', where '1234567' is a short commit id. - * It does not include a value from `cli.INDENT_SIZE`. - */ - COMMIT_INDENT_SIZE: 10, - - /** - * Asks a user for a confirmation for updating and tagging versions of the packages. - * - * @param {Map} packages Packages to release. - * @returns {Promise.} - */ - confirmUpdatingVersions( packages ) { - let message = 'Packages and their old and new versions:\n'; - - for ( const packageName of Array.from( packages.keys() ).sort() ) { - const packageDetails = packages.get( packageName ); - - message += ` * "${ packageName }": v${ packageDetails.previousVersion } => v${ packageDetails.version }\n`; - } +/** + * A size of default indent for a log. + */ +export const INDENT_SIZE = 3; - message += 'Continue?'; +/** + * A size of indent for a second and next lines in a log. The number is equal to length of the log string: + * '* 1234567 ', where '1234567' is a short commit id. + * It does not include a value from `cli.INDENT_SIZE`. + */ +export const COMMIT_INDENT_SIZE = 10; - const confirmQuestion = { - message, - type: 'confirm', - name: 'confirm', - default: true - }; +/** + * Asks a user for a confirmation for updating and tagging versions of the packages. + * + * @param {Map} packages Packages to release. + * @returns {Promise.} + */ +export function confirmUpdatingVersions( packages ) { + let message = 'Packages and their old and new versions:\n'; - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); - }, + for ( const packageName of Array.from( packages.keys() ).sort() ) { + const packageDetails = packages.get( packageName ); - /** - * Asks a user for a confirmation for publishing changes. - * - * @param {Map} packages Packages to release. - * @returns {Promise.} - */ - confirmPublishing( packages ) { - let message = 'Services where the release will be created:\n'; + message += ` * "${ packageName }": v${ packageDetails.previousVersion } => v${ packageDetails.version }\n`; + } - for ( const packageName of Array.from( packages.keys() ).sort() ) { - const packageDetails = packages.get( packageName ); + message += 'Continue?'; - let packageMessage = ` * "${ packageName }" - version: ${ packageDetails.version }`; + const confirmQuestion = { + message, + type: 'confirm', + name: 'confirm', + default: true + }; - const services = []; + return inquirer.prompt( [ confirmQuestion ] ) + .then( answers => answers.confirm ); +} - if ( packageDetails.shouldReleaseOnNpm ) { - services.push( 'NPM' ); - } +/** + * Asks a user for a confirmation for publishing changes. + * + * @param {Map} packages Packages to release. + * @returns {Promise.} + */ +export function confirmPublishing( packages ) { + let message = 'Services where the release will be created:\n'; - if ( packageDetails.shouldReleaseOnGithub ) { - services.push( 'GitHub' ); - } + for ( const packageName of Array.from( packages.keys() ).sort() ) { + const packageDetails = packages.get( packageName ); - let color; + let packageMessage = ` * "${ packageName }" - version: ${ packageDetails.version }`; - if ( services.length ) { - color = chalk.magenta; - packageMessage += ` - services: ${ services.join( ', ' ) } `; - } else { - color = chalk.gray; - packageMessage += ' - nothing to release'; - } + const services = []; + + if ( packageDetails.shouldReleaseOnNpm ) { + services.push( 'NPM' ); + } - message += color( packageMessage ) + '\n'; + if ( packageDetails.shouldReleaseOnGithub ) { + services.push( 'GitHub' ); } - message += 'Continue?'; - - const confirmQuestion = { - message, - type: 'confirm', - name: 'confirm', - default: true - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); - }, - - /** - * Asks a user for a confirmation for removing archives created by `npm pack` command. - * - * @returns {Promise.} - */ - confirmRemovingFiles() { - const confirmQuestion = { - message: 'Remove created archives?', - type: 'confirm', - name: 'confirm', - default: true - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); - }, - - /** - * Asks a user for a confirmation for including a package that does not contain all required files. - * - * @returns {Promise.} - */ - confirmIncludingPackage() { - const confirmQuestion = { - message: 'Package does not contain all required files to publish. Include this package in the release and continue?', - type: 'confirm', - name: 'confirm', - default: true - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); - }, - - /** - * Asks a user for providing the new version. - * - * @param {String} packageVersion - * @param {String|null} releaseTypeOrNewVersion - * @param {Object} [options] - * @param {Boolean} [options.disableInternalVersion=false] Whether to "internal" version is enabled. - * @param {Boolean} [options.disableSkipVersion=false] Whether to "skip" version is enabled. - * @param {Number} [options.indentLevel=0] The indent level. - * @returns {Promise.} - */ - provideVersion( packageVersion, releaseTypeOrNewVersion, options = {} ) { - const indentLevel = options.indentLevel || 0; - const suggestedVersion = getSuggestedVersion(); - - let message = 'Type the new version, "skip" or "internal"'; - - if ( options.disableInternalVersion ) { - message = 'Type the new version or "skip"'; + let color; + + if ( services.length ) { + color = chalk.magenta; + packageMessage += ` - services: ${ services.join( ', ' ) } `; + } else { + color = chalk.gray; + packageMessage += ' - nothing to release'; } - message += ` (suggested: "${ suggestedVersion }", current: "${ packageVersion }"):`; + message += color( packageMessage ) + '\n'; + } - const versionQuestion = { - type: 'input', - name: 'version', - default: suggestedVersion, - message, + message += 'Continue?'; - filter( input ) { - return input.trim(); - }, + const confirmQuestion = { + message, + type: 'confirm', + name: 'confirm', + default: true + }; - validate( input ) { - if ( !options.disableSkipVersion && input === 'skip' ) { - return true; - } + return inquirer.prompt( [ confirmQuestion ] ) + .then( answers => answers.confirm ); +} - if ( !options.disableInternalVersion && input === 'internal' ) { - return true; - } +/** + * Asks a user for a confirmation for removing archives created by `npm pack` command. + * + * @returns {Promise.} + */ +export function confirmRemovingFiles() { + const confirmQuestion = { + message: 'Remove created archives?', + type: 'confirm', + name: 'confirm', + default: true + }; + + return inquirer.prompt( [ confirmQuestion ] ) + .then( answers => answers.confirm ); +} - // TODO: Check whether provided version is available. - return semver.valid( input ) ? true : 'Please provide a valid version.'; - }, +/** + * Asks a user for a confirmation for including a package that does not contain all required files. + * + * @returns {Promise.} + */ +export function confirmIncludingPackage() { + const confirmQuestion = { + message: 'Package does not contain all required files to publish. Include this package in the release and continue?', + type: 'confirm', + name: 'confirm', + default: true + }; + + return inquirer.prompt( [ confirmQuestion ] ) + .then( answers => answers.confirm ); +} - prefix: getPrefix( indentLevel ) - }; +/** + * Asks a user for providing the new version. + * + * @param {String} packageVersion + * @param {String|null} releaseTypeOrNewVersion + * @param {Object} [options] + * @param {Boolean} [options.disableInternalVersion=false] Whether to "internal" version is enabled. + * @param {Boolean} [options.disableSkipVersion=false] Whether to "skip" version is enabled. + * @param {Number} [options.indentLevel=0] The indent level. + * @returns {Promise.} + */ +export function provideVersion( packageVersion, releaseTypeOrNewVersion, options = {} ) { + const indentLevel = options.indentLevel || 0; + const suggestedVersion = getSuggestedVersion(); - return inquirer.prompt( [ versionQuestion ] ) - .then( answers => answers.version ); + let message = 'Type the new version, "skip" or "internal"'; - function getSuggestedVersion() { - if ( !releaseTypeOrNewVersion || releaseTypeOrNewVersion === 'skip' ) { - return 'skip'; - } + if ( options.disableInternalVersion ) { + message = 'Type the new version or "skip"'; + } - if ( semver.valid( releaseTypeOrNewVersion ) ) { - return releaseTypeOrNewVersion; - } + message += ` (suggested: "${ suggestedVersion }", current: "${ packageVersion }"):`; - if ( releaseTypeOrNewVersion === 'internal' ) { - return options.disableInternalVersion ? 'skip' : 'internal'; + const versionQuestion = { + type: 'input', + name: 'version', + default: suggestedVersion, + message, + + filter( input ) { + return input.trim(); + }, + + validate( input ) { + if ( !options.disableSkipVersion && input === 'skip' ) { + return true; } - if ( semver.prerelease( packageVersion ) ) { - releaseTypeOrNewVersion = 'prerelease'; + if ( !options.disableInternalVersion && input === 'internal' ) { + return true; } - // If package's version is below the '1.0.0', bump the 'minor' instead of 'major' - if ( releaseTypeOrNewVersion === 'major' && semver.gt( '1.0.0', packageVersion ) ) { - return semver.inc( packageVersion, 'minor' ); + // TODO: Check whether provided version is available. + return semver.valid( input ) ? true : 'Please provide a valid version.'; + }, + + prefix: getPrefix( indentLevel ) + }; + + return inquirer.prompt( [ versionQuestion ] ) + .then( answers => answers.version ); + + function getSuggestedVersion() { + if ( !releaseTypeOrNewVersion || releaseTypeOrNewVersion === 'skip' ) { + return 'skip'; + } + + if ( semver.valid( releaseTypeOrNewVersion ) ) { + return releaseTypeOrNewVersion; + } + + if ( releaseTypeOrNewVersion === 'internal' ) { + return options.disableInternalVersion ? 'skip' : 'internal'; + } + + if ( semver.prerelease( packageVersion ) ) { + releaseTypeOrNewVersion = 'prerelease'; + } + + // If package's version is below the '1.0.0', bump the 'minor' instead of 'major' + if ( releaseTypeOrNewVersion === 'major' && semver.gt( '1.0.0', packageVersion ) ) { + return semver.inc( packageVersion, 'minor' ); + } + + return semver.inc( packageVersion, releaseTypeOrNewVersion ); + } +} + +/** + * Asks a user for providing the new version for a major release. + * + * @param {String} version + * @param {String} foundPackage + * @param {String} bumpType + * @param {Object} [options={}] + * @param {Number} [options.indentLevel=0] The indent level. + * @returns {Promise.} + */ +export function provideNewVersionForMonoRepository( version, foundPackage, bumpType, options = {} ) { + const indentLevel = options.indentLevel || 0; + const suggestedVersion = semver.inc( version, bumpType ); + + const message = 'Type the new version ' + + `(current highest: "${ version }" found in "${ chalk.underline( foundPackage ) }", suggested: "${ suggestedVersion }"):`; + + const versionQuestion = { + type: 'input', + name: 'version', + default: suggestedVersion, + message, + + filter( input ) { + return input.trim(); + }, + + validate( input ) { + if ( !semver.valid( input ) ) { + return 'Please provide a valid version.'; } - return semver.inc( packageVersion, releaseTypeOrNewVersion ); + return semver.gt( input, version ) ? true : `Provided version must be higher than "${ version }".`; + }, + prefix: getPrefix( indentLevel ) + }; + + return inquirer.prompt( [ versionQuestion ] ) + .then( answers => answers.version ); +} + +/** + * Asks a user for providing the GitHub token. + * + * @returns {Promise.} + */ +export function provideToken() { + const tokenQuestion = { + type: 'password', + name: 'token', + message: 'Provide the GitHub token:', + validate( input ) { + return input.length === 40 ? true : 'Please provide a valid token.'; } - }, - - /** - * Asks a user for providing the new version for a major release. - * - * @param {String} version - * @param {String} foundPackage - * @param {String} bumpType - * @param {Object} [options={}] - * @param {Number} [options.indentLevel=0] The indent level. - * @returns {Promise.} - */ - provideNewVersionForMonoRepository( version, foundPackage, bumpType, options = {} ) { - const indentLevel = options.indentLevel || 0; - const suggestedVersion = semver.inc( version, bumpType ); - - const message = 'Type the new version ' + - `(current highest: "${ version }" found in "${ chalk.underline( foundPackage ) }", suggested: "${ suggestedVersion }"):`; - - const versionQuestion = { - type: 'input', - name: 'version', - default: suggestedVersion, - message, - - filter( input ) { - return input.trim(); - }, - - validate( input ) { - if ( !semver.valid( input ) ) { - return 'Please provide a valid version.'; - } - - return semver.gt( input, version ) ? true : `Provided version must be higher than "${ version }".`; - }, - prefix: getPrefix( indentLevel ) - }; - - return inquirer.prompt( [ versionQuestion ] ) - .then( answers => answers.version ); - }, - - /** - * Asks a user for providing the GitHub token. - * - * @returns {Promise.} - */ - provideToken() { - const tokenQuestion = { - type: 'password', - name: 'token', - message: 'Provide the GitHub token:', - validate( input ) { - return input.length === 40 ? true : 'Please provide a valid token.'; + }; + + return inquirer.prompt( [ tokenQuestion ] ) + .then( answers => answers.token ); +} + +/** + * Asks a user for selecting services where packages will be released. + * + * If the user choices a GitHub, required token also has to be provided. + * + * @returns {Promise.} + */ +export function configureReleaseOptions() { + const options = {}; + + const servicesQuestion = { + type: 'checkbox', + name: 'services', + message: 'Select services where packages will be released:', + choices: [ + 'npm', + 'GitHub' + ], + default: [ + 'npm', + 'GitHub' + ] + }; + + return inquirer.prompt( [ servicesQuestion ] ) + .then( answers => { + options.npm = answers.services.includes( 'npm' ); + options.github = answers.services.includes( 'GitHub' ); + + if ( !options.github ) { + return options; } - }; - - return inquirer.prompt( [ tokenQuestion ] ) - .then( answers => answers.token ); - }, - - /** - * Asks a user for selecting services where packages will be released. - * - * If the user choices a GitHub, required token also has to be provided. - * - * @returns {Promise.} - */ - configureReleaseOptions() { - const options = {}; - - const servicesQuestion = { - type: 'checkbox', - name: 'services', - message: 'Select services where packages will be released:', - choices: [ - 'npm', - 'GitHub' - ], - default: [ - 'npm', - 'GitHub' - ] - }; - - return inquirer.prompt( [ servicesQuestion ] ) - .then( answers => { - options.npm = answers.services.includes( 'npm' ); - options.github = answers.services.includes( 'GitHub' ); - - if ( !options.github ) { + + return provideToken() + .then( token => { + options.token = token; + return options; - } - - return cli.provideToken() - .then( token => { - options.token = token; - - return options; - } ); - } ); - }, - - /** - * Asks a user for a confirmation for updating and tagging versions of the packages. - * - * @param {String} versionTag A version tag based on a package version specified in `package.json`. - * @param {String} npmTag A tag typed by the user when using the release tools. - * @returns {Promise.} - */ - confirmNpmTag( versionTag, npmTag ) { - const areVersionsEqual = versionTag === npmTag; - const color = areVersionsEqual ? chalk.magenta : chalk.red; - - // eslint-disable-next-line max-len - const message = `The next release bumps the "${ color( versionTag ) }" version. Should it be published to npm as "${ color( npmTag ) }"?`; - - const confirmQuestion = { - message, - type: 'confirm', - name: 'confirm', - default: areVersionsEqual - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); - } -}; + } ); + } ); +} -export default cli; +/** + * Asks a user for a confirmation for updating and tagging versions of the packages. + * + * @param {String} versionTag A version tag based on a package version specified in `package.json`. + * @param {String} npmTag A tag typed by the user when using the release tools. + * @returns {Promise.} + */ +export function confirmNpmTag( versionTag, npmTag ) { + const areVersionsEqual = versionTag === npmTag; + const color = areVersionsEqual ? chalk.magenta : chalk.red; + + // eslint-disable-next-line max-len + const message = `The next release bumps the "${ color( versionTag ) }" version. Should it be published to npm as "${ color( npmTag ) }"?`; + + const confirmQuestion = { + message, + type: 'confirm', + name: 'confirm', + default: areVersionsEqual + }; + + return inquirer.prompt( [ confirmQuestion ] ) + .then( answers => answers.confirm ); +} function getPrefix( indent ) { - return ' '.repeat( indent * cli.INDENT_SIZE ) + QUESTION_MARK; + return ' '.repeat( indent * INDENT_SIZE ) + QUESTION_MARK; } diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js b/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js index cca489914..4c67d1da8 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js @@ -9,7 +9,7 @@ import semver from 'semver'; * @param {String} version * @returns {String} */ -export function getNpmTagFromVersion( version ) { +export default function getNpmTagFromVersion( version ) { const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ]; return versionTag; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js index 70e3d7946..07fd3a76f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js @@ -7,130 +7,126 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; import changelogUtils from './changelog'; import getPackageJson from './getpackagejson'; -const versions = { - /** - * Returns a last created version in changelog file. - * - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} - */ - getLastFromChangelog( cwd = process.cwd() ) { - const changelog = changelogUtils.getChangelog( cwd ); - - if ( !changelog ) { - return null; - } - - const regexp = /\n## \[?([\da-z.\-+]+)/i; - const matches = changelog.match( regexp ); - - return matches ? matches[ 1 ] : null; - }, - - /** - * Returns the current (latest) pre-release version that matches the provided release identifier. - * If the package does not have any pre-releases with the provided identifier yet, `null` is returned. - * - * @param {ReleaseIdentifier} releaseIdentifier - * @param {String} [cwd=process.cwd()] - * @returns {Promise.} - */ - getLastPreRelease( releaseIdentifier, cwd = process.cwd() ) { - const packageName = getPackageJson( cwd ).name; - - return tools.shExec( `npm view ${ packageName } versions --json`, { verbosity: 'silent', async: true } ) - .then( result => { - const lastVersion = JSON.parse( result ) - .filter( version => version.startsWith( releaseIdentifier ) ) - .sort( ( a, b ) => a.localeCompare( b, undefined, { numeric: true } ) ) - .pop(); - - return lastVersion || null; - } ) - .catch( () => null ); - }, - - /** - * Returns the current (latest) nightly version in the format of "0.0.0-nightly-YYYYMMDD.X", where the "YYYYMMDD" is the date of the - * last nightly release and the "X" is the sequential number starting from 0. If the package does not have any nightly releases yet, - * `null` is returned. - * - * @param {String} [cwd=process.cwd()] - * @returns {Promise.} - */ - getLastNightly( cwd = process.cwd() ) { - return versions.getLastPreRelease( '0.0.0-nightly', cwd ); - }, - - /** - * Returns the next available pre-release version that matches the following format: ".X", where "X" is the - * next available pre-release sequential number starting from 0. - * - * @param {ReleaseIdentifier} releaseIdentifier - * @param {String} [cwd=process.cwd()] - * @returns {Promise} - */ - async getNextPreRelease( releaseIdentifier, cwd = process.cwd() ) { - const currentPreReleaseVersion = await versions.getLastPreRelease( releaseIdentifier, cwd ); - - if ( !currentPreReleaseVersion ) { - return `${ releaseIdentifier }.0`; - } - - const currentPreReleaseVersionTokens = currentPreReleaseVersion.split( '.' ); - const currentPreReleaseSequenceNumber = currentPreReleaseVersionTokens.pop(); - const currentPreReleaseIdentifier = currentPreReleaseVersionTokens.join( '.' ); - const nextPreReleaseSequenceNumber = Number( currentPreReleaseSequenceNumber ) + 1; - - return `${ currentPreReleaseIdentifier }.${ nextPreReleaseSequenceNumber }`; - }, - - /** - * Returns the next available nightly version in the format of "0.0.0-nightly-YYYYMMDD.X", where the "YYYYMMDD" is the current date for - * the nightly release and the "X" is the sequential number starting from 0. - * - * @param {String} [cwd=process.cwd()] - * @returns {Promise} - */ - async getNextNightly( cwd = process.cwd() ) { - const today = new Date(); - const year = today.getFullYear().toString(); - const month = ( today.getMonth() + 1 ).toString().padStart( 2, '0' ); - const day = today.getDate().toString().padStart( 2, '0' ); - - const nextNightlyReleaseIdentifier = `0.0.0-nightly-${ year }${ month }${ day }`; - - return versions.getNextPreRelease( nextNightlyReleaseIdentifier, cwd ); - }, - - /** - * Returns a name of the last created tag. - * - * @returns {String|null} - */ - getLastTagFromGit() { - try { - const lastTag = tools.shExec( 'git describe --abbrev=0 --tags 2> /dev/null', { verbosity: 'error' } ); - - return lastTag.trim().replace( /^v/, '' ) || null; - } catch ( err ) { - /* istanbul ignore next */ - return null; - } - }, - - /** - * Returns version of current package from `package.json`. - * - * @param {String} [cwd=process.cwd()] Current work directory. - * @returns {String} - */ - getCurrent( cwd = process.cwd() ) { - return getPackageJson( cwd ).version; +/** + * Returns a last created version in changelog file. + * + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {String|null} + */ +export function getLastFromChangelog( cwd = process.cwd() ) { + const changelog = changelogUtils.getChangelog( cwd ); + + if ( !changelog ) { + return null; + } + + const regexp = /\n## \[?([\da-z.\-+]+)/i; + const matches = changelog.match( regexp ); + + return matches ? matches[ 1 ] : null; +} + +/** + * Returns the current (latest) pre-release version that matches the provided release identifier. + * If the package does not have any pre-releases with the provided identifier yet, `null` is returned. + * + * @param {ReleaseIdentifier} releaseIdentifier + * @param {String} [cwd=process.cwd()] + * @returns {Promise.} + */ +export function getLastPreRelease( releaseIdentifier, cwd = process.cwd() ) { + const packageName = getPackageJson( cwd ).name; + + return tools.shExec( `npm view ${ packageName } versions --json`, { verbosity: 'silent', async: true } ) + .then( result => { + const lastVersion = JSON.parse( result ) + .filter( version => version.startsWith( releaseIdentifier ) ) + .sort( ( a, b ) => a.localeCompare( b, undefined, { numeric: true } ) ) + .pop(); + + return lastVersion || null; + } ) + .catch( () => null ); +} + +/** + * Returns the current (latest) nightly version in the format of "0.0.0-nightly-YYYYMMDD.X", where the "YYYYMMDD" is the date of the + * last nightly release and the "X" is the sequential number starting from 0. If the package does not have any nightly releases yet, + * `null` is returned. + * + * @param {String} [cwd=process.cwd()] + * @returns {Promise.} + */ +export function getLastNightly( cwd = process.cwd() ) { + return getLastPreRelease( '0.0.0-nightly', cwd ); +} + +/** + * Returns the next available pre-release version that matches the following format: ".X", where "X" is the + * next available pre-release sequential number starting from 0. + * + * @param {ReleaseIdentifier} releaseIdentifier + * @param {String} [cwd=process.cwd()] + * @returns {Promise} + */ +export async function getNextPreRelease( releaseIdentifier, cwd = process.cwd() ) { + const currentPreReleaseVersion = await getLastPreRelease( releaseIdentifier, cwd ); + + if ( !currentPreReleaseVersion ) { + return `${ releaseIdentifier }.0`; } -}; -export default versions; + const currentPreReleaseVersionTokens = currentPreReleaseVersion.split( '.' ); + const currentPreReleaseSequenceNumber = currentPreReleaseVersionTokens.pop(); + const currentPreReleaseIdentifier = currentPreReleaseVersionTokens.join( '.' ); + const nextPreReleaseSequenceNumber = Number( currentPreReleaseSequenceNumber ) + 1; + + return `${ currentPreReleaseIdentifier }.${ nextPreReleaseSequenceNumber }`; +} + +/** + * Returns the next available nightly version in the format of "0.0.0-nightly-YYYYMMDD.X", where the "YYYYMMDD" is the current date for + * the nightly release and the "X" is the sequential number starting from 0. + * + * @param {String} [cwd=process.cwd()] + * @returns {Promise} + */ +export async function getNextNightly( cwd = process.cwd() ) { + const today = new Date(); + const year = today.getFullYear().toString(); + const month = ( today.getMonth() + 1 ).toString().padStart( 2, '0' ); + const day = today.getDate().toString().padStart( 2, '0' ); + + const nextNightlyReleaseIdentifier = `0.0.0-nightly-${ year }${ month }${ day }`; + + return getNextPreRelease( nextNightlyReleaseIdentifier, cwd ); +} + +/** + * Returns a name of the last created tag. + * + * @returns {String|null} + */ +export function getLastTagFromGit() { + try { + const lastTag = tools.shExec( 'git describe --abbrev=0 --tags 2> /dev/null', { verbosity: 'error' } ); + + return lastTag.trim().replace( /^v/, '' ) || null; + } catch ( err ) { + /* istanbul ignore next */ + return null; + } +} + +/** + * Returns version of current package from `package.json`. + * + * @param {String} [cwd=process.cwd()] Current work directory. + * @returns {String} + */ +export function getCurrent( cwd = process.cwd() ) { + return getPackageJson( cwd ).version; +} /** * @typedef {String} ReleaseIdentifier The pre-release identifier without the last dynamic part (the pre-release sequential number). diff --git a/packages/ckeditor5-dev-release-tools/tests/index.js b/packages/ckeditor5-dev-release-tools/tests/index.js index 9bfc2b503..633374bb6 100644 --- a/packages/ckeditor5-dev-release-tools/tests/index.js +++ b/packages/ckeditor5-dev-release-tools/tests/index.js @@ -5,247 +5,246 @@ 'use strict'; -const sinon = require( 'sinon' ); -const expect = require( 'chai' ).expect; -const proxyquire = require( 'proxyquire' ); -const mockery = require( 'mockery' ); +import { describe, expect, it, vi } from 'vitest'; +import generateChangelogForSinglePackage from '../lib/tasks/generatechangelogforsinglepackage'; +import generateChangelogForMonoRepository from '../lib/tasks/generatechangelogformonorepository'; +import updateDependencies from '../lib/tasks/updatedependencies'; +import commitAndTag from '../lib/tasks/commitandtag'; +import createGithubRelease from '../lib/tasks/creategithubrelease'; +import reassignNpmTags from '../lib/tasks/reassignnpmtags'; +import prepareRepository from '../lib/tasks/preparerepository'; +import push from '../lib/tasks/push'; +import publishPackages from '../lib/tasks/publishpackages'; +import updateVersions from '../lib/tasks/updateversions'; +import cleanUpPackages from '../lib/tasks/cleanuppackages'; +import { + getLastFromChangelog, + getLastPreRelease, + getNextPreRelease, + getLastNightly, + getNextNightly, + getCurrent, + getLastTagFromGit +} from '../lib/utils/versions'; +import { + getChangesForVersion, + getChangelog, + saveChangelog +} from '../lib/utils/changelog'; +import executeInParallel from '../lib/utils/executeinparallel'; +import validateRepositoryToRelease from '../lib/utils/validaterepositorytorelease'; +import checkVersionAvailability from '../lib/utils/checkversionavailability'; +import verifyPackagesPublishedCorrectly from '../lib/tasks/verifypackagespublishedcorrectly'; +import getNpmTagFromVersion from '../lib/utils/getnpmtagfromversion'; +import isVersionPublishableForTag from '../lib/utils/isversionpublishablefortag'; + +import * as index from '../lib/index'; + +vi.mock( '../lib/tasks/generatechangelogforsinglepackage' ); +vi.mock( '../lib/tasks/generatechangelogformonorepository' ); +vi.mock( '../lib/tasks/updatedependencies' ); +vi.mock( '../lib/tasks/commitandtag' ); +vi.mock( '../lib/tasks/creategithubrelease' ); +vi.mock( '../lib/tasks/reassignnpmtags' ); +vi.mock( '../lib/tasks/preparerepository' ); +vi.mock( '../lib/tasks/push' ); +vi.mock( '../lib/tasks/publishpackages' ); +vi.mock( '../lib/tasks/updateversions' ); +vi.mock( '../lib/tasks/cleanuppackages' ); +vi.mock( '../lib/utils/versions' ); +vi.mock( '../lib/utils/getnpmtagfromversion' ); +vi.mock( '../lib/utils/changelog' ); +vi.mock( '../lib/utils/executeinparallel' ); +vi.mock( '../lib/utils/validaterepositorytorelease' ); +vi.mock( '../lib/utils/isversionpublishablefortag' ); describe( 'dev-release-tools/index', () => { - let index, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - logger: { - info: sandbox.spy(), - warning: sandbox.spy(), - error: sandbox.spy() - }, - release: { - generateChangelogForSinglePackage: sandbox.stub(), - generateChangelogForMonoRepository: sandbox.stub(), - updateDependencies: sandbox.stub(), - commitAndTag: sandbox.stub(), - createGithubRelease: sandbox.stub(), - reassignNpmTags: sandbox.stub(), - prepareRepository: sandbox.stub(), - push: sandbox.stub(), - publishPackages: sandbox.stub(), - updateVersions: sandbox.stub(), - cleanUpPackages: sandbox.stub(), - version: { - getLastFromChangelog: sandbox.stub(), - getLastPreRelease: sandbox.stub(), - getNextPreRelease: sandbox.stub(), - getLastNightly: sandbox.stub(), - getNextNightly: sandbox.stub(), - getCurrent: sandbox.stub(), - getLastTagFromGit: sandbox.stub() - }, - changelog: { - getChangesForVersion: sandbox.stub(), - getChangelog: sandbox.stub(), - saveChangelog: sandbox.stub() - }, - executeInParallel: sandbox.stub(), - validateRepositoryToRelease: sandbox.stub(), - getNpmTagFromVersion: sandbox.stub(), - isVersionPublishableForTag: sandbox.stub() - } - }; - - mockery.registerMock( './tasks/generatechangelogforsinglepackage', stubs.release.generateChangelogForSinglePackage ); - mockery.registerMock( './tasks/generatechangelogformonorepository', stubs.release.generateChangelogForMonoRepository ); - mockery.registerMock( './tasks/updatedependencies', stubs.release.updateDependencies ); - mockery.registerMock( './tasks/commitandtag', stubs.release.commitAndTag ); - mockery.registerMock( './tasks/creategithubrelease', stubs.release.createGithubRelease ); - mockery.registerMock( './tasks/reassignnpmtags', stubs.release.reassignNpmTags ); - mockery.registerMock( './tasks/preparerepository', stubs.release.prepareRepository ); - mockery.registerMock( './tasks/push', stubs.release.push ); - mockery.registerMock( './tasks/publishpackages', stubs.release.publishPackages ); - mockery.registerMock( './tasks/updateversions', stubs.release.updateVersions ); - mockery.registerMock( './tasks/cleanuppackages', stubs.release.cleanUpPackages ); - mockery.registerMock( './utils/versions', stubs.release.version ); - mockery.registerMock( './utils/getnpmtagfromversion', stubs.release.getNpmTagFromVersion ); - mockery.registerMock( './utils/changelog', stubs.release.changelog ); - mockery.registerMock( './utils/executeinparallel', stubs.release.executeInParallel ); - mockery.registerMock( './utils/validaterepositorytorelease', stubs.release.validateRepositoryToRelease ); - mockery.registerMock( './utils/isversionpublishablefortag', stubs.release.isVersionPublishableForTag ); - - index = proxyquire( '../lib/index', { - '@ckeditor/ckeditor5-dev-utils': { - logger() { - return stubs.logger; - } - } - } ); - } ); - - afterEach( () => { - sandbox.restore(); - mockery.disable(); - } ); - describe( 'generateChangelogForSinglePackage()', () => { it( 'should be a function', () => { expect( index.generateChangelogForSinglePackage ).to.be.a( 'function' ); + expect( index.generateChangelogForSinglePackage ).to.equal( generateChangelogForSinglePackage ); } ); } ); describe( 'generateChangelogForMonoRepository()', () => { it( 'should be a function', () => { - expect( index.generateChangelogForMonoRepository ).to.be.a( 'function' ); + expect( generateChangelogForMonoRepository ).to.be.a( 'function' ); + expect( index.generateChangelogForMonoRepository ).to.equal( generateChangelogForMonoRepository ); } ); } ); describe( 'updateDependencies()', () => { it( 'should be a function', () => { - expect( index.updateDependencies ).to.be.a( 'function' ); + expect( updateDependencies ).to.be.a( 'function' ); + expect( index.updateDependencies ).to.equal( updateDependencies ); } ); } ); describe( 'commitAndTag()', () => { it( 'should be a function', () => { - expect( index.commitAndTag ).to.be.a( 'function' ); + expect( commitAndTag ).to.be.a( 'function' ); + expect( index.commitAndTag ).to.equal( commitAndTag ); } ); } ); describe( 'createGithubRelease()', () => { it( 'should be a function', () => { - expect( index.createGithubRelease ).to.be.a( 'function' ); + expect( createGithubRelease ).to.be.a( 'function' ); + expect( index.createGithubRelease ).to.equal( createGithubRelease ); } ); } ); describe( 'reassignNpmTags()', () => { it( 'should be a function', () => { - expect( index.reassignNpmTags ).to.be.a( 'function' ); + expect( reassignNpmTags ).to.be.a( 'function' ); + expect( index.reassignNpmTags ).to.equal( reassignNpmTags ); } ); } ); describe( 'prepareRepository()', () => { it( 'should be a function', () => { - expect( index.prepareRepository ).to.be.a( 'function' ); + expect( prepareRepository ).to.be.a( 'function' ); + expect( index.prepareRepository ).to.equal( prepareRepository ); } ); } ); describe( 'push()', () => { it( 'should be a function', () => { - expect( index.push ).to.be.a( 'function' ); + expect( push ).to.be.a( 'function' ); + expect( index.push ).to.equal( push ); } ); } ); describe( 'publishPackages()', () => { it( 'should be a function', () => { - expect( index.publishPackages ).to.be.a( 'function' ); + expect( publishPackages ).to.be.a( 'function' ); + expect( index.publishPackages ).to.equal( publishPackages ); } ); } ); describe( 'updateVersions()', () => { it( 'should be a function', () => { - expect( index.updateVersions ).to.be.a( 'function' ); + expect( updateVersions ).to.be.a( 'function' ); + expect( index.updateVersions ).to.equal( updateVersions ); } ); } ); describe( 'cleanUpPackages()', () => { it( 'should be a function', () => { - expect( index.cleanUpPackages ).to.be.a( 'function' ); + expect( cleanUpPackages ).to.be.a( 'function' ); + expect( index.cleanUpPackages ).to.equal( cleanUpPackages ); } ); } ); describe( 'getLastFromChangelog()', () => { it( 'should be a function', () => { - expect( index.getLastFromChangelog ).to.be.a( 'function' ); + expect( getLastFromChangelog ).to.be.a( 'function' ); + expect( index.getLastFromChangelog ).to.equal( getLastFromChangelog ); } ); } ); describe( 'getCurrent()', () => { it( 'should be a function', () => { - expect( index.getCurrent ).to.be.a( 'function' ); + expect( getCurrent ).to.be.a( 'function' ); + expect( index.getCurrent ).to.equal( getCurrent ); } ); } ); describe( 'getLastPreRelease()', () => { it( 'should be a function', () => { - expect( index.getLastPreRelease ).to.be.a( 'function' ); + expect( getLastPreRelease ).to.be.a( 'function' ); + expect( index.getLastPreRelease ).to.equal( getLastPreRelease ); } ); } ); describe( 'getNextPreRelease()', () => { it( 'should be a function', () => { - expect( index.getNextPreRelease ).to.be.a( 'function' ); + expect( getNextPreRelease ).to.be.a( 'function' ); + expect( index.getNextPreRelease ).to.equal( getNextPreRelease ); } ); } ); describe( 'getLastNightly()', () => { it( 'should be a function', () => { - expect( index.getLastNightly ).to.be.a( 'function' ); + expect( getLastNightly ).to.be.a( 'function' ); + expect( index.getLastNightly ).to.equal( getLastNightly ); } ); } ); describe( 'getNextNightly()', () => { it( 'should be a function', () => { - expect( index.getNextNightly ).to.be.a( 'function' ); + expect( getNextNightly ).to.be.a( 'function' ); + expect( index.getNextNightly ).to.equal( getNextNightly ); } ); } ); describe( 'getLastTagFromGit()', () => { it( 'should be a function', () => { - expect( index.getLastTagFromGit ).to.be.a( 'function' ); + expect( getLastTagFromGit ).to.be.a( 'function' ); + expect( index.getLastTagFromGit ).to.equal( getLastTagFromGit ); } ); } ); describe( 'getNpmTagFromVersion()', () => { it( 'should be a function', () => { - expect( index.getNpmTagFromVersion ).to.be.a( 'function' ); + expect( getNpmTagFromVersion ).to.be.a( 'function' ); + expect( index.getNpmTagFromVersion ).to.equal( getNpmTagFromVersion ); } ); } ); describe( 'getChangesForVersion()', () => { it( 'should be a function', () => { - expect( index.getChangesForVersion ).to.be.a( 'function' ); + expect( getChangesForVersion ).to.be.a( 'function' ); + expect( index.getChangesForVersion ).to.equal( getChangesForVersion ); } ); } ); describe( 'getChangelog()', () => { it( 'should be a function', () => { - expect( index.getChangelog ).to.be.a( 'function' ); + expect( getChangelog ).to.be.a( 'function' ); + expect( index.getChangelog ).to.equal( getChangelog ); } ); } ); describe( 'saveChangelog()', () => { it( 'should be a function', () => { - expect( index.saveChangelog ).to.be.a( 'function' ); + expect( saveChangelog ).to.be.a( 'function' ); + expect( index.saveChangelog ).to.equal( saveChangelog ); } ); } ); describe( 'executeInParallel()', () => { it( 'should be a function', () => { - expect( index.executeInParallel ).to.be.a( 'function' ); + expect( executeInParallel ).to.be.a( 'function' ); + expect( index.executeInParallel ).to.equal( executeInParallel ); } ); } ); describe( 'validateRepositoryToRelease()', () => { it( 'should be a function', () => { - expect( index.validateRepositoryToRelease ).to.be.a( 'function' ); + expect( validateRepositoryToRelease ).to.be.a( 'function' ); + expect( index.validateRepositoryToRelease ).to.equal( validateRepositoryToRelease ); } ); } ); describe( 'checkVersionAvailability()', () => { it( 'should be a function', () => { - expect( index.checkVersionAvailability ).to.be.a( 'function' ); + expect( checkVersionAvailability ).to.be.a( 'function' ); + expect( index.checkVersionAvailability ).to.equal( checkVersionAvailability ); } ); } ); describe( 'isVersionPublishableForTag()', () => { it( 'should be a function', () => { - expect( index.isVersionPublishableForTag ).to.be.a( 'function' ); + expect( isVersionPublishableForTag ).to.be.a( 'function' ); + expect( index.isVersionPublishableForTag ).to.equal( isVersionPublishableForTag ); + } ); + } ); + + describe( 'verifyPackagesPublishedCorrectly()', () => { + it( 'should be a function', () => { + expect( verifyPackagesPublishedCorrectly ).to.be.a( 'function' ); + expect( index.verifyPackagesPublishedCorrectly ).to.equal( verifyPackagesPublishedCorrectly ); } ); } ); } ); From ed4f770d028ce9d8e3d3e674d6a72531b4a5c515 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 6 Sep 2024 12:29:46 +0200 Subject: [PATCH 018/172] More tests. --- .../tests/templates/commit.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/templates/commit.js b/packages/ckeditor5-dev-release-tools/tests/templates/commit.js index 1fac7ac82..d1535b94f 100644 --- a/packages/ckeditor5-dev-release-tools/tests/templates/commit.js +++ b/packages/ckeditor5-dev-release-tools/tests/templates/commit.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const handlebars = require( 'handlebars' ); -const expect = require( 'chai' ).expect; +import { beforeEach, describe, expect, it } from 'vitest'; +import fs from 'fs'; +import path from 'upath'; +import handlebars from 'handlebars'; const templatePath = path.resolve( __dirname, '..', '..', 'lib', 'templates', 'commit.hbs' ); const templateContent = fs.readFileSync( templatePath, 'utf-8' ); From 2bc96e9b3e527be80de50a72272599c08c414dd4 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 6 Sep 2024 12:39:07 +0200 Subject: [PATCH 019/172] ESM requires an extension in a path. --- .eslintrc.js | 9 ++++-- .../lib/tasks/creategithubrelease.js | 2 +- .../lib/tasks/publishpackages.js | 12 ++++---- .../lib/tasks/updateversions.js | 2 +- .../tasks/verifypackagespublishedcorrectly.js | 2 +- .../lib/utils/abortcontroller.js | 9 ++---- .../tests/tasks/cleanuppackages.js | 2 +- .../tests/tasks/creategithubrelease.js | 4 +-- .../tests/tasks/preparerepository.js | 2 +- .../tests/tasks/publishpackages.js | 28 +++++++++---------- .../tests/tasks/push.js | 2 +- .../tests/tasks/reassignnpmtags.js | 6 ++-- .../tests/tasks/updatedependencies.js | 2 +- .../tests/tasks/updateversions.js | 4 +-- .../tasks/verifypackagespublishedcorrectly.js | 4 +-- 15 files changed, 44 insertions(+), 46 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 22527e8f2..4fcdcae6d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,7 +17,6 @@ module.exports = { ], rules: { 'no-console': 'off', - 'ckeditor5-rules/require-file-extensions-in-imports': 'off', 'ckeditor5-rules/license-header': [ 'error', { headerLines: [ '/**', @@ -25,7 +24,13 @@ module.exports = { ' * For licensing, see LICENSE.md.', ' */' ] - } ] + } ], + 'ckeditor5-rules/require-file-extensions-in-imports': [ + 'error', + { + extensions: [ '.ts', '.js', '.json' ] + } + ] }, overrides: [ { diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js index e772d5cc4..eaf7b5602 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js @@ -5,7 +5,7 @@ import { Octokit } from '@octokit/rest'; import semver from 'semver'; -import transformCommitUtils from '../utils/transformcommitutils'; +import transformCommitUtils from '../utils/transformcommitutils.js'; const { getRepositoryUrl } = transformCommitUtils; diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js index b4c3c8db9..6b4a3891c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js @@ -5,12 +5,12 @@ import upath from 'upath'; import { glob } from 'glob'; -import assertNpmAuthorization from '../utils/assertnpmauthorization'; -import assertPackages from '../utils/assertpackages'; -import assertNpmTag from '../utils/assertnpmtag'; -import assertFilesToPublish from '../utils/assertfilestopublish'; -import executeInParallel from '../utils/executeinparallel'; -import publishPackageOnNpmCallback from '../utils/publishpackageonnpmcallback'; +import assertNpmAuthorization from '../utils/assertnpmauthorization.js'; +import assertPackages from '../utils/assertpackages.js'; +import assertNpmTag from '../utils/assertnpmtag.js'; +import assertFilesToPublish from '../utils/assertfilestopublish.js'; +import executeInParallel from '../utils/executeinparallel.js'; +import publishPackageOnNpmCallback from '../utils/publishpackageonnpmcallback.js'; /** * The purpose of the script is to validate the packages prepared for the release and then release them on npm. diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js index adaa891a5..30066e4a4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js @@ -7,7 +7,7 @@ import { glob } from 'glob'; import fs from 'fs-extra'; import semver from 'semver'; import { normalizeTrim, toUnix, dirname, join } from 'upath'; -import checkVersionAvailability from '../utils/checkversionavailability'; +import checkVersionAvailability from '../utils/checkversionavailability.js'; /** * The purpose of the script is to update the version of a root package found in the current working diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js index 80eebf31b..495d81e95 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js @@ -6,7 +6,7 @@ import upath from 'upath'; import { glob } from 'glob'; import fs from 'fs-extra'; -import checkVersionAvailability from '../utils/checkversionavailability'; +import checkVersionAvailability from '../utils/checkversionavailability.js'; /** * Npm sometimes throws incorrect error 409 while publishing, while the package uploads correctly. diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js b/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js index fbda9ad24..ce4895eba 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js @@ -8,7 +8,7 @@ * * @returns {AbortController} */ -function registerAbortController() { +export function registerAbortController() { const abortController = new AbortController(); const listener = () => { @@ -29,15 +29,10 @@ function registerAbortController() { * * @param {AbortController} abortController */ -function deregisterAbortController( abortController ) { +export function deregisterAbortController( abortController ) { if ( !abortController || !abortController._listener ) { return; } process.removeListener( 'SIGINT', abortController._listener ); } - -export { - registerAbortController, - deregisterAbortController -}; diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js index 64e1a6495..6b3d3f6b8 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/cleanuppackages.js @@ -33,7 +33,7 @@ describe( 'cleanUpPackages()', () => { ...( await import( 'fs-extra' ) ).default }; - cleanUpPackages = ( await import( '../../lib/tasks/cleanuppackages' ) ).default; + cleanUpPackages = ( await import( '../../lib/tasks/cleanuppackages.js' ) ).default; } ); afterEach( () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js index 4c28d38a5..c175293ea 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js @@ -4,8 +4,8 @@ */ import { beforeEach, describe, expect, it, vi } from 'vitest'; -import createGithubRelease from '../../lib/tasks/creategithubrelease'; -import transformCommitUtils from '../../lib/utils/transformcommitutils'; +import createGithubRelease from '../../lib/tasks/creategithubrelease.js'; +import transformCommitUtils from '../../lib/utils/transformcommitutils.js'; const stubs = vi.hoisted( () => ( { constructor: vi.fn(), diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js index e379c8647..bd7317204 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js @@ -6,7 +6,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import fs from 'fs-extra'; import { glob } from 'glob'; -import prepareRepository from '../../lib/tasks/preparerepository'; +import prepareRepository from '../../lib/tasks/preparerepository.js'; vi.mock( 'fs-extra' ); vi.mock( 'glob' ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js index c5fbd6588..d44cb7d71 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js @@ -3,26 +3,24 @@ * For licensing, see LICENSE.md. */ -'use strict'; - import { beforeEach, describe, expect, it, vi } from 'vitest'; import upath from 'upath'; import { glob } from 'glob'; -import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization'; -import assertPackages from '../../lib/utils/assertpackages'; -import assertNpmTag from '../../lib/utils/assertnpmtag'; -import assertFilesToPublish from '../../lib/utils/assertfilestopublish'; -import executeInParallel from '../../lib/utils/executeinparallel'; -import publishPackageOnNpmCallback from '../../lib/utils/publishpackageonnpmcallback'; -import publishPackages from '../../lib/tasks/publishpackages'; +import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization.js'; +import assertPackages from '../../lib/utils/assertpackages.js'; +import assertNpmTag from '../../lib/utils/assertnpmtag.js'; +import assertFilesToPublish from '../../lib/utils/assertfilestopublish.js'; +import executeInParallel from '../../lib/utils/executeinparallel.js'; +import publishPackageOnNpmCallback from '../../lib/utils/publishpackageonnpmcallback.js'; +import publishPackages from '../../lib/tasks/publishpackages.js'; vi.mock( 'glob' ); -vi.mock( '../../lib/utils/assertnpmauthorization' ); -vi.mock( '../../lib/utils/assertpackages' ); -vi.mock( '../../lib/utils/assertnpmtag' ); -vi.mock( '../../lib/utils/assertfilestopublish' ); -vi.mock( '../../lib/utils/executeinparallel' ); -vi.mock( '../../lib/utils/publishpackageonnpmcallback' ); +vi.mock( '../../lib/utils/assertnpmauthorization.js' ); +vi.mock( '../../lib/utils/assertpackages.js' ); +vi.mock( '../../lib/utils/assertnpmtag.js' ); +vi.mock( '../../lib/utils/assertfilestopublish.js' ); +vi.mock( '../../lib/utils/executeinparallel.js' ); +vi.mock( '../../lib/utils/publishpackageonnpmcallback.js' ); describe( 'publishPackages()', () => { beforeEach( () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/push.js b/packages/ckeditor5-dev-release-tools/tests/tasks/push.js index 7f9fd5111..fbed66b09 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/push.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/push.js @@ -8,7 +8,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; import shellEscape from 'shell-escape'; -import push from '../../lib/tasks/push'; +import push from '../../lib/tasks/push.js'; vi.mock( '@ckeditor/ckeditor5-dev-utils' ); vi.mock( 'shell-escape' ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js b/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js index f5e8fb636..addc44512 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/reassignnpmtags.js @@ -10,8 +10,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import columns from 'cli-columns'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; import shellEscape from 'shell-escape'; -import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization'; -import reassignNpmTags from '../../lib/tasks/reassignnpmtags'; +import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization.js'; +import reassignNpmTags from '../../lib/tasks/reassignnpmtags.js'; const stubs = vi.hoisted( () => { const values = { @@ -61,7 +61,7 @@ vi.mock( 'chalk', () => ( { default: stubs.chalk } ) ); vi.mock( 'shell-escape' ); -vi.mock( '../../lib/utils/assertnpmauthorization' ); +vi.mock( '../../lib/utils/assertnpmauthorization.js' ); describe( 'reassignNpmTags()', () => { beforeEach( () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js b/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js index aba41c0c2..9574d1762 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/updatedependencies.js @@ -7,7 +7,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import fs from 'fs-extra'; import { glob } from 'glob'; import upath from 'upath'; -import updateDependencies from '../../lib/tasks/updatedependencies'; +import updateDependencies from '../../lib/tasks/updatedependencies.js'; vi.mock( 'fs-extra' ); vi.mock( 'glob' ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js b/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js index bdd54508f..3eb93c3f6 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/updateversions.js @@ -9,12 +9,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { glob } from 'glob'; import upath from 'upath'; import fs from 'fs-extra'; -import checkVersionAvailability from '../../lib/utils/checkversionavailability'; +import checkVersionAvailability from '../../lib/utils/checkversionavailability.js'; import updateVersions from '../../lib/tasks/updateversions.js'; vi.mock( 'fs-extra' ); vi.mock( 'glob' ); -vi.mock( '../../lib/utils/checkversionavailability' ); +vi.mock( '../../lib/utils/checkversionavailability.js' ); describe( 'updateVersions()', () => { beforeEach( () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js b/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js index 2d701fcd5..afbf6c14b 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/verifypackagespublishedcorrectly.js @@ -8,8 +8,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { glob } from 'glob'; import fs from 'fs-extra'; -import verifyPackagesPublishedCorrectly from '../../lib/tasks/verifypackagespublishedcorrectly'; -import checkVersionAvailability from '../../lib/utils/checkversionavailability'; +import verifyPackagesPublishedCorrectly from '../../lib/tasks/verifypackagespublishedcorrectly.js'; +import checkVersionAvailability from '../../lib/utils/checkversionavailability.js'; vi.mock( 'fs-extra' ); vi.mock( '../../lib/utils/checkversionavailability' ); From 05b138aa762ab2489c41939b9ff8f0bf3070d28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Fri, 6 Sep 2024 13:33:42 +0200 Subject: [PATCH 020/172] Added extensions in local file imports. --- packages/ckeditor5-dev-docs/lib/buildtypedoc.js | 2 +- packages/ckeditor5-dev-docs/lib/index.js | 2 +- packages/ckeditor5-dev-docs/lib/validators/index.js | 10 +++++----- .../tests/validators/fires-validator/index.js | 4 ++-- .../fixtures/inheritance/derivedclass.ts | 2 +- .../tests/validators/link-validator/index.js | 4 ++-- .../tests/validators/module-validator/index.js | 4 ++-- .../tests/validators/overloads-validator/index.js | 4 ++-- .../tests/validators/see-validator/index.js | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js b/packages/ckeditor5-dev-docs/lib/buildtypedoc.js index 33f25fc88..35d9dfc89 100644 --- a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js +++ b/packages/ckeditor5-dev-docs/lib/buildtypedoc.js @@ -7,7 +7,7 @@ import glob from 'fast-glob'; import TypeDoc from 'typedoc'; import { plugins } from '@ckeditor/typedoc-plugins'; -import validators from './validators'; +import validators from './validators/index.js'; /** * Builds CKEditor 5 documentation using `typedoc`. diff --git a/packages/ckeditor5-dev-docs/lib/index.js b/packages/ckeditor5-dev-docs/lib/index.js index 17abd1091..d3947d7ce 100644 --- a/packages/ckeditor5-dev-docs/lib/index.js +++ b/packages/ckeditor5-dev-docs/lib/index.js @@ -3,4 +3,4 @@ * For licensing, see LICENSE.md. */ -export { default as build } from './build'; +export { default as build } from './build.js'; diff --git a/packages/ckeditor5-dev-docs/lib/validators/index.js b/packages/ckeditor5-dev-docs/lib/validators/index.js index 0e5f76b02..aa51c1cae 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/index.js @@ -4,11 +4,11 @@ */ import { utils } from '@ckeditor/typedoc-plugins'; -import seeValidator from './see-validator'; -import linkValidator from './link-validator'; -import firesValidator from './fires-validator'; -import moduleValidator from './module-validator'; -import overloadsValidator from './overloads-validator'; +import seeValidator from './see-validator/index.js'; +import linkValidator from './link-validator/index.js'; +import firesValidator from './fires-validator/index.js'; +import moduleValidator from './module-validator/index.js'; +import overloadsValidator from './overloads-validator/index.js'; /** * Validates the CKEditor 5 documentation. diff --git a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js index 960192a8b..b6fb52994 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js @@ -4,9 +4,9 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils'; +import testUtils from '../../utils.js'; -import build from '../../../lib/buildtypedoc'; +import build from '../../../lib/buildtypedoc.js'; const stubs = vi.hoisted( () => { return { diff --git a/packages/ckeditor5-dev-docs/tests/validators/link-validator/fixtures/inheritance/derivedclass.ts b/packages/ckeditor5-dev-docs/tests/validators/link-validator/fixtures/inheritance/derivedclass.ts index bdfd0c730..4831fa2ee 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/link-validator/fixtures/inheritance/derivedclass.ts +++ b/packages/ckeditor5-dev-docs/tests/validators/link-validator/fixtures/inheritance/derivedclass.ts @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import BaseCLass from './baseclass'; +import BaseCLass from './baseclass.js'; /** * @module fixtures/inheritance/derivedclass diff --git a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js index cf594d4dd..0b0eca9aa 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js @@ -4,9 +4,9 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils'; +import testUtils from '../../utils.js'; -import build from '../../../lib/buildtypedoc'; +import build from '../../../lib/buildtypedoc.js'; const stubs = vi.hoisted( () => { return { diff --git a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js index 23c15cd37..0fdd4749c 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js @@ -4,9 +4,9 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils'; +import testUtils from '../../utils.js'; -import build from '../../../lib/buildtypedoc'; +import build from '../../../lib/buildtypedoc.js'; const stubs = vi.hoisted( () => { return { diff --git a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js index 176bfacd0..cb960565b 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js @@ -4,9 +4,9 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import testUtils from '../../utils'; +import testUtils from '../../utils.js'; -import build from '../../../lib/buildtypedoc'; +import build from '../../../lib/buildtypedoc.js'; const stubs = vi.hoisted( () => { return { diff --git a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js index 352847267..2448da4bb 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js @@ -4,9 +4,9 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils'; +import testUtils from '../../utils.js'; -import build from '../../../lib/buildtypedoc'; +import build from '../../../lib/buildtypedoc.js'; const stubs = vi.hoisted( () => { return { From 3531b1f0934fa17b4155630e856791d8a6acd0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Fri, 6 Sep 2024 13:47:35 +0200 Subject: [PATCH 021/172] Added test tools to exclusion in Vitest. --- packages/ckeditor5-dev-docs/tests/{utils.js => _utils.js} | 0 .../tests/validators/fires-validator/index.js | 2 +- .../tests/validators/link-validator/index.js | 2 +- .../tests/validators/module-validator/index.js | 2 +- .../tests/validators/overloads-validator/index.js | 2 +- .../ckeditor5-dev-docs/tests/validators/see-validator/index.js | 2 +- packages/ckeditor5-dev-docs/vitest.config.js | 3 +++ 7 files changed, 8 insertions(+), 5 deletions(-) rename packages/ckeditor5-dev-docs/tests/{utils.js => _utils.js} (100%) diff --git a/packages/ckeditor5-dev-docs/tests/utils.js b/packages/ckeditor5-dev-docs/tests/_utils.js similarity index 100% rename from packages/ckeditor5-dev-docs/tests/utils.js rename to packages/ckeditor5-dev-docs/tests/_utils.js diff --git a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js index b6fb52994..ac5b2c688 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils.js'; +import testUtils from '../../_utils.js'; import build from '../../../lib/buildtypedoc.js'; diff --git a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js index 0b0eca9aa..6f417d1ed 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils.js'; +import testUtils from '../../_utils.js'; import build from '../../../lib/buildtypedoc.js'; diff --git a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js index 0fdd4749c..d1a7a9d71 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils.js'; +import testUtils from '../../_utils.js'; import build from '../../../lib/buildtypedoc.js'; diff --git a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js index cb960565b..3bd540603 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import testUtils from '../../utils.js'; +import testUtils from '../../_utils.js'; import build from '../../../lib/buildtypedoc.js'; diff --git a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js index 2448da4bb..18f0ca300 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import testUtils from '../../utils.js'; +import testUtils from '../../_utils.js'; import build from '../../../lib/buildtypedoc.js'; diff --git a/packages/ckeditor5-dev-docs/vitest.config.js b/packages/ckeditor5-dev-docs/vitest.config.js index 5ad784a28..48441e92f 100644 --- a/packages/ckeditor5-dev-docs/vitest.config.js +++ b/packages/ckeditor5-dev-docs/vitest.config.js @@ -12,6 +12,9 @@ export default defineConfig( { include: [ 'tests/**/*.js' ], + exclude: [ + 'tests/_utils.js' + ], coverage: { provider: 'v8', include: [ From 18cb47d6c6040f7040e04fa1df68596d23160938 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 6 Sep 2024 14:19:51 +0200 Subject: [PATCH 022/172] More and more. --- .../ckeditor5-dev-release-tools/lib/utils/abortcontroller.js | 2 +- .../lib/utils/assertfilestopublish.js | 2 +- packages/ckeditor5-dev-release-tools/vitest.config.js | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js b/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js index ce4895eba..54c9617fe 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/abortcontroller.js @@ -29,7 +29,7 @@ export function registerAbortController() { * * @param {AbortController} abortController */ -export function deregisterAbortController( abortController ) { +export function deregisterAbortController( abortController = undefined ) { if ( !abortController || !abortController._listener ) { return; } diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js index d1e26b6ac..20b94d5f5 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js @@ -14,7 +14,7 @@ import { glob } from 'glob'; * @param {Object.>|null} optionalEntries * @returns {Promise} */ -export default async function assertFilesToPublish( packagePaths, optionalEntries ) { +export default async function assertFilesToPublish( packagePaths, optionalEntries = null ) { const errors = []; for ( const packagePath of packagePaths ) { diff --git a/packages/ckeditor5-dev-release-tools/vitest.config.js b/packages/ckeditor5-dev-release-tools/vitest.config.js index 0f187ad82..17bbb4835 100644 --- a/packages/ckeditor5-dev-release-tools/vitest.config.js +++ b/packages/ckeditor5-dev-release-tools/vitest.config.js @@ -16,11 +16,15 @@ export default defineConfig( { include: [ 'tests/**/*.js' ], + exclude: [ + './tests/_utils/**/*.j' + ], coverage: { provider: 'v8', include: [ 'lib/**' ], + reporter: [ 'text', 'json', 'html', 'lcov' ] } } From 281aaeaf5495ed231373bf646cccb116d2ccc73e Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 6 Sep 2024 14:29:26 +0200 Subject: [PATCH 023/172] Breakpoint. --- .../tests/utils/abortcontroller.js | 127 ++-- .../tests/utils/assertfilestopublish.js | 617 +++++++++--------- 2 files changed, 369 insertions(+), 375 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js b/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js index 22aa90efa..a64809755 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js @@ -3,99 +3,78 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); - -describe( 'dev-release-tools/utils', () => { - describe( 'abortController()', () => { - let abortController, listeners, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - listeners = {}; - - stubs = { - process: { - prependOnceListener: sandbox.stub().callsFake( ( eventName, listener ) => { - listeners[ eventName ] = new Set( [ - listener, - ...listeners[ eventName ] || [] - ] ); - } ), - removeListener: sandbox.stub().callsFake( ( eventName, listener ) => { - if ( listeners[ eventName ] ) { - listeners[ eventName ].delete( listener ); - } - } ) - } - }; - - sandbox.stub( process, 'prependOnceListener' ).callsFake( stubs.process.prependOnceListener ); - sandbox.stub( process, 'removeListener' ).callsFake( stubs.process.removeListener ); - - abortController = require( '../../lib/utils/abortcontroller' ); - } ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { deregisterAbortController, registerAbortController } from '../../lib/utils/abortcontroller.js'; - afterEach( () => { - sandbox.restore(); - } ); +vi.stubGlobal( 'process', { + prependOnceListener: vi.fn(), + removeListener: vi.fn() +} ); - it( 'should return AbortController instance', () => { - const abortControllerInstance = abortController.registerAbortController(); +describe( 'abortController()', () => { + let listeners; - expect( abortControllerInstance ).to.be.instanceof( global.AbortController ); + beforeEach( () => { + listeners = {}; + + vi.mocked( process ).prependOnceListener.mockImplementation( ( eventName, listener ) => { + listeners[ eventName ] = new Set( [ + listener, + ...listeners[ eventName ] || [] + ] ); + } ); + vi.mocked( process ).removeListener.mockImplementation( ( eventName, listener ) => { + if ( listeners[ eventName ] ) { + listeners[ eventName ].delete( listener ); + } } ); + } ); - it( 'should store listener in internal property for further use', () => { - const abortControllerInstance = abortController.registerAbortController(); + it( 'should return AbortController instance', () => { + const abortControllerInstance = registerAbortController(); - expect( abortControllerInstance._listener ).to.be.a( 'function' ); - } ); + expect( abortControllerInstance ).to.be.instanceof( global.AbortController ); + } ); - it( 'should register listener on SIGINT event', () => { - const abortControllerInstance = abortController.registerAbortController(); + it( 'should store listener in internal property for further use', () => { + const abortControllerInstance = registerAbortController(); - expect( stubs.process.prependOnceListener.callCount ).to.equal( 1 ); - expect( stubs.process.prependOnceListener.firstCall.args[ 0 ] ).to.equal( 'SIGINT' ); - expect( stubs.process.prependOnceListener.firstCall.args[ 1 ] ).to.equal( abortControllerInstance._listener ); - } ); + expect( abortControllerInstance._listener ).to.be.a( 'function' ); + } ); - it( 'should call abort method on SIGINT event', () => { - const abortControllerInstance = abortController.registerAbortController(); + it( 'should register listener on SIGINT event', () => { + const abortControllerInstance = registerAbortController(); - sandbox.spy( abortControllerInstance, 'abort' ); + expect( vi.mocked( process ).prependOnceListener ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', abortControllerInstance._listener ); + } ); - listeners.SIGINT.forEach( listener => listener() ); + it( 'should call abort method on SIGINT event', () => { + const abortControllerInstance = registerAbortController(); - expect( abortControllerInstance.abort.callCount ).to.equal( 1 ); - expect( abortControllerInstance.abort.firstCall.args[ 0 ] ).to.equal( 'SIGINT' ); - } ); + vi.spyOn( abortControllerInstance, 'abort' ); - it( 'should not deregister listener if AbortController instance is not set', () => { - abortController.deregisterAbortController(); + listeners.SIGINT.forEach( listener => listener() ); - expect( stubs.process.removeListener.callCount ).to.equal( 0 ); - } ); + expect( abortControllerInstance.abort ).toHaveBeenCalledExactlyOnceWith( 'SIGINT' ); + } ); - it( 'should not deregister listener if AbortController instance is not registered', () => { - const abortControllerInstance = new AbortController(); + it( 'should not deregister listener if AbortController instance is not set', () => { + deregisterAbortController(); + expect( vi.mocked( process ).removeListener ).not.toHaveBeenCalled(); + } ); - abortController.deregisterAbortController( abortControllerInstance ); + it( 'should not deregister listener if AbortController instance is not registered', () => { + const abortControllerInstance = new AbortController(); + deregisterAbortController( abortControllerInstance ); - expect( stubs.process.removeListener.callCount ).to.equal( 0 ); - } ); + expect( vi.mocked( process ).removeListener ).not.toHaveBeenCalled(); + } ); - it( 'should deregister listener if AbortController instance is registered', () => { - const abortControllerInstance = abortController.registerAbortController(); + it( 'should deregister listener if AbortController instance is registered', () => { + const abortControllerInstance = registerAbortController(); - abortController.deregisterAbortController( abortControllerInstance ); + deregisterAbortController( abortControllerInstance ); - expect( stubs.process.removeListener.callCount ).to.equal( 1 ); - expect( stubs.process.removeListener.firstCall.args[ 0 ] ).to.equal( 'SIGINT' ); - expect( stubs.process.removeListener.firstCall.args[ 1 ] ).to.equal( abortControllerInstance._listener ); - } ); + expect( vi.mocked( process ).removeListener ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', abortControllerInstance._listener ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js b/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js index a9474e58d..1039fd13e 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js @@ -3,366 +3,381 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import assertFilesToPublish from '../../lib/utils/assertfilestopublish.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'fs-extra' ); +vi.mock( 'glob' ); -describe( 'dev-release-tools/utils', () => { - describe( 'assertFilesToPublish()', () => { - let assertFilesToPublish, sandbox, stubs; +describe( 'assertFilesToPublish()', () => { + it( 'should do nothing if list of packages is empty', async () => { + await assertFilesToPublish( [] ); - beforeEach( () => { - sandbox = sinon.createSandbox(); + expect( vi.mocked( fs ).readJson ).not.toHaveBeenCalled(); + expect( vi.mocked( glob ) ).not.toHaveBeenCalled(); + } ); - stubs = { - fs: { - readJson: sandbox.stub() - }, - glob: { - glob: sandbox.stub() - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); + it( 'should read `package.json` from each package', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( {} ); - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( 'glob', stubs.glob ); + await assertFilesToPublish( [ 'ckeditor5-foo', 'ckeditor5-bar' ] ); - assertFilesToPublish = require( '../../lib/utils/assertfilestopublish' ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( 'ckeditor5-foo/package.json' ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( 'ckeditor5-bar/package.json' ); + } ); + + it( 'should not check any file if `package.json` does not contain required files', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo' } ); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); + await assertFilesToPublish( [ 'ckeditor5-foo' ] ); + + expect( vi.mocked( glob ) ).not.toHaveBeenCalled(); + } ); + + it( 'should not throw if all files from `files` field exist', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + files: [ + 'src', + 'README.md' + ] } ); - it( 'should do nothing if list of packages is empty', () => { - return assertFilesToPublish( [] ) - .then( () => { - expect( stubs.fs.readJson.called ).to.equal( false ); - expect( stubs.glob.glob.called ).to.equal( false ); - } ); + vi.mocked( glob ).mockImplementation( input => { + if ( input[ 0 ] === 'src' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } + + return Promise.resolve( [ 'README.md' ] ); } ); - it( 'should read `package.json` from each package', () => { - stubs.fs.readJson.resolves( {} ); + await assertFilesToPublish( [ 'ckeditor5-foo' ] ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'src', 'src/**' ], + expect.objectContaining( { + cwd: 'ckeditor5-foo', + dot: true, + nodir: true + } ) + ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'README.md', 'README.md/**' ], + expect.objectContaining( { + cwd: 'ckeditor5-foo', + dot: true, + nodir: true + } ) + ); + } ); - return assertFilesToPublish( [ 'ckeditor5-foo', 'ckeditor5-bar' ] ) - .then( () => { - expect( stubs.fs.readJson.callCount ).to.equal( 2 ); - expect( stubs.fs.readJson.firstCall.args[ 0 ] ).to.equal( 'ckeditor5-foo/package.json' ); - expect( stubs.fs.readJson.secondCall.args[ 0 ] ).to.equal( 'ckeditor5-bar/package.json' ); - } ); + it( 'should not throw if all files from `files` field exist except the optional ones (for package)', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + files: [ + 'src', + 'README.md' + ] } ); - it( 'should not check any file if `package.json` does not contain required files', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo' - } ); + vi.mocked( glob ).mockImplementation( input => { + if ( input[ 0 ] === 'src' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( () => { - expect( stubs.glob.glob.called ).to.equal( false ); - } ); + return Promise.resolve( [ 'README.md' ] ); } ); - it( 'should not throw if all files from `files` field exist', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - files: [ - 'src', - 'README.md' - ] - } ); + const optionalEntries = { + 'ckeditor5-foo': [ + 'README.md' + ] + }; + + await assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'src', 'src/**' ], + expect.objectContaining( { + cwd: 'ckeditor5-foo', + dot: true, + nodir: true + } ) + ); + } ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 2 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'cwd', 'ckeditor5-foo' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'dot', true ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'nodir', true ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 1 ] ).to.have.property( 'cwd', 'ckeditor5-foo' ); - expect( stubs.glob.glob.secondCall.args[ 1 ] ).to.have.property( 'dot', true ); - expect( stubs.glob.glob.secondCall.args[ 1 ] ).to.have.property( 'nodir', true ); - } ); + it( 'should not throw if all files from `files` field exist except the optional ones (for all packages)', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + files: [ + 'src', + 'README.md' + ] } ); - it( 'should not throw if all files from `files` field exist except the optional ones (for package)', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - files: [ - 'src', - 'README.md' - ] - } ); + vi.mocked( glob ).mockImplementation( input => { + if ( input[ 0 ] === 'src' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); + return Promise.resolve( [ ] ); + } ); - const optionalEntries = { - 'ckeditor5-foo': [ - 'README.md' - ] - }; + const optionalEntries = { + 'default': [ + 'README.md' + ] + }; + + await assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + [ 'src', 'src/**' ], + expect.objectContaining( { + cwd: 'ckeditor5-foo', + dot: true, + nodir: true + } ) + ); + } ); - return assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - } ); + it( 'should prefer own configuration for optional entries', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + files: [ + 'src', + 'README.md' + ] } ); - it( 'should not throw if all files from `files` field exist except the optional ones (for all packages)', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - files: [ - 'src', - 'README.md' - ] - } ); + vi.mocked( glob ).mockImplementation( input => { + if ( input[ 0 ] === 'src' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } + + return Promise.resolve( [ 'README.md' ] ); + } ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [] ); + const optionalEntries = { + // Make all entries as required for the "ckeditor5-foo" package. + 'ckeditor5-foo': [], + 'default': [ + 'README.md' + ] + }; + + await assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'src', 'src/**' ], + expect.objectContaining( { + cwd: 'ckeditor5-foo', + dot: true, + nodir: true + } ) + ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'README.md', 'README.md/**' ], + expect.objectContaining( { + cwd: 'ckeditor5-foo', + dot: true, + nodir: true + } ) + ); + } ); - const optionalEntries = { - 'default': [ - 'README.md' - ] - }; + it( 'should consider entry as required if there are not matches in optional entries', () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + files: [ + 'src', + 'README.md' + ] + } ); - return assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - } ); + vi.mocked( glob ).mockImplementation( input => { + if ( input[ 0 ] === 'src' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } + + return Promise.resolve( [ 'README.md' ] ); } ); - it( 'should prefer own configuration for optional entries', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - files: [ - 'src', - 'README.md' - ] + const optionalEntries = { + 'ckeditor5-bar': [ + 'src', + 'README.md' + ] + }; + + return assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ) + .then( () => { + expect( stubs.glob.glob.callCount ).to.equal( 2 ); + expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); + expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); } ); + } ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); + it( 'should not throw if `main` file exists', () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + main: 'src/index.ts', + files: [ + 'src', + 'README.md' + ] + } ); - const optionalEntries = { - // Make all entries as required for the "ckeditor5-foo" package. - 'ckeditor5-foo': [], - 'default': [ - 'README.md' - ] - }; + stubs.glob.glob + .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) + .withArgs( [ 'src/index.ts', 'src/index.ts/**' ] ).resolves( [ 'src/index.ts' ] ) + .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); + + return assertFilesToPublish( [ 'ckeditor5-foo' ] ) + .then( () => { + expect( stubs.glob.glob.callCount ).to.equal( 3 ); + expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src/index.ts', 'src/index.ts/**' ] ); + expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); + expect( stubs.glob.glob.thirdCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); + } ); + } ); - return assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 2 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); - } ); + it( 'should not throw if `types` file exists', () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + types: 'src/index.d.ts', + files: [ + 'src', + 'README.md' + ] } ); - it( 'should consider entry as required if there are not matches in optional entries', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - files: [ - 'src', - 'README.md' - ] + stubs.glob.glob + .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) + .withArgs( [ 'src/index.d.ts', 'src/index.d.ts/**' ] ).resolves( [ 'src/index.d.ts' ] ) + .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); + + return assertFilesToPublish( [ 'ckeditor5-foo' ] ) + .then( () => { + expect( stubs.glob.glob.callCount ).to.equal( 3 ); + expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src/index.d.ts', 'src/index.d.ts/**' ] ); + expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); + expect( stubs.glob.glob.thirdCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); } ); + } ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); + it( 'should throw if not all files from `files` field exist', () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + files: [ + 'src', + 'LICENSE.md', + 'README.md' + ] + } ); - const optionalEntries = { - 'ckeditor5-bar': [ - 'src', - 'README.md' - ] - }; + stubs.glob.glob.resolves( [] ); - return assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 2 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); + return assertFilesToPublish( [ 'ckeditor5-foo' ] ) + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + error => { + expect( error ).to.be.an( 'Error' ); + expect( error.message ).to.equal( + 'Missing files in "ckeditor5-foo" package for entries: "src", "LICENSE.md", "README.md"' + ); } ); + } ); + + it( 'should throw if file from `main` field does not exist', () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + main: 'src/index.ts' } ); - it( 'should not throw if `main` file exists', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - main: 'src/index.ts', - files: [ - 'src', - 'README.md' - ] - } ); + stubs.glob.glob.resolves( [] ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'src/index.ts', 'src/index.ts/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 3 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src/index.ts', 'src/index.ts/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.thirdCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); + return assertFilesToPublish( [ 'ckeditor5-foo' ] ) + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + error => { + expect( error ).to.be.an( 'Error' ); + expect( error.message ).to.equal( + 'Missing files in "ckeditor5-foo" package for entries: "src/index.ts"' + ); } ); + } ); + + it( 'should throw if file from `types` field does not exist', () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + types: 'src/index.d.ts' } ); - it( 'should not throw if `types` file exists', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - types: 'src/index.d.ts', - files: [ - 'src', - 'README.md' - ] - } ); + stubs.glob.glob.resolves( [] ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'src/index.d.ts', 'src/index.d.ts/**' ] ).resolves( [ 'src/index.d.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 3 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src/index.d.ts', 'src/index.d.ts/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.thirdCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); + return assertFilesToPublish( [ 'ckeditor5-foo' ] ) + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + error => { + expect( error ).to.be.an( 'Error' ); + expect( error.message ).to.equal( + 'Missing files in "ckeditor5-foo" package for entries: "src/index.d.ts"' + ); } ); - } ); + } ); - it( 'should throw if not all files from `files` field exist', () => { - stubs.fs.readJson.resolves( { + it( 'should throw one error for all packages with missing files', () => { + stubs.fs.readJson + .withArgs( 'ckeditor5-foo/package.json' ).resolves( { name: 'ckeditor5-foo', + files: [ + 'src' + ] + } ) + .withArgs( 'ckeditor5-bar/package.json' ).resolves( { + name: 'ckeditor5-bar', files: [ 'src', - 'LICENSE.md', 'README.md' ] - } ); - - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src", "LICENSE.md", "README.md"' - ); - } ); + } ) + .withArgs( 'ckeditor5-baz/package.json' ).resolves( { + name: 'ckeditor5-baz', + files: [ + 'src', + 'LICENSE.md', + 'README.md' + ] } ); - it( 'should throw if file from `main` field does not exist', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - main: 'src/index.ts' - } ); + stubs.glob.glob.resolves( [] ); - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src/index.ts"' - ); - } ); - } ); - - it( 'should throw if file from `types` field does not exist', () => { - stubs.fs.readJson.resolves( { - name: 'ckeditor5-foo', - types: 'src/index.d.ts' - } ); - - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src/index.d.ts"' - ); - } ); - } ); - - it( 'should throw one error for all packages with missing files', () => { - stubs.fs.readJson - .withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - files: [ - 'src' - ] - } ) - .withArgs( 'ckeditor5-bar/package.json' ).resolves( { - name: 'ckeditor5-bar', - files: [ - 'src', - 'README.md' - ] - } ) - .withArgs( 'ckeditor5-baz/package.json' ).resolves( { - name: 'ckeditor5-baz', - files: [ - 'src', - 'LICENSE.md', - 'README.md' - ] + return assertFilesToPublish( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ] ) + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + error => { + expect( error ).to.be.an( 'Error' ); + expect( error.message ).to.equal( + 'Missing files in "ckeditor5-foo" package for entries: "src"\n' + + 'Missing files in "ckeditor5-bar" package for entries: "src", "README.md"\n' + + 'Missing files in "ckeditor5-baz" package for entries: "src", "LICENSE.md", "README.md"' + ); } ); - - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src"\n' + - 'Missing files in "ckeditor5-bar" package for entries: "src", "README.md"\n' + - 'Missing files in "ckeditor5-baz" package for entries: "src", "LICENSE.md", "README.md"' - ); - } ); - } ); } ); } ); From 951a64f8df40a912d8e58e3b2f33e11515d38e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Fri, 6 Sep 2024 14:30:26 +0200 Subject: [PATCH 024/172] Fixed imports from `typedoc-plugins`. --- packages/ckeditor5-dev-docs/lib/build.js | 4 ++-- packages/ckeditor5-dev-docs/lib/buildtypedoc.js | 3 ++- .../lib/validators/fires-validator/index.js | 5 ++++- packages/ckeditor5-dev-docs/lib/validators/index.js | 3 ++- .../lib/validators/link-validator/index.js | 3 ++- .../lib/validators/module-validator/index.js | 3 ++- .../lib/validators/overloads-validator/index.js | 3 ++- .../ckeditor5-dev-docs/lib/validators/see-validator/index.js | 3 ++- 8 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index f7987baac..7fc82c9ab 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -13,9 +13,9 @@ export default async function build( config ) { const type = config.type || 'jsdoc'; if ( type === 'jsdoc' ) { - return import( './buildjsdoc' )( config ); + return ( await import( './buildjsdoc' ) ).default( config ); } else if ( type === 'typedoc' ) { - return import( './buildtypedoc' )( config ); + return ( await import( './buildtypedoc' ) ).default( config ); } else { throw new Error( `Unknown documentation tool (${ type }).` ); } diff --git a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js b/packages/ckeditor5-dev-docs/lib/buildtypedoc.js index 35d9dfc89..e952b2cbd 100644 --- a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js +++ b/packages/ckeditor5-dev-docs/lib/buildtypedoc.js @@ -5,7 +5,7 @@ import glob from 'fast-glob'; import TypeDoc from 'typedoc'; -import { plugins } from '@ckeditor/typedoc-plugins'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; import validators from './validators/index.js'; @@ -16,6 +16,7 @@ import validators from './validators/index.js'; * @returns {Promise} */ export default async function build( config ) { + const { plugins } = typedocPlugins; const sourceFilePatterns = config.sourceFiles.filter( Boolean ); const strictMode = config.strict || false; const extraPlugins = config.extraPlugins || []; diff --git a/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js index 2c55b2528..46f36aaca 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js @@ -4,7 +4,7 @@ */ import { ReflectionKind } from 'typedoc'; -import { utils } from '@ckeditor/typedoc-plugins'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; /** * Validates the output produced by TypeDoc. @@ -15,6 +15,7 @@ import { utils } from '@ckeditor/typedoc-plugins'; * @param {Function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { + const { utils } = typedocPlugins; const reflections = project .getReflectionsByKind( ReflectionKind.Class | ReflectionKind.CallSignature ) .filter( utils.isReflectionValid ); @@ -39,6 +40,8 @@ export default function validate( project, onError ) { } function getIdentifiersFromFiresTag( reflection ) { + const { utils } = typedocPlugins; + if ( !reflection.comment ) { return []; } diff --git a/packages/ckeditor5-dev-docs/lib/validators/index.js b/packages/ckeditor5-dev-docs/lib/validators/index.js index aa51c1cae..90947e59a 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/index.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import { utils } from '@ckeditor/typedoc-plugins'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; import seeValidator from './see-validator/index.js'; import linkValidator from './link-validator/index.js'; import firesValidator from './fires-validator/index.js'; @@ -20,6 +20,7 @@ import overloadsValidator from './overloads-validator/index.js'; */ export default { validate( project, typeDoc, options = {} ) { + const { utils } = typedocPlugins; const validators = [ seeValidator, linkValidator, diff --git a/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js index 02d71a74f..8db19a347 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js @@ -4,7 +4,7 @@ */ import { ReflectionKind } from 'typedoc'; -import { utils } from '@ckeditor/typedoc-plugins'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; /** * Validates the output produced by TypeDoc. @@ -15,6 +15,7 @@ import { utils } from '@ckeditor/typedoc-plugins'; * @param {Function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { + const { utils } = typedocPlugins; const reflections = project.getReflectionsByKind( ReflectionKind.All ).filter( utils.isReflectionValid ); for ( const reflection of reflections ) { diff --git a/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js index 28586eca5..8512b21c4 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js @@ -4,7 +4,7 @@ */ import { ReflectionKind } from 'typedoc'; -import { utils } from '@ckeditor/typedoc-plugins'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; const AUGMENTATION_MODULE_REGEXP = /[^\\/]+[\\/]src[\\/]augmentation/; @@ -17,6 +17,7 @@ const AUGMENTATION_MODULE_REGEXP = /[^\\/]+[\\/]src[\\/]augmentation/; * @param {Function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { + const { utils } = typedocPlugins; const reflections = project.getReflectionsByKind( ReflectionKind.Module ); for ( const reflection of reflections ) { diff --git a/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js index 51949f619..2eaa895a8 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js @@ -4,7 +4,7 @@ */ import { ReflectionKind } from 'typedoc'; -import { utils } from '@ckeditor/typedoc-plugins'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; // The `@label` validator is currently not used. // See: https://github.com/ckeditor/ckeditor5/issues/13591. @@ -20,6 +20,7 @@ import { utils } from '@ckeditor/typedoc-plugins'; * @param {Function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { + const { utils } = typedocPlugins; const kinds = ReflectionKind.Method | ReflectionKind.Constructor | ReflectionKind.Function; const reflections = project.getReflectionsByKind( kinds ).filter( utils.isReflectionValid ); diff --git a/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js index fcc9164e3..0c063851a 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js @@ -4,7 +4,7 @@ */ import { ReflectionKind } from 'typedoc'; -import { utils } from '@ckeditor/typedoc-plugins'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; /** * Validates the output produced by TypeDoc. @@ -15,6 +15,7 @@ import { utils } from '@ckeditor/typedoc-plugins'; * @param {Function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { + const { utils } = typedocPlugins; const reflections = project.getReflectionsByKind( ReflectionKind.All ).filter( utils.isReflectionValid ); for ( const reflection of reflections ) { From e80a89c8a41947ab99d27d2bb61f19d1e2cfb3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Fri, 6 Sep 2024 14:34:23 +0200 Subject: [PATCH 025/172] Added missing extension in imported files. --- packages/ckeditor5-dev-docs/lib/build.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index 7fc82c9ab..dc6932645 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -13,9 +13,9 @@ export default async function build( config ) { const type = config.type || 'jsdoc'; if ( type === 'jsdoc' ) { - return ( await import( './buildjsdoc' ) ).default( config ); + return ( await import( './buildjsdoc.js' ) ).default( config ); } else if ( type === 'typedoc' ) { - return ( await import( './buildtypedoc' ) ).default( config ); + return ( await import( './buildtypedoc.js' ) ).default( config ); } else { throw new Error( `Unknown documentation tool (${ type }).` ); } From 8684a02cd0caf9b8026379aa3cf5a70f2f4f857f Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:21:16 +0200 Subject: [PATCH 026/172] Migrated to ESM. --- .../ckeditor5-dev-stale-bot/bin/stale-bot.js | 18 +- .../bin/utils/createspinner.js | 10 +- .../bin/utils/parsearguments.js | 10 +- .../bin/utils/parseconfig.js | 8 +- .../bin/utils/validateconfig.js | 6 +- .../lib/githubrepository.js | 75 +- .../lib/utils/findstaledate.js | 8 +- .../lib/utils/isissueorpullrequestactive.js | 8 +- .../lib/utils/isissueorpullrequesttoclose.js | 12 +- .../lib/utils/isissueorpullrequesttostale.js | 8 +- .../utils/isissueorpullrequesttounstale.js | 10 +- .../lib/utils/ispendingissuestale.js | 6 +- .../lib/utils/ispendingissuetostale.js | 10 +- .../lib/utils/ispendingissuetounlabel.js | 6 +- .../lib/utils/preparesearchquery.js | 6 +- packages/ckeditor5-dev-stale-bot/package.json | 10 +- .../tests/githubrepository.js | 1634 ++++++++--------- .../tests/utils/findstaledate.js | 12 +- .../tests/utils/isissueorpullrequestactive.js | 30 +- .../utils/isissueorpullrequesttoclose.js | 53 +- .../utils/isissueorpullrequesttostale.js | 34 +- .../utils/isissueorpullrequesttounstale.js | 43 +- .../tests/utils/ispendingissuestale.js | 16 +- .../tests/utils/ispendingissuetostale.js | 46 +- .../tests/utils/ispendingissuetounlabel.js | 12 +- .../tests/utils/preparesearchquery.js | 28 +- .../ckeditor5-dev-stale-bot/vitest.config.js | 23 + 27 files changed, 1015 insertions(+), 1127 deletions(-) create mode 100644 packages/ckeditor5-dev-stale-bot/vitest.config.js diff --git a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js index d08a2a800..951d3f4b6 100755 --- a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js +++ b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js @@ -5,15 +5,13 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs-extra' ); -const chalk = require( 'chalk' ); -const createSpinner = require( './utils/createspinner' ); -const parseArguments = require( './utils/parsearguments' ); -const validateConfig = require( './utils/validateconfig' ); -const parseConfig = require( './utils/parseconfig' ); -const GitHubRepository = require( '../lib/githubrepository' ); +import fs from 'fs-extra'; +import chalk from 'chalk'; +import createSpinner from './utils/createspinner'; +import parseArguments from './utils/parsearguments'; +import validateConfig from './utils/validateconfig'; +import parseConfig from './utils/parseconfig'; +import GitHubRepository from '../lib/githubrepository'; main().catch( error => { console.error( '\nđŸ”Ĩ Unable to process stale issues and pull requests.\n', error ); @@ -286,7 +284,7 @@ function printStatus( dryRun, searchResult, options ) { /** * Prints in the console issues and pull requests from a single section. * - * @param {String} statusMessage Seaction header. + * @param {String} statusMessage Section header. * @param {Array.} entries Found issues and pull requests. */ function printStatusSection( statusMessage, entries ) { diff --git a/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js b/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js index 9c74f1b72..1da67107d 100644 --- a/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js +++ b/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js @@ -3,17 +3,15 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const ora = require( 'ora' ); -const chalk = require( 'chalk' ); +import ora from 'ora'; +import chalk from 'chalk'; /** * Creates the spinner instance with methods to update spinner text. * * @returns {Spinner} */ -module.exports = function createSpinner() { +export default function createSpinner() { const instance = ora(); const printStatus = text => { @@ -40,7 +38,7 @@ module.exports = function createSpinner() { printStatus, onProgress }; -}; +} /** * @typedef {Object} Spinner diff --git a/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js b/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js index ac394ea3e..1f6220d23 100644 --- a/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js +++ b/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const minimist = require( 'minimist' ); -const upath = require( 'upath' ); +import minimist from 'minimist'; +import upath from 'upath'; /** * Parses CLI arguments. @@ -16,7 +14,7 @@ const upath = require( 'upath' ); * @returns {Boolean} result.dryRun * @returns {String} result.configPath */ -module.exports = function parseArguments( args ) { +export default function parseArguments( args ) { const config = { boolean: [ 'dry-run' @@ -38,4 +36,4 @@ module.exports = function parseArguments( args ) { dryRun: options[ 'dry-run' ], configPath: upath.join( process.cwd(), options[ 'config-path' ] ) }; -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js b/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js index a397e1b84..ccfa3e54b 100644 --- a/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js +++ b/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { subDays, formatISO } = require( 'date-fns' ); +import { subDays, formatISO } from 'date-fns'; /** * Converts configuration options into format required by the GitHubRepository. @@ -14,7 +12,7 @@ const { subDays, formatISO } = require( 'date-fns' ); * @param {Config} config Configuration options. * @returns {Options} */ -module.exports = function parseConfig( viewerLogin, config ) { +export default function parseConfig( viewerLogin, config ) { const { REPOSITORY_SLUG, STALE_LABELS, @@ -63,7 +61,7 @@ module.exports = function parseConfig( viewerLogin, config ) { [ ...IGNORED_ACTIVITY_LOGINS, viewerLogin ] : IGNORED_ACTIVITY_LOGINS }; -}; +} /** * @typedef {Object} Config diff --git a/packages/ckeditor5-dev-stale-bot/bin/utils/validateconfig.js b/packages/ckeditor5-dev-stale-bot/bin/utils/validateconfig.js index 0a5f363d5..8a4dc534c 100644 --- a/packages/ckeditor5-dev-stale-bot/bin/utils/validateconfig.js +++ b/packages/ckeditor5-dev-stale-bot/bin/utils/validateconfig.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - const requiredFields = [ 'GITHUB_TOKEN', 'REPOSITORY_SLUG', @@ -23,7 +21,7 @@ const requiredFields = [ * @param {Config} config Configuration options. * @returns {void} */ -module.exports = function validateConfig( config ) { +export default function validateConfig( config ) { const missingFields = requiredFields.filter( fieldName => !config[ fieldName ] ); if ( !missingFields.length ) { @@ -31,5 +29,5 @@ module.exports = function validateConfig( config ) { } throw new Error( `Missing configuration options: ${ missingFields.join( ', ' ) }.` ); -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js index 9a6bfb1a4..a5f9bde09 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js +++ b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js @@ -3,47 +3,32 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const upath = require( 'upath' ); -const fs = require( 'fs-extra' ); -const { GraphQLClient } = require( 'graphql-request' ); -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); +import upath from 'upath'; +import fs from 'fs-extra'; +import { GraphQLClient } from 'graphql-request'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; const { addSeconds, fromUnixTime, formatDistanceToNow, differenceInSeconds } = require( 'date-fns' ); -const prepareSearchQuery = require( './utils/preparesearchquery' ); -const isIssueOrPullRequestToStale = require( './utils/isissueorpullrequesttostale' ); -const isIssueOrPullRequestToUnstale = require( './utils/isissueorpullrequesttounstale' ); -const isIssueOrPullRequestToClose = require( './utils/isissueorpullrequesttoclose' ); -const isPendingIssueToStale = require( './utils/ispendingissuetostale' ); -const isPendingIssueToUnlabel = require( './utils/ispendingissuetounlabel' ); +import prepareSearchQuery from './utils/preparesearchquery'; +import isIssueOrPullRequestToStale from './utils/isissueorpullrequesttostale'; +import isIssueOrPullRequestToUnstale from './utils/isissueorpullrequesttounstale'; +import isIssueOrPullRequestToClose from './utils/isissueorpullrequesttoclose'; +import isPendingIssueToStale from './utils/ispendingissuetostale'; +import isPendingIssueToUnlabel from './utils/ispendingissuetounlabel'; const GRAPHQL_PATH = upath.join( __dirname, 'graphql' ); -const queries = { - getViewerLogin: readGraphQL( 'getviewerlogin' ), - searchIssuesOrPullRequests: readGraphQL( 'searchissuesorpullrequests' ), - searchPendingIssues: readGraphQL( 'searchpendingissues' ), - getIssueOrPullRequestTimelineItems: readGraphQL( 'getissueorpullrequesttimelineitems' ), - addComment: readGraphQL( 'addcomment' ), - getLabels: readGraphQL( 'getlabels' ), - addLabels: readGraphQL( 'addlabels' ), - removeLabels: readGraphQL( 'removelabels' ), - closeIssue: readGraphQL( 'closeissue' ), - closePullRequest: readGraphQL( 'closepullrequest' ) -}; - /** * A GitHub client containing methods used to interact with GitHub using its GraphQL API. * * All methods handles paginated data and it supports a case when a request has exceeded the GitHub API rate limit. * In such a case, the request waits until the limit is reset and it is automatically sent again. */ -module.exports = class GitHubRepository { +export default class GitHubRepository { constructor( authToken ) { /** * @private @@ -63,6 +48,22 @@ module.exports = class GitHubRepository { * @property {Logger} */ this.logger = logger(); + + /** + * @private + */ + this.queries = { + getViewerLogin: readGraphQL( 'getviewerlogin' ), + searchIssuesOrPullRequests: readGraphQL( 'searchissuesorpullrequests' ), + searchPendingIssues: readGraphQL( 'searchpendingissues' ), + getIssueOrPullRequestTimelineItems: readGraphQL( 'getissueorpullrequesttimelineitems' ), + addComment: readGraphQL( 'addcomment' ), + getLabels: readGraphQL( 'getlabels' ), + addLabels: readGraphQL( 'addlabels' ), + removeLabels: readGraphQL( 'removelabels' ), + closeIssue: readGraphQL( 'closeissue' ), + closePullRequest: readGraphQL( 'closepullrequest' ) + }; } /** @@ -71,7 +72,7 @@ module.exports = class GitHubRepository { * @returns {Promise.} */ async getViewerLogin() { - return this.sendRequest( await queries.getViewerLogin ) + return this.sendRequest( await this.queries.getViewerLogin ) .then( data => data.viewer.login ) .catch( error => { this.logger.error( 'Unexpected error when executing "#getViewerLogin()".', error ); @@ -105,7 +106,7 @@ module.exports = class GitHubRepository { cursor: pageInfo.cursor || null }; - return this.sendRequest( await queries.searchIssuesOrPullRequests, variables ) + return this.sendRequest( await this.queries.searchIssuesOrPullRequests, variables ) .then( async data => { const issuesOrPullRequests = await this.parseIssuesOrPullRequests( data.search ); @@ -151,7 +152,7 @@ module.exports = class GitHubRepository { cursor: pageInfo.cursor || null }; - return this.sendRequest( await queries.searchIssuesOrPullRequests, variables ) + return this.sendRequest( await this.queries.searchIssuesOrPullRequests, variables ) .then( async data => { const issuesOrPullRequests = await this.parseIssuesOrPullRequests( data.search ); @@ -217,7 +218,7 @@ module.exports = class GitHubRepository { cursor: pageInfo.cursor || null }; - return this.sendRequest( await queries.searchPendingIssues, variables ) + return this.sendRequest( await this.queries.searchPendingIssues, variables ) .then( async data => { const pendingIssues = this.parsePendingIssues( data.search ); @@ -263,7 +264,7 @@ module.exports = class GitHubRepository { cursor: pageInfo.cursor || null }; - return this.sendRequest( await queries.getIssueOrPullRequestTimelineItems, variables ) + return this.sendRequest( await this.queries.getIssueOrPullRequestTimelineItems, variables ) .then( async data => { pageInfo = data.node.timelineItems.pageInfo; @@ -295,7 +296,7 @@ module.exports = class GitHubRepository { comment }; - return this.sendRequest( await queries.addComment, variables ) + return this.sendRequest( await this.queries.addComment, variables ) .catch( error => { this.logger.error( 'Unexpected error when executing "#addComment()".', error ); @@ -322,7 +323,7 @@ module.exports = class GitHubRepository { labelNames: labelNames.join( ' ' ) }; - return this.sendRequest( await queries.getLabels, variables ) + return this.sendRequest( await this.queries.getLabels, variables ) .then( data => { return data.repository.labels.nodes // Additional filtering is needed, because GitHub endpoint may return many more results than match the query. @@ -349,7 +350,7 @@ module.exports = class GitHubRepository { labelIds }; - return this.sendRequest( await queries.addLabels, variables ) + return this.sendRequest( await this.queries.addLabels, variables ) .catch( error => { this.logger.error( 'Unexpected error when executing "#addLabels()".', error ); @@ -370,7 +371,7 @@ module.exports = class GitHubRepository { labelIds }; - return this.sendRequest( await queries.removeLabels, variables ) + return this.sendRequest( await this.queries.removeLabels, variables ) .catch( error => { this.logger.error( 'Unexpected error when executing "#removeLabels()".', error ); @@ -390,7 +391,7 @@ module.exports = class GitHubRepository { nodeId }; - const query = type === 'Issue' ? await queries.closeIssue : await queries.closePullRequest; + const query = type === 'Issue' ? await this.queries.closeIssue : await this.queries.closePullRequest; return this.sendRequest( query, variables ) .catch( error => { @@ -556,7 +557,7 @@ module.exports = class GitHubRepository { return Promise.reject( error ); } ); } -}; +} /** * Reads the GraphQL query from filesystem. diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js b/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js index 51d738c8e..f4156042c 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { isAfter, parseISO } = require( 'date-fns' ); +import { isAfter, parseISO } from 'date-fns'; /** * Finds the most recent event date of the stale label assignment to issue or pull request. @@ -14,7 +12,7 @@ const { isAfter, parseISO } = require( 'date-fns' ); * @param {Options} options Configuration options. * @returns {String} */ -module.exports = function findStaleDate( issueOrPullRequest, options ) { +export default function findStaleDate( issueOrPullRequest, options ) { const { staleLabels } = options; return issueOrPullRequest.timelineItems @@ -27,4 +25,4 @@ module.exports = function findStaleDate( issueOrPullRequest, options ) { } ) .find( entry => staleLabels.includes( entry.label ) ) .eventDate; -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js index 3193a3c82..843082da3 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { isAfter, parseISO } = require( 'date-fns' ); +import { isAfter, parseISO } from 'date-fns'; /** * Verifies dates from an issue or pull request to check if some of them occurred after the provided moment, meaning that the issue or pull @@ -25,7 +23,7 @@ const { isAfter, parseISO } = require( 'date-fns' ); * @param {Options} options Configuration options. * @returns {Boolean} */ -module.exports = function isIssueOrPullRequestActive( issueOrPullRequest, staleDate, options ) { +export default function isIssueOrPullRequestActive( issueOrPullRequest, staleDate, options ) { const { ignoredActivityLogins, ignoredActivityLabels } = options; const dates = [ @@ -53,4 +51,4 @@ module.exports = function isIssueOrPullRequestActive( issueOrPullRequest, staleD return dates .filter( Boolean ) .some( date => isAfter( parseISO( date ), parseISO( staleDate ) ) ); -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js index 9a024721f..64c614091 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { isAfter, parseISO } = require( 'date-fns' ); -const isIssueOrPullRequestActive = require( './isissueorpullrequestactive' ); -const findStaleDate = require( './findstaledate' ); +import { isAfter, parseISO } from 'date-fns'; +import isIssueOrPullRequestActive from './isissueorpullrequestactive'; +import findStaleDate from './findstaledate'; /** * Checks whether the time to close a stale issue or pull request has passed and whether it is still inactive. @@ -16,7 +14,7 @@ const findStaleDate = require( './findstaledate' ); * @param {Options} options Configuration options. * @returns {Boolean} */ -module.exports = function isIssueOrPullRequestToClose( issueOrPullRequest, options ) { +export default function isIssueOrPullRequestToClose( issueOrPullRequest, options ) { const staleDate = findStaleDate( issueOrPullRequest, options ); const hasTimeToClosePassed = isAfter( parseISO( options.closeDate ), parseISO( staleDate ) ); @@ -25,4 +23,4 @@ module.exports = function isIssueOrPullRequestToClose( issueOrPullRequest, optio } return !isIssueOrPullRequestActive( issueOrPullRequest, staleDate, options ); -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js index 42ad9ba0a..634e4561e 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const isIssueOrPullRequestActive = require( './isissueorpullrequestactive' ); +import isIssueOrPullRequestActive from './isissueorpullrequestactive'; /** * Checks whether issue or pull request should be staled, because it was not active since the defined moment of time. @@ -14,8 +12,8 @@ const isIssueOrPullRequestActive = require( './isissueorpullrequestactive' ); * @param {Options} options Configuration options. * @returns {Boolean} */ -module.exports = function isIssueOrPullRequestToStale( issueOrPullRequest, options ) { +export default function isIssueOrPullRequestToStale( issueOrPullRequest, options ) { const { staleDate } = options; return !isIssueOrPullRequestActive( issueOrPullRequest, staleDate, options ); -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js index b287a2681..859290402 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const isIssueOrPullRequestActive = require( './isissueorpullrequestactive' ); -const findStaleDate = require( './findstaledate' ); +import isIssueOrPullRequestActive from './isissueorpullrequestactive'; +import findStaleDate from './findstaledate'; /** * Checks whether issue or pull request should be unstaled, because it was active after it was staled. @@ -15,8 +13,8 @@ const findStaleDate = require( './findstaledate' ); * @param {Options} options Configuration options. * @returns {Boolean} */ -module.exports = function isIssueOrPullRequestToUnstale( issueOrPullRequest, options ) { +export default function isIssueOrPullRequestToUnstale( issueOrPullRequest, options ) { const staleDate = findStaleDate( issueOrPullRequest, options ); return isIssueOrPullRequestActive( issueOrPullRequest, staleDate, options ); -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js index 8a1531466..d24656a21 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Checks whether pending issue is already stale. * @@ -12,6 +10,6 @@ * @param {Options} options Configuration options. * @returns {Boolean} */ -module.exports = function isPendingIssueStale( pendingIssue, options ) { +export default function isPendingIssueStale( pendingIssue, options ) { return options.staleLabels.every( staleLabel => pendingIssue.labels.includes( staleLabel ) ); -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js index b8d10a7bb..5faa39340 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { isBefore, parseISO } = require( 'date-fns' ); -const isPendingIssueStale = require( './ispendingissuestale' ); +import { isBefore, parseISO } from 'date-fns'; +import isPendingIssueStale from './ispendingissuestale'; /** * Checks whether pending issue should be staled, because it was not answered by a community member since the defined moment of time. @@ -15,7 +13,7 @@ const isPendingIssueStale = require( './ispendingissuestale' ); * @param {Options} options Configuration options. * @returns {Boolean} */ -module.exports = function isPendingIssueToStale( pendingIssue, options ) { +export default function isPendingIssueToStale( pendingIssue, options ) { const { lastComment } = pendingIssue; const { staleDatePendingIssue } = options; @@ -32,4 +30,4 @@ module.exports = function isPendingIssueToStale( pendingIssue, options ) { } return isBefore( parseISO( lastComment.createdAt ), parseISO( staleDatePendingIssue ) ); -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js index 404472608..c27877b1f 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js @@ -3,18 +3,16 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Checks whether pending issue should be unlabeled, because it was answered by a community member. * * @param {PendingIssue} pendingIssue Pending issue to check. * @returns {Boolean} */ -module.exports = function isPendingIssueToUnlabel( pendingIssue ) { +export default function isPendingIssueToUnlabel( pendingIssue ) { if ( !pendingIssue.lastComment ) { return false; } return pendingIssue.lastComment.isExternal; -}; +} diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js b/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js index 05a9238fc..34faa943b 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Creates a query to search for issues or pull requests. * @@ -16,7 +14,7 @@ * @param {Array.} [options.ignoredLabels=[]] * @returns {String} */ -module.exports = function prepareSearchQuery( options ) { +export default function prepareSearchQuery( options ) { const { repositorySlug, searchDate, @@ -36,7 +34,7 @@ module.exports = function prepareSearchQuery( options ) { ...labels.map( label => `label:${ label }` ), ...ignoredLabels.map( label => `-label:${ label }` ) ].filter( Boolean ).join( ' ' ); -}; +} function mapGitHubResourceType( type ) { const resourceMap = { diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index dca97c5bb..c2bc10592 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -16,6 +16,7 @@ "node": ">=18.0.0", "npm": ">=5.7.1" }, + "type": "module", "files": [ "bin", "lib" @@ -35,14 +36,11 @@ "upath": "^2.0.1" }, "devDependencies": { - "chai": "^4.2.0", - "mocha": "^7.1.2", - "sinon": "^9.2.4", - "proxyquire": "^2.1.3" + "vitest": "^2.0.5" }, "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.js --coverage" }, "depcheckIgnore": [ "graphql" diff --git a/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js b/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js index a9d50d724..a64210ae6 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js +++ b/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js @@ -3,48 +3,84 @@ * For licensing, see LICENSE.md. */ -const upath = require( 'upath' ); -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import upath from 'upath'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import prepareSearchQuery from '../lib/utils/preparesearchquery'; +import isIssueOrPullRequestToStale from '../lib/utils/isissueorpullrequesttostale'; +import isIssueOrPullRequestToUnstale from '../lib/utils/isissueorpullrequesttounstale'; +import isIssueOrPullRequestToClose from '../lib/utils/isissueorpullrequesttoclose'; +import isPendingIssueToStale from '../lib/utils/ispendingissuetostale'; +import isPendingIssueToUnlabel from '../lib/utils/ispendingissuetounlabel'; +import GitHubRepository from '../lib/githubrepository'; + +vi.mock( '../lib/utils/preparesearchquery' ); +vi.mock( '../lib/utils/isissueorpullrequesttostale' ); +vi.mock( '../lib/utils/isissueorpullrequesttounstale' ); +vi.mock( '../lib/utils/isissueorpullrequesttoclose' ); +vi.mock( '../lib/utils/ispendingissuetostale' ); +vi.mock( '../lib/utils/ispendingissuetounlabel' ); + +const { + fsReadFileMock, + loggerInfoMock, + loggerErrorMock, + graphQLClientConstructorSpy, + graphQLClientRequestMock +} = vi.hoisted( () => { + return { + fsReadFileMock: vi.fn(), + loggerInfoMock: vi.fn(), + loggerErrorMock: vi.fn(), + graphQLClientConstructorSpy: vi.fn(), + graphQLClientRequestMock: vi.fn() + + }; +} ); + +vi.mock( 'fs-extra', () => { + return { + default: { + readFile: fsReadFileMock + } + }; +} ); + +vi.mock( '@ckeditor/ckeditor5-dev-utils', () => { + return { + logger: () => { + return { + info: loggerInfoMock, + error: loggerErrorMock + }; + } + }; +} ); + +vi.mock( 'graphql-request', () => { + return { + GraphQLClient: class { + constructor( ...args ) { + graphQLClientConstructorSpy( ...args ); -const GRAPHQL_PATH = upath.join( __dirname, '..', 'lib', 'graphql' ); + this.request = graphQLClientRequestMock; + } + } + }; +} ); describe( 'dev-stale-bot/lib', () => { describe( 'GitHubRepository', () => { - let githubRepository, pageInfoNoNextPage, pageInfoWithNextPage, stubs, sandbox; + let githubRepository, pageInfoNoNextPage, pageInfoWithNextPage; beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - fs: { - readFile: sinon.stub() - }, - logger: { - error: sinon.stub(), - info: sinon.stub() - }, - GraphQLClient: { - class: class { - constructor( ...args ) { - stubs.GraphQLClient.constructor( ...args ); - } + vi.mocked( prepareSearchQuery ).mockReturnValue( 'search query' ); + vi.mocked( isIssueOrPullRequestToStale ).mockReturnValue( true ); + vi.mocked( isIssueOrPullRequestToUnstale ).mockReturnValue( true ); + vi.mocked( isIssueOrPullRequestToClose ).mockReturnValue( true ); + vi.mocked( isPendingIssueToStale ).mockReturnValue( true ); + vi.mocked( isPendingIssueToUnlabel ).mockReturnValue( true ); - request( ...args ) { - return stubs.GraphQLClient.request( ...args ); - } - }, - constructor: sinon.stub(), - request: sinon.stub() - }, - prepareSearchQuery: sinon.stub().returns( 'search query' ), - isIssueOrPullRequestToStale: sinon.stub().returns( true ), - isIssueOrPullRequestToUnstale: sinon.stub().returns( true ), - isIssueOrPullRequestToClose: sinon.stub().returns( true ), - isPendingIssueToStale: sinon.stub().returns( true ), - isPendingIssueToUnlabel: sinon.stub().returns( true ) - }; + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( {} ); pageInfoWithNextPage = { hasNextPage: true, @@ -57,117 +93,113 @@ describe( 'dev-stale-bot/lib', () => { }; const queries = { - getviewerlogin: 'query GetViewerLogin', - searchissuesorpullrequests: 'query SearchIssuesOrPullRequests', - searchpendingissues: 'query SearchPendingIssues', - getissueorpullrequesttimelineitems: 'query GetIssueOrPullRequestTimelineItems', - addcomment: 'mutation AddComment', - getlabels: 'query GetLabels', - addlabels: 'mutation AddLabels', - removelabels: 'mutation RemoveLabels', - closeissue: 'mutation CloseIssue', - closepullrequest: 'mutation ClosePullRequest' + 'getviewerlogin.graphql': 'query GetViewerLogin', + 'searchissuesorpullrequests.graphql': 'query SearchIssuesOrPullRequests', + 'searchpendingissues.graphql': 'query SearchPendingIssues', + 'getissueorpullrequesttimelineitems.graphql': 'query GetIssueOrPullRequestTimelineItems', + 'addcomment.graphql': 'mutation AddComment', + 'getlabels.graphql': 'query GetLabels', + 'addlabels.graphql': 'mutation AddLabels', + 'removelabels.graphql': 'mutation RemoveLabels', + 'closeissue.graphql': 'mutation CloseIssue', + 'closepullrequest.graphql': 'mutation ClosePullRequest' }; - for ( const [ file, query ] of Object.entries( queries ) ) { - const absolutePath = upath.join( GRAPHQL_PATH, `${ file }.graphql` ); - - stubs.fs.readFile.withArgs( absolutePath, 'utf-8' ).resolves( query ); - } - - const GitHubRepository = proxyquire( '../lib/githubrepository', { - 'fs-extra': stubs.fs, - 'graphql-request': { - GraphQLClient: stubs.GraphQLClient.class - }, - '@ckeditor/ckeditor5-dev-utils': { - logger() { - return stubs.logger; - } - }, - './utils/preparesearchquery': stubs.prepareSearchQuery, - './utils/isissueorpullrequesttostale': stubs.isIssueOrPullRequestToStale, - './utils/isissueorpullrequesttounstale': stubs.isIssueOrPullRequestToUnstale, - './utils/isissueorpullrequesttoclose': stubs.isIssueOrPullRequestToClose, - './utils/ispendingissuetostale': stubs.isPendingIssueToStale, - './utils/ispendingissuetounlabel': stubs.isPendingIssueToUnlabel - } ); + vi.mocked( fsReadFileMock ).mockImplementation( path => queries[ upath.basename( path ) ] ); githubRepository = new GitHubRepository( 'authorization-token' ); } ); - afterEach( () => { - sandbox.restore(); - } ); - describe( '#constructor()', () => { it( 'should create a new instance of GraphQLClient', () => { - expect( stubs.GraphQLClient.constructor.callCount ).to.equal( 1 ); + expect( graphQLClientConstructorSpy ).toHaveBeenCalledTimes( 1 ); } ); it( 'should pass the API URL to the GraphQLClient instance', () => { - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 0 ] ).to.equal( 'https://api.github.com/graphql' ); + expect( graphQLClientConstructorSpy ).toHaveBeenCalledWith( + 'https://api.github.com/graphql', + expect.any( Object ) + ); } ); it( 'should pass the authorization token to the GraphQLClient instance', () => { - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 1 ] ).to.have.property( 'headers' ); - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 1 ].headers ).to.have.property( - 'Authorization', - 'Bearer authorization-token' + expect( graphQLClientConstructorSpy ).toHaveBeenCalledWith( + expect.any( String ), + expect.objectContaining( { + headers: expect.objectContaining( { + Authorization: 'Bearer authorization-token' + } ) + } ) ); } ); it( 'should pass a proper "Accept" header to the GraphQLClient instance', () => { - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 1 ] ).to.have.property( 'headers' ); - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 1 ].headers ).to.have.property( - 'Accept', - 'application/vnd.github.bane-preview+json' + expect( graphQLClientConstructorSpy ).toHaveBeenCalledWith( + expect.any( String ), + expect.objectContaining( { + headers: expect.objectContaining( { + Accept: 'application/vnd.github.bane-preview+json' + } ) + } ) ); } ); it( 'should switch to the new global GitHub ID namespace in the GraphQLClient instance', () => { - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 1 ] ).to.have.property( 'headers' ); - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 1 ].headers ).to.have.property( 'X-Github-Next-Global-ID', 1 ); + expect( graphQLClientConstructorSpy ).toHaveBeenCalledWith( + expect.any( String ), + expect.objectContaining( { + headers: expect.objectContaining( { + 'X-Github-Next-Global-ID': 1 + } ) + } ) + ); } ); it( 'should disable the cache in the GraphQLClient instance', () => { - expect( stubs.GraphQLClient.constructor.getCall( 0 ).args[ 1 ] ).to.have.property( 'cache', 'no-store' ); + expect( graphQLClientConstructorSpy ).toHaveBeenCalledWith( + expect.any( String ), + expect.objectContaining( { + cache: 'no-store' + } ) + ); } ); } ); describe( '#getViewerLogin()', () => { it( 'should be a function', () => { - expect( githubRepository.getViewerLogin ).to.be.a( 'function' ); + expect( githubRepository.getViewerLogin ).toBeInstanceOf( Function ); } ); - it( 'should return viewer login', () => { - stubs.GraphQLClient.request.resolves( { + it( 'should return viewer login', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { viewer: { login: 'CKEditorBot' } } ); - return githubRepository.getViewerLogin().then( result => { - expect( result ).to.equal( 'CKEditorBot' ); - } ); + const result = await githubRepository.getViewerLogin(); + + expect( result ).toEqual( 'CKEditorBot' ); } ); - it( 'should send one request for viewer login', () => { - stubs.GraphQLClient.request.resolves( { + it( 'should send one request for viewer login', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { viewer: { login: 'CKEditorBot' } } ); - return githubRepository.getViewerLogin().then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query GetViewerLogin' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( {} ); - } ); + await githubRepository.getViewerLogin(); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'query GetViewerLogin', + {} + ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.getViewerLogin().then( () => { @@ -182,18 +214,18 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.getViewerLogin().then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( - 'Unexpected error when executing "#getViewerLogin()".' + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#getViewerLogin()".', + error ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); } ); } ); @@ -201,17 +233,17 @@ describe( 'dev-stale-bot/lib', () => { describe( '#getIssueOrPullRequestTimelineItems()', () => { it( 'should be a function', () => { - expect( githubRepository.getIssueOrPullRequestTimelineItems ).to.be.a( 'function' ); + expect( githubRepository.getIssueOrPullRequestTimelineItems ).toBeInstanceOf( Function ); } ); - it( 'should return all timeline events if they are not paginated', () => { + it( 'should return all timeline events if they are not paginated', async () => { const timelineItems = [ { createdAt: '2022-12-01T09:00:00Z' }, { createdAt: '2022-12-02T09:00:00Z' }, { createdAt: '2022-12-03T09:00:00Z' } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { node: { timelineItems: { nodes: timelineItems, @@ -220,16 +252,16 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 3 ); - expect( result[ 0 ] ).to.deep.equal( { eventDate: '2022-12-01T09:00:00Z' } ); - expect( result[ 1 ] ).to.deep.equal( { eventDate: '2022-12-02T09:00:00Z' } ); - expect( result[ 2 ] ).to.deep.equal( { eventDate: '2022-12-03T09:00:00Z' } ); - } ); + const result = await githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ); + + expect( result ).toEqual( [ + { eventDate: '2022-12-01T09:00:00Z' }, + { eventDate: '2022-12-02T09:00:00Z' }, + { eventDate: '2022-12-03T09:00:00Z' } + ] ); } ); - it( 'should return all timeline events if they are paginated', () => { + it( 'should return all timeline events if they are paginated', async () => { const timelineItems = [ { createdAt: '2022-12-01T09:00:00Z' }, { createdAt: '2022-12-02T09:00:00Z' }, @@ -247,23 +279,23 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 3 ); - expect( result[ 0 ] ).to.deep.equal( { eventDate: '2022-12-01T09:00:00Z' } ); - expect( result[ 1 ] ).to.deep.equal( { eventDate: '2022-12-02T09:00:00Z' } ); - expect( result[ 2 ] ).to.deep.equal( { eventDate: '2022-12-03T09:00:00Z' } ); - } ); + const result = await githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ); + + expect( result ).toEqual( [ + { eventDate: '2022-12-01T09:00:00Z' }, + { eventDate: '2022-12-02T09:00:00Z' }, + { eventDate: '2022-12-03T09:00:00Z' } + ] ); } ); - it( 'should send one request for all timeline events if they are not paginated', () => { + it( 'should send one request for all timeline events if they are not paginated', async () => { const timelineItems = [ { createdAt: '2022-12-01T09:00:00Z' }, { createdAt: '2022-12-02T09:00:00Z' }, { createdAt: '2022-12-03T09:00:00Z' } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { node: { timelineItems: { nodes: timelineItems, @@ -272,14 +304,16 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query GetIssueOrPullRequestTimelineItems' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( { nodeId: 'IssueId', cursor: null } ); - } ); + await githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'query GetIssueOrPullRequestTimelineItems', + { nodeId: 'IssueId', cursor: null } + ); } ); - it( 'should send multiple requests for all timeline events if they are paginated', () => { + it( 'should send multiple requests for all timeline events if they are paginated', async () => { const timelineItems = [ { createdAt: '2022-12-01T09:00:00Z' }, { createdAt: '2022-12-02T09:00:00Z' }, @@ -297,21 +331,27 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( () => { - expect( stubs.GraphQLClient.request.callCount ).to.equal( 3 ); + await githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query GetIssueOrPullRequestTimelineItems' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( { nodeId: 'IssueId', cursor: null } ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 3 ); - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 0 ] ).to.equal( 'query GetIssueOrPullRequestTimelineItems' ); - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 1 ] ).to.deep.equal( { nodeId: 'IssueId', cursor: 'cursor' } ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( 1, + 'query GetIssueOrPullRequestTimelineItems', + { nodeId: 'IssueId', cursor: null } + ); - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 0 ] ).to.equal( 'query GetIssueOrPullRequestTimelineItems' ); - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 1 ] ).to.deep.equal( { nodeId: 'IssueId', cursor: 'cursor' } ); - } ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( 2, + 'query GetIssueOrPullRequestTimelineItems', + { nodeId: 'IssueId', cursor: 'cursor' } + ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( 3, + 'query GetIssueOrPullRequestTimelineItems', + { nodeId: 'IssueId', cursor: 'cursor' } + ); } ); - it( 'should return event date, event author and label if any of these exist', () => { + it( 'should return event date, event author and label if any of these exist', async () => { const timelineItems = [ { createdAt: '2022-12-01T09:00:00Z' }, { updatedAt: '2022-12-02T09:00:00Z' }, @@ -320,7 +360,7 @@ describe( 'dev-stale-bot/lib', () => { { createdAt: '2022-12-05T09:00:00Z', label: { name: 'type:bug' } } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { node: { timelineItems: { nodes: timelineItems, @@ -329,19 +369,19 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 5 ); - expect( result[ 0 ] ).to.deep.equal( { eventDate: '2022-12-01T09:00:00Z' } ); - expect( result[ 1 ] ).to.deep.equal( { eventDate: '2022-12-02T09:00:00Z' } ); - expect( result[ 2 ] ).to.deep.equal( { eventDate: '2022-12-03T09:00:00Z', author: 'RandomUser' } ); - expect( result[ 3 ] ).to.deep.equal( { eventDate: '2022-12-04T09:00:00Z', author: 'RandomUser' } ); - expect( result[ 4 ] ).to.deep.equal( { eventDate: '2022-12-05T09:00:00Z', label: 'type:bug' } ); - } ); + const result = await githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ); + + expect( result ).toEqual( [ + { eventDate: '2022-12-01T09:00:00Z' }, + { eventDate: '2022-12-02T09:00:00Z' }, + { eventDate: '2022-12-03T09:00:00Z', author: 'RandomUser' }, + { eventDate: '2022-12-04T09:00:00Z', author: 'RandomUser' }, + { eventDate: '2022-12-05T09:00:00Z', label: 'type:bug' } + ] ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( () => { @@ -357,7 +397,7 @@ describe( 'dev-stale-bot/lib', () => { const timelineItems = [ { createdAt: '2022-12-01T09:00:00Z' }, { createdAt: '2022-12-02T09:00:00Z' }, - { createdAt: '2022-12-03T09:00:00Z' } + { error: new Error( '500 Internal Server Error' ) } ]; paginateRequest( timelineItems, ( { nodes, pageInfo } ) => { @@ -371,8 +411,6 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - stubs.GraphQLClient.request.onCall( 2 ).rejects( new Error( '500 Internal Server Error' ) ); - return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( () => { throw new Error( 'Expected to be rejected.' ); @@ -386,18 +424,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.getIssueOrPullRequestTimelineItems( 'IssueId' ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( - 'Unexpected error when executing "#getIssueOrPullRequestTimelineItems()".' + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#getIssueOrPullRequestTimelineItems()".', error ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); } ); } ); @@ -407,7 +444,7 @@ describe( 'dev-stale-bot/lib', () => { let onProgress, optionsBase, issueBase; beforeEach( () => { - onProgress = sinon.stub(); + onProgress = vi.fn(); optionsBase = { repositorySlug: 'ckeditor/ckeditor5', @@ -447,10 +484,10 @@ describe( 'dev-stale-bot/lib', () => { describe( '#searchIssuesOrPullRequestsToStale()', () => { it( 'should be a function', () => { - expect( githubRepository.searchIssuesOrPullRequestsToStale ).to.be.a( 'function' ); + expect( githubRepository.searchIssuesOrPullRequestsToStale ).toBeInstanceOf( Function ); } ); - it( 'should ask for issue search query', () => { + it( 'should ask for issue search query', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -462,7 +499,7 @@ describe( 'dev-stale-bot/lib', () => { ignoredIssueLabels: [ 'support:1', 'support:2', 'support:3' ] }; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -470,18 +507,18 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', options, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.calledOnce ).to.equal( true ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.deep.equal( { - type: 'Issue', - searchDate: '2022-12-01', - repositorySlug: 'ckeditor/ckeditor5', - ignoredLabels: [ 'status:stale', 'support:1', 'support:2', 'support:3' ] - } ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', options, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledWith( { + type: 'Issue', + searchDate: '2022-12-01', + repositorySlug: 'ckeditor/ckeditor5', + ignoredLabels: [ 'status:stale', 'support:1', 'support:2', 'support:3' ] } ); } ); - it( 'should ask for pull request search query', () => { + it( 'should ask for pull request search query', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -493,7 +530,7 @@ describe( 'dev-stale-bot/lib', () => { ignoredPullRequestLabels: [ 'support:1', 'support:2', 'support:3' ] }; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -501,25 +538,25 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'PullRequest', options, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.calledOnce ).to.equal( true ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.deep.equal( { - type: 'PullRequest', - searchDate: '2022-12-01', - repositorySlug: 'ckeditor/ckeditor5', - ignoredLabels: [ 'status:stale', 'support:1', 'support:2', 'support:3' ] - } ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'PullRequest', options, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledWith( { + type: 'PullRequest', + searchDate: '2022-12-01', + repositorySlug: 'ckeditor/ckeditor5', + ignoredLabels: [ 'status:stale', 'support:1', 'support:2', 'support:3' ] } ); } ); - it( 'should start the search from stale date if search date is not set', () => { + it( 'should start the search from stale date if search date is not set', async () => { const options = { ...optionsBase, searchDate: undefined, staleDate: '2023-01-01' }; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: 0, nodes: [], @@ -527,20 +564,22 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', options, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.calledOnce ).to.equal( true ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.have.property( 'searchDate', '2023-01-01' ); - } ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', options, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledWith( + expect.objectContaining( { 'searchDate': '2023-01-01' } ) + ); } ); - it( 'should return all issues to stale if they are not paginated', () => { + it( 'should return all issues to stale if they are not paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -548,22 +587,16 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 3 ); - expect( result[ 0 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - expect( result[ 1 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - expect( result[ 2 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - } ); + const result = await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); + + expect( result ).toEqual( [ + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' }, + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' }, + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } + ] ); } ); - it( 'should return all issues to stale if they are paginated', () => { + it( 'should return all issues to stale if they are paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -580,29 +613,23 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 3 ); - expect( result[ 0 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - expect( result[ 1 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - expect( result[ 2 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - } ); + const result = await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); + + expect( result ).toEqual( [ + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' }, + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' }, + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } + ] ); } ); - it( 'should send one request for all issues to stale if they are not paginated', () => { + it( 'should send one request for all issues to stale if they are not paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -610,16 +637,15 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: null } - ); - } ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: null } + ); } ); - it( 'should send multiple requests for all issues to stale if they are paginated', () => { + it( 'should send multiple requests for all issues to stale if they are paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -636,27 +662,20 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { - expect( stubs.GraphQLClient.request.callCount ).to.equal( 3 ); - - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: null } - ); - - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: 'cursor' } - ); - - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: 'cursor' } - ); - } ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 1, 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: null } + ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 2, 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: 'cursor' } + ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 3, 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: 'cursor' } + ); } ); - it( 'should fetch all timeline events for any issue if they are paginated', () => { + it( 'should fetch all timeline events for any issue if they are paginated', async () => { const issues = [ { ...issueBase, number: 1, timelineItems: { nodes: [], @@ -664,9 +683,9 @@ describe( 'dev-stale-bot/lib', () => { } } ]; - sinon.stub( githubRepository, 'getIssueOrPullRequestTimelineItems' ).resolves( [] ); + githubRepository.getIssueOrPullRequestTimelineItems = vi.fn().mockResolvedValue( [] ); - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -674,18 +693,15 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { - expect( githubRepository.getIssueOrPullRequestTimelineItems.callCount ).to.equal( 1 ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); - expect( githubRepository.getIssueOrPullRequestTimelineItems.getCall( 0 ).args[ 0 ] ).to.equal( 'IssueId' ); - expect( githubRepository.getIssueOrPullRequestTimelineItems.getCall( 0 ).args[ 1 ] ).to.deep.equal( { - hasNextPage: true, - cursor: 'cursor' - } ); - } ); + expect( vi.mocked( githubRepository.getIssueOrPullRequestTimelineItems ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( githubRepository.getIssueOrPullRequestTimelineItems ) ).toHaveBeenCalledWith( + 'IssueId', { hasNextPage: true, cursor: 'cursor' } + ); } ); - it( 'should ask for a new search query with new offset if GitHub prevents going to the next page', () => { + it( 'should ask for a new search query with new offset if GitHub prevents going to the next page', async () => { const issues = [ { ...issueBase, number: 1, createdAt: '2022-11-01T09:00:00Z' }, { ...issueBase, number: 2, createdAt: '2022-10-01T09:00:00Z' }, @@ -702,15 +718,21 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.callCount ).to.equal( 3 ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.have.property( 'searchDate', '2022-12-01' ); - expect( stubs.prepareSearchQuery.getCall( 1 ).args[ 0 ] ).to.have.property( 'searchDate', '2022-11-01T09:00:00Z' ); - expect( stubs.prepareSearchQuery.getCall( 2 ).args[ 0 ] ).to.have.property( 'searchDate', '2022-10-01T09:00:00Z' ); - } ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 1, expect.objectContaining( { searchDate: '2022-12-01' } ) + ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 2, expect.objectContaining( { searchDate: '2022-11-01T09:00:00Z' } ) + ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 3, expect.objectContaining( { searchDate: '2022-10-01T09:00:00Z' } ) + ); } ); - it( 'should return all issues to stale if GitHub prevents going to the next page', () => { + it( 'should return all issues to stale if GitHub prevents going to the next page', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -727,29 +749,25 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 3 ); - expect( result[ 0 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - expect( result[ 1 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - expect( result[ 2 ] ).to.deep.equal( - { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } - ); - } ); + const result = await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); + + expect( result ).toBeInstanceOf( Array ); + expect( result.length ).toEqual( 3 ); + expect( result ).toEqual( [ + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' }, + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' }, + { id: 'IssueId', title: 'IssueTitle', type: 'Issue', url: 'https://github.com/' } + ] ); } ); - it( 'should check each issue if it is stale', () => { + it( 'should check each issue if it is stale', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -757,32 +775,33 @@ describe( 'dev-stale-bot/lib', () => { } } ); - stubs.isIssueOrPullRequestToStale.onCall( 1 ).returns( false ); - - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( result => { - const expectedIssue = { - ...issueBase, - lastReactedAt: null, - timelineItems: [] - }; - - expect( stubs.isIssueOrPullRequestToStale.callCount ).to.equal( 3 ); + vi.mocked( isIssueOrPullRequestToStale ).mockReturnValueOnce( true ); + vi.mocked( isIssueOrPullRequestToStale ).mockReturnValueOnce( false ); - expect( stubs.isIssueOrPullRequestToStale.getCall( 0 ).args[ 0 ] ).to.deep.equal( { ...expectedIssue, number: 1 } ); - expect( stubs.isIssueOrPullRequestToStale.getCall( 0 ).args[ 1 ] ).to.deep.equal( optionsBase ); + const result = await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); - expect( stubs.isIssueOrPullRequestToStale.getCall( 1 ).args[ 0 ] ).to.deep.equal( { ...expectedIssue, number: 2 } ); - expect( stubs.isIssueOrPullRequestToStale.getCall( 1 ).args[ 1 ] ).to.deep.equal( optionsBase ); + const expectedIssue = { + ...issueBase, + lastReactedAt: null, + timelineItems: [] + }; - expect( stubs.isIssueOrPullRequestToStale.getCall( 2 ).args[ 0 ] ).to.deep.equal( { ...expectedIssue, number: 3 } ); - expect( stubs.isIssueOrPullRequestToStale.getCall( 2 ).args[ 1 ] ).to.deep.equal( optionsBase ); + expect( vi.mocked( isIssueOrPullRequestToStale ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( isIssueOrPullRequestToStale ) ).toHaveBeenNthCalledWith( + 1, { ...expectedIssue, number: 1 }, optionsBase + ); + expect( vi.mocked( isIssueOrPullRequestToStale ) ).toHaveBeenNthCalledWith( + 2, { ...expectedIssue, number: 2 }, optionsBase + ); + expect( vi.mocked( isIssueOrPullRequestToStale ) ).toHaveBeenNthCalledWith( + 3, { ...expectedIssue, number: 3 }, optionsBase + ); - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 2 ); - } ); + expect( result ).toBeInstanceOf( Array ); + expect( result.length ).toEqual( 2 ); } ); - it( 'should call on progress callback', () => { + it( 'should call on progress callback', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -799,16 +818,15 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { - expect( onProgress.callCount ).to.equal( 3 ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); - expect( onProgress.getCall( 0 ).args[ 0 ] ).to.deep.equal( { done: 1, total: 3 } ); - expect( onProgress.getCall( 1 ).args[ 0 ] ).to.deep.equal( { done: 2, total: 3 } ); - expect( onProgress.getCall( 2 ).args[ 0 ] ).to.deep.equal( { done: 3, total: 3 } ); - } ); + expect( vi.mocked( onProgress ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 1, { done: 1, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 2, { done: 2, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 3, { done: 3, total: 3 } ); } ); - it( 'should count total hits only once using the value from first response', () => { + it( 'should count total hits only once using the value from first response', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -825,17 +843,16 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { - expect( onProgress.callCount ).to.equal( 3 ); + await githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ); - expect( onProgress.getCall( 0 ).args[ 0 ] ).to.deep.equal( { done: 1, total: 3 } ); - expect( onProgress.getCall( 1 ).args[ 0 ] ).to.deep.equal( { done: 2, total: 3 } ); - expect( onProgress.getCall( 2 ).args[ 0 ] ).to.deep.equal( { done: 3, total: 3 } ); - } ); + expect( vi.mocked( onProgress ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 1, { done: 1, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 2, { done: 2, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 3, { done: 3, total: 3 } ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { @@ -851,7 +868,7 @@ describe( 'dev-stale-bot/lib', () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, - { ...issueBase, number: 3 } + { error: new Error( '500 Internal Server Error' ) } ]; paginateRequest( issues, ( { nodes, pageInfo } ) => { @@ -864,8 +881,6 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - stubs.GraphQLClient.request.onCall( 2 ).rejects( new Error( '500 Internal Server Error' ) ); - return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { throw new Error( 'Expected to be rejected.' ); @@ -879,18 +894,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.searchIssuesOrPullRequestsToStale( 'Issue', optionsBase, onProgress ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( - 'Unexpected error when executing "#searchIssuesOrPullRequestsToStale()".' + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#searchIssuesOrPullRequestsToStale()".', error ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); } ); } ); @@ -898,17 +912,17 @@ describe( 'dev-stale-bot/lib', () => { describe( '#searchStaleIssuesOrPullRequests()', () => { it( 'should be a function', () => { - expect( githubRepository.searchStaleIssuesOrPullRequests ).to.be.a( 'function' ); + expect( githubRepository.searchStaleIssuesOrPullRequests ).toBeInstanceOf( Function ); } ); - it( 'should ask for search query', () => { + it( 'should ask for search query', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -916,24 +930,24 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.calledOnce ).to.equal( true ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.deep.equal( { - searchDate: undefined, - repositorySlug: 'ckeditor/ckeditor5', - labels: [ 'status:stale' ] - } ); + await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledWith( { + searchDate: undefined, + repositorySlug: 'ckeditor/ckeditor5', + labels: [ 'status:stale' ] } ); } ); - it( 'should not set the initial start date', () => { + it( 'should not set the initial start date', async () => { const options = { ...optionsBase, searchDate: undefined, staleDate: '2023-01-01' }; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: 0, nodes: [], @@ -941,20 +955,22 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchStaleIssuesOrPullRequests( options, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.calledOnce ).to.equal( true ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.have.property( 'searchDate', undefined ); - } ); + await githubRepository.searchStaleIssuesOrPullRequests( options, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledWith( + expect.objectContaining( { 'searchDate': undefined } ) + ); } ); - it( 'should return all stale issues if they are not paginated', () => { + it( 'should return all stale issues if they are not paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -962,37 +978,23 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( result => { - expect( result ).to.have.property( 'issuesOrPullRequestsToClose' ); - expect( result ).to.have.property( 'issuesOrPullRequestsToUnstale' ); + const result = await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( result.issuesOrPullRequestsToClose ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToClose ).to.have.length( 3 ); - expect( result.issuesOrPullRequestsToClose[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToClose[ 1 ] ).to.deep.equal( + expect( result ).toEqual( { + issuesOrPullRequestsToClose: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToClose[ 2 ] ).to.deep.equal( + ], + issuesOrPullRequestsToUnstale: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - - expect( result.issuesOrPullRequestsToUnstale ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToUnstale ).to.have.length( 3 ); - expect( result.issuesOrPullRequestsToUnstale[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToUnstale[ 1 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToUnstale[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + ] } ); } ); - it( 'should return all stale issues if they are paginated', () => { + it( 'should return all stale issues if they are paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1009,44 +1011,30 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( result => { - expect( result ).to.have.property( 'issuesOrPullRequestsToClose' ); - expect( result ).to.have.property( 'issuesOrPullRequestsToUnstale' ); - - expect( result.issuesOrPullRequestsToClose ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToClose ).to.have.length( 3 ); - expect( result.issuesOrPullRequestsToClose[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToClose[ 1 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToClose[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + const result = await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( result.issuesOrPullRequestsToUnstale ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToUnstale ).to.have.length( 3 ); - expect( result.issuesOrPullRequestsToUnstale[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToUnstale[ 1 ] ).to.deep.equal( + expect( result ).toEqual( { + issuesOrPullRequestsToClose: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToUnstale[ 2 ] ).to.deep.equal( + ], + issuesOrPullRequestsToUnstale: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + ] } ); } ); - it( 'should send one request for all stale issues if they are not paginated', () => { + it( 'should send one request for all stale issues if they are not paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -1054,16 +1042,15 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: null } - ); - } ); + await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: null } + ); } ); - it( 'should send multiple requests for all stale issues if they are paginated', () => { + it( 'should send multiple requests for all stale issues if they are paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1080,27 +1067,22 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { - expect( stubs.GraphQLClient.request.callCount ).to.equal( 3 ); + await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: null } - ); - - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: 'cursor' } - ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 3 ); - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 0 ] ).to.equal( 'query SearchIssuesOrPullRequests' ); - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: 'cursor' } - ); - } ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 1, 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: null } + ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 2, 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: 'cursor' } + ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 3, 'query SearchIssuesOrPullRequests', { query: 'search query', cursor: 'cursor' } + ); } ); - it( 'should fetch all timeline events for any issue if they are paginated', () => { + it( 'should fetch all timeline events for any issue if they are paginated', async () => { const issues = [ { ...issueBase, number: 1, timelineItems: { nodes: [], @@ -1108,9 +1090,9 @@ describe( 'dev-stale-bot/lib', () => { } } ]; - sinon.stub( githubRepository, 'getIssueOrPullRequestTimelineItems' ).resolves( [] ); + githubRepository.getIssueOrPullRequestTimelineItems = vi.fn().mockResolvedValue( [] ); - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -1118,18 +1100,15 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { - expect( githubRepository.getIssueOrPullRequestTimelineItems.callCount ).to.equal( 1 ); + await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( githubRepository.getIssueOrPullRequestTimelineItems.getCall( 0 ).args[ 0 ] ).to.equal( 'IssueId' ); - expect( githubRepository.getIssueOrPullRequestTimelineItems.getCall( 0 ).args[ 1 ] ).to.deep.equal( { - hasNextPage: true, - cursor: 'cursor' - } ); - } ); + expect( vi.mocked( githubRepository.getIssueOrPullRequestTimelineItems ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( githubRepository.getIssueOrPullRequestTimelineItems ) ).toHaveBeenCalledWith( + 'IssueId', { hasNextPage: true, cursor: 'cursor' } + ); } ); - it( 'should ask for a new search query with new offset if GitHub prevents going to the next page', () => { + it( 'should ask for a new search query with new offset if GitHub prevents going to the next page', async () => { const issues = [ { ...issueBase, number: 1, createdAt: '2022-11-01T09:00:00Z' }, { ...issueBase, number: 2, createdAt: '2022-10-01T09:00:00Z' }, @@ -1146,15 +1125,21 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.callCount ).to.equal( 3 ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.have.property( 'searchDate', undefined ); - expect( stubs.prepareSearchQuery.getCall( 1 ).args[ 0 ] ).to.have.property( 'searchDate', '2022-11-01T09:00:00Z' ); - expect( stubs.prepareSearchQuery.getCall( 2 ).args[ 0 ] ).to.have.property( 'searchDate', '2022-10-01T09:00:00Z' ); - } ); + await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 3 ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 1, expect.objectContaining( { searchDate: undefined } ) + ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 2, expect.objectContaining( { searchDate: '2022-11-01T09:00:00Z' } ) + ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 3, expect.objectContaining( { searchDate: '2022-10-01T09:00:00Z' } ) + ); } ); - it( 'should return all stale issues if GitHub prevents going to the next page', () => { + it( 'should return all stale issues if GitHub prevents going to the next page', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1171,44 +1156,30 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( result => { - expect( result ).to.have.property( 'issuesOrPullRequestsToClose' ); - expect( result ).to.have.property( 'issuesOrPullRequestsToUnstale' ); - - expect( result.issuesOrPullRequestsToClose ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToClose ).to.have.length( 3 ); - expect( result.issuesOrPullRequestsToClose[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToClose[ 1 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToClose[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + const result = await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( result.issuesOrPullRequestsToUnstale ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToUnstale ).to.have.length( 3 ); - expect( result.issuesOrPullRequestsToUnstale[ 0 ] ).to.deep.equal( + expect( result ).toEqual( { + issuesOrPullRequestsToClose: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToUnstale[ 1 ] ).to.deep.equal( + ], + issuesOrPullRequestsToUnstale: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.issuesOrPullRequestsToUnstale[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + ] } ); } ); - it( 'should check each issue if it should be unstaled or closed', () => { + it( 'should check each issue if it should be unstaled or closed', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -1216,52 +1187,46 @@ describe( 'dev-stale-bot/lib', () => { } } ); - stubs.isIssueOrPullRequestToUnstale.onCall( 1 ).returns( false ); - stubs.isIssueOrPullRequestToClose.onCall( 1 ).returns( false ); - - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( result => { - const expectedIssue = { - ...issueBase, - lastReactedAt: null, - timelineItems: [] - }; - - expect( stubs.isIssueOrPullRequestToUnstale.callCount ).to.equal( 3 ); - - expect( stubs.isIssueOrPullRequestToUnstale.getCall( 0 ).args[ 0 ] ).to.deep.equal( - { ...expectedIssue, number: 1 } - ); - expect( stubs.isIssueOrPullRequestToUnstale.getCall( 0 ).args[ 1 ] ).to.deep.equal( optionsBase ); - - expect( stubs.isIssueOrPullRequestToUnstale.getCall( 1 ).args[ 0 ] ).to.deep.equal( - { ...expectedIssue, number: 2 } - ); - expect( stubs.isIssueOrPullRequestToUnstale.getCall( 1 ).args[ 1 ] ).to.deep.equal( optionsBase ); - - expect( stubs.isIssueOrPullRequestToUnstale.getCall( 2 ).args[ 0 ] ).to.deep.equal( - { ...expectedIssue, number: 3 } - ); - expect( stubs.isIssueOrPullRequestToUnstale.getCall( 2 ).args[ 1 ] ).to.deep.equal( optionsBase ); + vi.mocked( isIssueOrPullRequestToUnstale ).mockReturnValueOnce( false ); + vi.mocked( isIssueOrPullRequestToClose ).mockReturnValueOnce( false ); - expect( stubs.isIssueOrPullRequestToClose.callCount ).to.equal( 3 ); + const result = await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( stubs.isIssueOrPullRequestToClose.getCall( 0 ).args[ 0 ] ).to.deep.equal( { ...expectedIssue, number: 1 } ); - expect( stubs.isIssueOrPullRequestToClose.getCall( 0 ).args[ 1 ] ).to.deep.equal( optionsBase ); + const expectedIssue = { + ...issueBase, + lastReactedAt: null, + timelineItems: [] + }; - expect( stubs.isIssueOrPullRequestToClose.getCall( 1 ).args[ 0 ] ).to.deep.equal( { ...expectedIssue, number: 2 } ); - expect( stubs.isIssueOrPullRequestToClose.getCall( 1 ).args[ 1 ] ).to.deep.equal( optionsBase ); + expect( vi.mocked( isIssueOrPullRequestToUnstale ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( isIssueOrPullRequestToUnstale ) ).toHaveBeenNthCalledWith( + 1, { ...expectedIssue, number: 1 }, optionsBase + ); + expect( vi.mocked( isIssueOrPullRequestToUnstale ) ).toHaveBeenNthCalledWith( + 2, { ...expectedIssue, number: 2 }, optionsBase + ); + expect( vi.mocked( isIssueOrPullRequestToUnstale ) ).toHaveBeenNthCalledWith( + 3, { ...expectedIssue, number: 3 }, optionsBase + ); - expect( stubs.isIssueOrPullRequestToClose.getCall( 2 ).args[ 0 ] ).to.deep.equal( { ...expectedIssue, number: 3 } ); - expect( stubs.isIssueOrPullRequestToClose.getCall( 2 ).args[ 1 ] ).to.deep.equal( optionsBase ); + expect( vi.mocked( isIssueOrPullRequestToClose ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( isIssueOrPullRequestToClose ) ).toHaveBeenNthCalledWith( + 1, { ...expectedIssue, number: 1 }, optionsBase + ); + expect( vi.mocked( isIssueOrPullRequestToClose ) ).toHaveBeenNthCalledWith( + 2, { ...expectedIssue, number: 2 }, optionsBase + ); + expect( vi.mocked( isIssueOrPullRequestToClose ) ).toHaveBeenNthCalledWith( + 3, { ...expectedIssue, number: 3 }, optionsBase + ); - expect( result.issuesOrPullRequestsToUnstale ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToUnstale ).to.have.length( 2 ); - expect( result.issuesOrPullRequestsToClose ).to.be.an( 'array' ); - expect( result.issuesOrPullRequestsToClose ).to.have.length( 2 ); + expect( result ).toEqual( { + issuesOrPullRequestsToUnstale: [ expect.anything(), expect.anything() ], + issuesOrPullRequestsToClose: [ expect.anything(), expect.anything() ] } ); } ); - it( 'should call on progress callback', () => { + it( 'should call on progress callback', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1278,16 +1243,16 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { - expect( onProgress.callCount ).to.equal( 3 ); + await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( onProgress.getCall( 0 ).args[ 0 ] ).to.deep.equal( { done: 1, total: 3 } ); - expect( onProgress.getCall( 1 ).args[ 0 ] ).to.deep.equal( { done: 2, total: 3 } ); - expect( onProgress.getCall( 2 ).args[ 0 ] ).to.deep.equal( { done: 3, total: 3 } ); - } ); + expect( vi.mocked( onProgress ) ).toHaveBeenCalledTimes( 3 ); + + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 1, { done: 1, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 2, { done: 2, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 3, { done: 3, total: 3 } ); } ); - it( 'should count total hits only once using the value from first response', () => { + it( 'should count total hits only once using the value from first response', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1304,17 +1269,17 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { - expect( onProgress.callCount ).to.equal( 3 ); + await githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ); - expect( onProgress.getCall( 0 ).args[ 0 ] ).to.deep.equal( { done: 1, total: 3 } ); - expect( onProgress.getCall( 1 ).args[ 0 ] ).to.deep.equal( { done: 2, total: 3 } ); - expect( onProgress.getCall( 2 ).args[ 0 ] ).to.deep.equal( { done: 3, total: 3 } ); - } ); + expect( vi.mocked( onProgress ) ).toHaveBeenCalledTimes( 3 ); + + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 1, { done: 1, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 2, { done: 2, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 3, { done: 3, total: 3 } ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { @@ -1330,7 +1295,7 @@ describe( 'dev-stale-bot/lib', () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, - { ...issueBase, number: 3 } + { error: new Error( '500 Internal Server Error' ) } ]; paginateRequest( issues, ( { nodes, pageInfo } ) => { @@ -1343,8 +1308,6 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - stubs.GraphQLClient.request.onCall( 2 ).rejects( new Error( '500 Internal Server Error' ) ); - return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { throw new Error( 'Expected to be rejected.' ); @@ -1358,18 +1321,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.searchStaleIssuesOrPullRequests( optionsBase, onProgress ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( - 'Unexpected error when executing "#searchStaleIssuesOrPullRequests()".' + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#searchStaleIssuesOrPullRequests()".', error ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); } ); } ); @@ -1383,10 +1345,10 @@ describe( 'dev-stale-bot/lib', () => { } ); it( 'should be a function', () => { - expect( githubRepository.searchPendingIssues ).to.be.a( 'function' ); + expect( githubRepository.searchPendingIssues ).toBeInstanceOf( Function ); } ); - it( 'should ask for search query', () => { + it( 'should ask for search query', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1398,7 +1360,7 @@ describe( 'dev-stale-bot/lib', () => { ignoredIssueLabels: [ 'support:1', 'support:2', 'support:3' ] }; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -1406,26 +1368,26 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchPendingIssues( options, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.calledOnce ).to.equal( true ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.deep.equal( { - type: 'Issue', - searchDate: undefined, - repositorySlug: 'ckeditor/ckeditor5', - labels: [ 'pending:feedback' ], - ignoredLabels: [ 'support:1', 'support:2', 'support:3' ] - } ); + await githubRepository.searchPendingIssues( options, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledWith( { + type: 'Issue', + searchDate: undefined, + repositorySlug: 'ckeditor/ckeditor5', + labels: [ 'pending:feedback' ], + ignoredLabels: [ 'support:1', 'support:2', 'support:3' ] } ); } ); - it( 'should not set the initial start date', () => { + it( 'should not set the initial start date', async () => { const options = { ...optionsBase, searchDate: undefined, staleDate: '2023-01-01' }; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: 0, nodes: [], @@ -1433,20 +1395,22 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchPendingIssues( options, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.calledOnce ).to.equal( true ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.have.property( 'searchDate', undefined ); - } ); + await githubRepository.searchPendingIssues( options, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledWith( + expect.objectContaining( { searchDate: undefined } ) + ); } ); - it( 'should return all pending issues if they are not paginated', () => { + it( 'should return all pending issues if they are not paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -1454,37 +1418,23 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( result => { - expect( result ).to.have.property( 'pendingIssuesToStale' ); - expect( result ).to.have.property( 'pendingIssuesToUnlabel' ); - - expect( result.pendingIssuesToStale ).to.be.an( 'array' ); - expect( result.pendingIssuesToStale ).to.have.length( 3 ); - expect( result.pendingIssuesToStale[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToStale[ 1 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToStale[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + const result = await githubRepository.searchPendingIssues( optionsBase, onProgress ); - expect( result.pendingIssuesToUnlabel ).to.be.an( 'array' ); - expect( result.pendingIssuesToUnlabel ).to.have.length( 3 ); - expect( result.pendingIssuesToUnlabel[ 0 ] ).to.deep.equal( + expect( result ).toEqual( { + pendingIssuesToStale: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToUnlabel[ 1 ] ).to.deep.equal( + ], + pendingIssuesToUnlabel: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToUnlabel[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + ] } ); } ); - it( 'should return all pending issues if they are paginated', () => { + it( 'should return all pending issues if they are paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1501,44 +1451,30 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( result => { - expect( result ).to.have.property( 'pendingIssuesToStale' ); - expect( result ).to.have.property( 'pendingIssuesToUnlabel' ); - - expect( result.pendingIssuesToStale ).to.be.an( 'array' ); - expect( result.pendingIssuesToStale ).to.have.length( 3 ); - expect( result.pendingIssuesToStale[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToStale[ 1 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToStale[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + const result = await githubRepository.searchPendingIssues( optionsBase, onProgress ); - expect( result.pendingIssuesToUnlabel ).to.be.an( 'array' ); - expect( result.pendingIssuesToUnlabel ).to.have.length( 3 ); - expect( result.pendingIssuesToUnlabel[ 0 ] ).to.deep.equal( + expect( result ).toEqual( { + pendingIssuesToStale: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToUnlabel[ 1 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToUnlabel[ 2 ] ).to.deep.equal( + ], + pendingIssuesToUnlabel: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + ] } ); } ); - it( 'should send one request for all pending issues if they are not paginated', () => { + it( 'should send one request for all pending issues if they are not paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, { ...issueBase, number: 3 } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -1546,16 +1482,15 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query SearchPendingIssues' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: null } - ); - } ); + await githubRepository.searchPendingIssues( optionsBase, onProgress ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'query SearchPendingIssues', { query: 'search query', cursor: null } + ); } ); - it( 'should send multiple requests for all pending issues if they are paginated', () => { + it( 'should send multiple requests for all pending issues if they are paginated', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1572,27 +1507,21 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { - expect( stubs.GraphQLClient.request.callCount ).to.equal( 3 ); + await githubRepository.searchPendingIssues( optionsBase, onProgress ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query SearchPendingIssues' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: null } - ); - - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 0 ] ).to.equal( 'query SearchPendingIssues' ); - expect( stubs.GraphQLClient.request.getCall( 1 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: 'cursor' } - ); - - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 0 ] ).to.equal( 'query SearchPendingIssues' ); - expect( stubs.GraphQLClient.request.getCall( 2 ).args[ 1 ] ).to.deep.equal( - { query: 'search query', cursor: 'cursor' } - ); - } ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 1, 'query SearchPendingIssues', { query: 'search query', cursor: null } + ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 2, 'query SearchPendingIssues', { query: 'search query', cursor: 'cursor' } + ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenNthCalledWith( + 3, 'query SearchPendingIssues', { query: 'search query', cursor: 'cursor' } + ); } ); - it( 'should ask for a new search query with new offset if GitHub prevents going to the next page', () => { + it( 'should ask for a new search query with new offset if GitHub prevents going to the next page', async () => { const issues = [ { ...issueBase, number: 1, createdAt: '2022-11-01T09:00:00Z' }, { ...issueBase, number: 2, createdAt: '2022-10-01T09:00:00Z' }, @@ -1609,15 +1538,21 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { - expect( stubs.prepareSearchQuery.callCount ).to.equal( 3 ); - expect( stubs.prepareSearchQuery.getCall( 0 ).args[ 0 ] ).to.have.property( 'searchDate', undefined ); - expect( stubs.prepareSearchQuery.getCall( 1 ).args[ 0 ] ).to.have.property( 'searchDate', '2022-11-01T09:00:00Z' ); - expect( stubs.prepareSearchQuery.getCall( 2 ).args[ 0 ] ).to.have.property( 'searchDate', '2022-10-01T09:00:00Z' ); - } ); + await githubRepository.searchPendingIssues( optionsBase, onProgress ); + + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 1, expect.objectContaining( { searchDate: undefined } ) + ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 2, expect.objectContaining( { searchDate: '2022-11-01T09:00:00Z' } ) + ); + expect( vi.mocked( prepareSearchQuery ) ).toHaveBeenNthCalledWith( + 3, expect.objectContaining( { searchDate: '2022-10-01T09:00:00Z' } ) + ); } ); - it( 'should return all pending issues if GitHub prevents going to the next page', () => { + it( 'should return all pending issues if GitHub prevents going to the next page', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1634,37 +1569,23 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( result => { - expect( result ).to.have.property( 'pendingIssuesToStale' ); - expect( result ).to.have.property( 'pendingIssuesToUnlabel' ); + const result = await githubRepository.searchPendingIssues( optionsBase, onProgress ); - expect( result.pendingIssuesToStale ).to.be.an( 'array' ); - expect( result.pendingIssuesToStale ).to.have.length( 3 ); - expect( result.pendingIssuesToStale[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToStale[ 1 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToStale[ 2 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - - expect( result.pendingIssuesToUnlabel ).to.be.an( 'array' ); - expect( result.pendingIssuesToUnlabel ).to.have.length( 3 ); - expect( result.pendingIssuesToUnlabel[ 0 ] ).to.deep.equal( - { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToUnlabel[ 1 ] ).to.deep.equal( + expect( result ).toEqual( { + pendingIssuesToStale: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); - expect( result.pendingIssuesToUnlabel[ 2 ] ).to.deep.equal( + ], + pendingIssuesToUnlabel: [ + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, + { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' }, { id: 'IssueId', type: 'Issue', title: 'IssueTitle', url: 'https://github.com/' } - ); + ] } ); } ); - it( 'should check each issue if it should be staled or unlabeled', () => { + it( 'should check each issue if it should be staled or unlabeled', async () => { const commentMember = { createdAt: '2022-11-30T23:59:59Z', authorAssociation: 'MEMBER' @@ -1681,7 +1602,7 @@ describe( 'dev-stale-bot/lib', () => { { ...issueBase, number: 3, comments: { nodes: [ commentNonMember ] } } ]; - stubs.GraphQLClient.request.resolves( { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { search: { issueCount: issues.length, nodes: issues, @@ -1689,47 +1610,61 @@ describe( 'dev-stale-bot/lib', () => { } } ); - stubs.isPendingIssueToStale.onCall( 1 ).returns( false ); - stubs.isPendingIssueToUnlabel.onCall( 1 ).returns( false ); - - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( result => { - expect( stubs.isPendingIssueToStale.callCount ).to.equal( 3 ); - - expect( stubs.isPendingIssueToStale.getCall( 0 ).args[ 0 ] ).to.have.deep.property( 'lastComment', { - createdAt: '2022-11-30T23:59:59Z', isExternal: false - } ); - expect( stubs.isPendingIssueToStale.getCall( 0 ).args[ 1 ] ).to.deep.equal( optionsBase ); - - expect( stubs.isPendingIssueToStale.getCall( 1 ).args[ 0 ] ).to.have.deep.property( 'lastComment', { - createdAt: '2022-11-30T23:59:59Z', isExternal: false - } ); - expect( stubs.isPendingIssueToStale.getCall( 1 ).args[ 1 ] ).to.deep.equal( optionsBase ); - - expect( stubs.isPendingIssueToStale.getCall( 2 ).args[ 0 ] ).to.have.deep.property( 'lastComment', { - createdAt: '2022-11-30T23:59:59Z', isExternal: true - } ); - expect( stubs.isPendingIssueToStale.getCall( 2 ).args[ 1 ] ).to.deep.equal( optionsBase ); - - expect( stubs.isPendingIssueToUnlabel.callCount ).to.equal( 3 ); - - expect( stubs.isPendingIssueToUnlabel.getCall( 0 ).args[ 0 ] ).to.have.deep.property( 'lastComment', { - createdAt: '2022-11-30T23:59:59Z', isExternal: false - } ); - expect( stubs.isPendingIssueToUnlabel.getCall( 1 ).args[ 0 ] ).to.have.deep.property( 'lastComment', { - createdAt: '2022-11-30T23:59:59Z', isExternal: false - } ); - expect( stubs.isPendingIssueToUnlabel.getCall( 2 ).args[ 0 ] ).to.have.deep.property( 'lastComment', { - createdAt: '2022-11-30T23:59:59Z', isExternal: true - } ); - - expect( result.pendingIssuesToStale ).to.be.an( 'array' ); - expect( result.pendingIssuesToStale ).to.have.length( 2 ); - expect( result.pendingIssuesToUnlabel ).to.be.an( 'array' ); - expect( result.pendingIssuesToUnlabel ).to.have.length( 2 ); + vi.mocked( isPendingIssueToStale ).mockReturnValueOnce( false ); + vi.mocked( isPendingIssueToUnlabel ).mockReturnValueOnce( false ); + + const result = await githubRepository.searchPendingIssues( optionsBase, onProgress ); + + expect( vi.mocked( isPendingIssueToStale ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( isPendingIssueToStale ) ).toHaveBeenNthCalledWith( + 1, + expect.objectContaining( { + lastComment: { createdAt: '2022-11-30T23:59:59Z', isExternal: false } + } ), + optionsBase + ); + expect( vi.mocked( isPendingIssueToStale ) ).toHaveBeenNthCalledWith( + 2, + expect.objectContaining( { + lastComment: { createdAt: '2022-11-30T23:59:59Z', isExternal: false } + } ), + optionsBase + ); + expect( vi.mocked( isPendingIssueToStale ) ).toHaveBeenNthCalledWith( + 3, + expect.objectContaining( { + lastComment: { createdAt: '2022-11-30T23:59:59Z', isExternal: true } + } ), + optionsBase + ); + + expect( vi.mocked( isPendingIssueToUnlabel ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( isPendingIssueToUnlabel ) ).toHaveBeenNthCalledWith( + 1, + expect.objectContaining( { + lastComment: { createdAt: '2022-11-30T23:59:59Z', isExternal: false } + } ) + ); + expect( vi.mocked( isPendingIssueToUnlabel ) ).toHaveBeenNthCalledWith( + 2, + expect.objectContaining( { + lastComment: { createdAt: '2022-11-30T23:59:59Z', isExternal: false } + } ) + ); + expect( vi.mocked( isPendingIssueToUnlabel ) ).toHaveBeenNthCalledWith( + 3, + expect.objectContaining( { + lastComment: { createdAt: '2022-11-30T23:59:59Z', isExternal: true } + } ) + ); + + expect( result ).toEqual( { + pendingIssuesToStale: [ expect.anything(), expect.anything() ], + pendingIssuesToUnlabel: [ expect.anything(), expect.anything() ] } ); } ); - it( 'should call on progress callback', () => { + it( 'should call on progress callback', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1746,16 +1681,15 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { - expect( onProgress.callCount ).to.equal( 3 ); + await githubRepository.searchPendingIssues( optionsBase, onProgress ); - expect( onProgress.getCall( 0 ).args[ 0 ] ).to.deep.equal( { done: 1, total: 3 } ); - expect( onProgress.getCall( 1 ).args[ 0 ] ).to.deep.equal( { done: 2, total: 3 } ); - expect( onProgress.getCall( 2 ).args[ 0 ] ).to.deep.equal( { done: 3, total: 3 } ); - } ); + expect( vi.mocked( onProgress ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 1, { done: 1, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 2, { done: 2, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 3, { done: 3, total: 3 } ); } ); - it( 'should count total hits only once using the value from first response', () => { + it( 'should count total hits only once using the value from first response', async () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, @@ -1772,17 +1706,16 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { - expect( onProgress.callCount ).to.equal( 3 ); + await githubRepository.searchPendingIssues( optionsBase, onProgress ); - expect( onProgress.getCall( 0 ).args[ 0 ] ).to.deep.equal( { done: 1, total: 3 } ); - expect( onProgress.getCall( 1 ).args[ 0 ] ).to.deep.equal( { done: 2, total: 3 } ); - expect( onProgress.getCall( 2 ).args[ 0 ] ).to.deep.equal( { done: 3, total: 3 } ); - } ); + expect( vi.mocked( onProgress ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 1, { done: 1, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 2, { done: 2, total: 3 } ); + expect( vi.mocked( onProgress ) ).toHaveBeenNthCalledWith( 3, { done: 3, total: 3 } ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { @@ -1798,7 +1731,7 @@ describe( 'dev-stale-bot/lib', () => { const issues = [ { ...issueBase, number: 1 }, { ...issueBase, number: 2 }, - { ...issueBase, number: 3 } + { error: new Error( '500 Internal Server Error' ) } ]; paginateRequest( issues, ( { nodes, pageInfo } ) => { @@ -1811,8 +1744,6 @@ describe( 'dev-stale-bot/lib', () => { }; } ); - stubs.GraphQLClient.request.onCall( 2 ).rejects( new Error( '500 Internal Server Error' ) ); - return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { throw new Error( 'Expected to be rejected.' ); @@ -1826,18 +1757,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.searchPendingIssues( optionsBase, onProgress ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( - 'Unexpected error when executing "#searchPendingIssues()".' + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#searchPendingIssues()".', error ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); } ); } ); @@ -1846,23 +1776,22 @@ describe( 'dev-stale-bot/lib', () => { describe( '#addComment()', () => { it( 'should be a function', () => { - expect( githubRepository.addComment ).to.be.a( 'function' ); + expect( githubRepository.addComment ).toBeInstanceOf( Function ); } ); - it( 'should add a comment', () => { - stubs.GraphQLClient.request.resolves(); + it( 'should add a comment', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue(); - return githubRepository.addComment( 'IssueId', 'A comment.' ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'mutation AddComment' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( - { nodeId: 'IssueId', comment: 'A comment.' } - ); - } ); + await githubRepository.addComment( 'IssueId', 'A comment.' ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'mutation AddComment', { nodeId: 'IssueId', comment: 'A comment.' } + ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.addComment( 'IssueId', 'A comment.' ).then( () => { @@ -1877,16 +1806,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.addComment( 'IssueId', 'A comment.' ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( 'Unexpected error when executing "#addComment()".' ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#addComment()".', error + ); } ); } ); @@ -1904,18 +1834,17 @@ describe( 'dev-stale-bot/lib', () => { } ); it( 'should be a function', () => { - expect( githubRepository.getLabels ).to.be.a( 'function' ); + expect( githubRepository.getLabels ).toBeInstanceOf( Function ); } ); - it( 'should return an empty array if no labels are provided', () => { - return githubRepository.getLabels( 'ckeditor/ckeditor5', [] ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 0 ); - } ); + it( 'should return an empty array if no labels are provided', async () => { + const result = await githubRepository.getLabels( 'ckeditor/ckeditor5', [] ); + + expect( result ).toEqual( [] ); } ); - it( 'should return labels', () => { - stubs.GraphQLClient.request.resolves( { + it( 'should return labels', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { repository: { labels: { nodes: labels @@ -1923,17 +1852,15 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 3 ); - expect( result[ 0 ] ).to.equal( 'LabelId1' ); - expect( result[ 1 ] ).to.equal( 'LabelId2' ); - expect( result[ 2 ] ).to.equal( 'LabelId3' ); - } ); + const result = await githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ); + + expect( result ).toEqual( [ + 'LabelId1', 'LabelId2', 'LabelId3' + ] ); } ); - it( 'should return only requested labels even if GitHub endpoint returned additional ones', () => { - stubs.GraphQLClient.request.resolves( { + it( 'should return only requested labels even if GitHub endpoint returned additional ones', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { repository: { labels: { nodes: [ @@ -1947,17 +1874,15 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ).then( result => { - expect( result ).to.be.an( 'array' ); - expect( result ).to.have.length( 3 ); - expect( result[ 0 ] ).to.equal( 'LabelId1' ); - expect( result[ 1 ] ).to.equal( 'LabelId2' ); - expect( result[ 2 ] ).to.equal( 'LabelId3' ); - } ); + const result = await githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ); + + expect( result ).toEqual( [ + 'LabelId1', 'LabelId2', 'LabelId3' + ] ); } ); - it( 'should send one request for labels', () => { - stubs.GraphQLClient.request.resolves( { + it( 'should send one request for labels', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( { repository: { labels: { nodes: labels @@ -1965,19 +1890,16 @@ describe( 'dev-stale-bot/lib', () => { } } ); - return githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'query GetLabels' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( { - repositoryOwner: 'ckeditor', - repositoryName: 'ckeditor5', - labelNames: 'type:bug type:task type:feature' - } ); - } ); + await githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'query GetLabels', + { repositoryOwner: 'ckeditor', repositoryName: 'ckeditor5', labelNames: 'type:bug type:task type:feature' } ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ).then( () => { @@ -1992,16 +1914,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.getLabels( 'ckeditor/ckeditor5', [ 'type:bug', 'type:task', 'type:feature' ] ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( 'Unexpected error when executing "#getLabels()".' ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#getLabels()".', error + ); } ); } ); @@ -2009,24 +1932,22 @@ describe( 'dev-stale-bot/lib', () => { describe( '#addLabels()', () => { it( 'should be a function', () => { - expect( githubRepository.addLabels ).to.be.a( 'function' ); + expect( githubRepository.addLabels ).toBeInstanceOf( Function ); } ); - it( 'should add a comment', () => { - stubs.GraphQLClient.request.resolves(); + it( 'should add a comment', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue(); - return githubRepository.addLabels( 'IssueId', [ 'LabelId' ] ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'mutation AddLabels' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( { - nodeId: 'IssueId', - labelIds: [ 'LabelId' ] - } ); - } ); + await githubRepository.addLabels( 'IssueId', [ 'LabelId' ] ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'mutation AddLabels', { nodeId: 'IssueId', labelIds: [ 'LabelId' ] } + ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.addLabels( 'IssueId', [ 'LabelId' ] ).then( () => { @@ -2041,16 +1962,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.addLabels( 'IssueId', [ 'LabelId' ] ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( 'Unexpected error when executing "#addLabels()".' ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#addLabels()".', error + ); } ); } ); @@ -2058,24 +1980,22 @@ describe( 'dev-stale-bot/lib', () => { describe( '#removeLabels()', () => { it( 'should be a function', () => { - expect( githubRepository.removeLabels ).to.be.a( 'function' ); + expect( githubRepository.removeLabels ).toBeInstanceOf( Function ); } ); - it( 'should add a comment', () => { - stubs.GraphQLClient.request.resolves(); + it( 'should add a comment', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue(); - return githubRepository.removeLabels( 'IssueId', [ 'LabelId' ] ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'mutation RemoveLabels' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( { - nodeId: 'IssueId', - labelIds: [ 'LabelId' ] - } ); - } ); + await githubRepository.removeLabels( 'IssueId', [ 'LabelId' ] ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'mutation RemoveLabels', { nodeId: 'IssueId', labelIds: [ 'LabelId' ] } + ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.removeLabels( 'IssueId', [ 'LabelId' ] ).then( () => { @@ -2090,18 +2010,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.removeLabels( 'IssueId', [ 'LabelId' ] ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( - 'Unexpected error when executing "#removeLabels()".' + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#removeLabels()".', error ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); } ); } ); @@ -2109,31 +2028,33 @@ describe( 'dev-stale-bot/lib', () => { describe( '#closeIssueOrPullRequest()', () => { it( 'should be a function', () => { - expect( githubRepository.closeIssueOrPullRequest ).to.be.a( 'function' ); + expect( githubRepository.closeIssueOrPullRequest ).toBeInstanceOf( Function ); } ); - it( 'should close issue', () => { - stubs.GraphQLClient.request.resolves(); + it( 'should close issue', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue(); - return githubRepository.closeIssueOrPullRequest( 'Issue', 'IssueId' ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'mutation CloseIssue' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( { nodeId: 'IssueId' } ); - } ); + await githubRepository.closeIssueOrPullRequest( 'Issue', 'IssueId' ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'mutation CloseIssue', { nodeId: 'IssueId' } + ); } ); - it( 'should close pull request', () => { - stubs.GraphQLClient.request.resolves(); + it( 'should close pull request', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue(); - return githubRepository.closeIssueOrPullRequest( 'PullRequest', 'PullRequestId' ).then( () => { - expect( stubs.GraphQLClient.request.calledOnce ).to.equal( true ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 0 ] ).to.equal( 'mutation ClosePullRequest' ); - expect( stubs.GraphQLClient.request.getCall( 0 ).args[ 1 ] ).to.deep.equal( { nodeId: 'PullRequestId' } ); - } ); + await githubRepository.closeIssueOrPullRequest( 'PullRequest', 'PullRequestId' ); + + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledWith( + 'mutation ClosePullRequest', { nodeId: 'PullRequestId' } + ); } ); it( 'should reject if request failed', () => { - stubs.GraphQLClient.request.rejects( new Error( '500 Internal Server Error' ) ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( new Error( '500 Internal Server Error' ) ); return githubRepository.closeIssueOrPullRequest( 'Issue', 'IssueId' ).then( () => { @@ -2148,18 +2069,17 @@ describe( 'dev-stale-bot/lib', () => { it( 'should log an error if request failed', () => { const error = new Error( '500 Internal Server Error' ); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.closeIssueOrPullRequest( 'Issue', 'IssueId' ).then( () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.logger.error.callCount ).to.equal( 1 ); - expect( stubs.logger.error.getCall( 0 ).args[ 0 ] ).to.equal( - 'Unexpected error when executing "#closeIssueOrPullRequest()".' + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledWith( + 'Unexpected error when executing "#closeIssueOrPullRequest()".', error ); - expect( stubs.logger.error.getCall( 0 ).args[ 1 ] ).to.equal( error ); } ); } ); @@ -2172,39 +2092,37 @@ describe( 'dev-stale-bot/lib', () => { } }; - let clock; - beforeEach( () => { - clock = sinon.useFakeTimers(); + vi.useFakeTimers(); } ); afterEach( () => { - clock.restore(); + vi.useRealTimers(); } ); it( 'should be a function', () => { - expect( githubRepository.sendRequest ).to.be.a( 'function' ); + expect( githubRepository.sendRequest ).toBeInstanceOf( Function ); } ); - it( 'should resolve with the payload if no error occurred', () => { - stubs.GraphQLClient.request.resolves( payload ); + it( 'should resolve with the payload if no error occurred', async () => { + vi.mocked( graphQLClientRequestMock ).mockResolvedValue( payload ); - return githubRepository.sendRequest( 'query' ).then( result => { - expect( result ).to.equal( payload ); - } ); + const result = await githubRepository.sendRequest( 'query' ); + + expect( result ).toEqual( payload ); } ); it( 'should reject with the error - no custom properties', () => { const error = new Error(); - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.sendRequest( 'query' ).then( () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( error ); + expect( err ).toEqual( error ); } ); } ); @@ -2213,14 +2131,14 @@ describe( 'dev-stale-bot/lib', () => { const error = new Error(); error.response = {}; - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.sendRequest( 'query' ).then( () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( error ); + expect( err ).toEqual( error ); } ); } ); @@ -2231,14 +2149,14 @@ describe( 'dev-stale-bot/lib', () => { errors: [] }; - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.sendRequest( 'query' ).then( () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( error ); + expect( err ).toEqual( error ); } ); } ); @@ -2251,14 +2169,14 @@ describe( 'dev-stale-bot/lib', () => { ] }; - stubs.GraphQLClient.request.rejects( error ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValue( error ); return githubRepository.sendRequest( 'query' ).then( () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( error ); + expect( err ).toEqual( error ); } ); } ); @@ -2276,15 +2194,16 @@ describe( 'dev-stale-bot/lib', () => { headers: new Map( [ [ 'x-ratelimit-reset', resetTimestamp ] ] ) }; - stubs.GraphQLClient.request.onCall( 0 ).rejects( error ).onCall( 1 ).resolves( payload ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValueOnce( error ); + vi.mocked( graphQLClientRequestMock ).mockResolvedValueOnce( payload ); const sendPromise = githubRepository.sendRequest( 'query' ); - expect( stubs.GraphQLClient.request.callCount ).to.equal( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); - await clock.tickAsync( timeToWait * 1000 ); + await vi.advanceTimersByTimeAsync( timeToWait * 1000 ); - expect( stubs.GraphQLClient.request.callCount ).to.equal( 2 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 2 ); return sendPromise; } ); @@ -2297,17 +2216,18 @@ describe( 'dev-stale-bot/lib', () => { headers: new Map( [ [ 'retry-after', timeToWait ] ] ) }; - stubs.GraphQLClient.request.onCall( 0 ).rejects( error ).onCall( 1 ).resolves( payload ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValueOnce( error ); + vi.mocked( graphQLClientRequestMock ).mockResolvedValueOnce( payload ); const sendPromise = githubRepository.sendRequest( 'query' ); - expect( stubs.GraphQLClient.request.callCount ).to.equal( 1 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 1 ); - await clock.tickAsync( timeToWait * 1000 ); + await vi.advanceTimersByTimeAsync( timeToWait * 1000 ); - expect( stubs.GraphQLClient.request.callCount ).to.equal( 2 ); + expect( vi.mocked( graphQLClientRequestMock ) ).toHaveBeenCalledTimes( 2 ); - return sendPromise; + await sendPromise; } ); it( 'should log the progress and resolve with the payload after API rate is reset', async () => { @@ -2323,30 +2243,34 @@ describe( 'dev-stale-bot/lib', () => { headers: new Map( [ [ 'x-ratelimit-reset', resetTimestamp ] ] ) }; - stubs.GraphQLClient.request.onCall( 0 ).rejects( error ).onCall( 1 ).resolves( payload ); + vi.mocked( graphQLClientRequestMock ).mockRejectedValueOnce( error ); + vi.mocked( graphQLClientRequestMock ).mockResolvedValueOnce( payload ); const sendPromise = githubRepository.sendRequest( 'query' ); - await clock.tickAsync( 0 ); + await vi.advanceTimersByTimeAsync( 0 ); - expect( stubs.logger.info.callCount ).to.equal( 1 ); - expect( stubs.logger.info.getCall( 0 ).args[ 0 ] ).to.equal( + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenLastCalledWith( '⛔ The API limit is exceeded. Request is paused for 28 minutes.' ); - await clock.tickAsync( timeToWait * 1000 ); + await vi.advanceTimersByTimeAsync( timeToWait * 1000 ); - expect( stubs.logger.info.callCount ).to.equal( 2 ); - expect( stubs.logger.info.getCall( 1 ).args[ 0 ] ).to.equal( '📍 Re-sending postponed request.' ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenLastCalledWith( '📍 Re-sending postponed request.' ); - return sendPromise.then( result => { - expect( result ).to.equal( payload ); - } ); + const result = await sendPromise; + expect( result ).toEqual( payload ); } ); } ); function paginateRequest( dataToPaginate, paginator ) { for ( const entry of dataToPaginate ) { + if ( entry.error ) { + vi.mocked( graphQLClientRequestMock ).mockRejectedValueOnce( entry.error ); + } + const entryIndex = dataToPaginate.indexOf( entry ); const isLastEntry = entryIndex === dataToPaginate.length - 1; const pageInfo = isLastEntry ? pageInfoNoNextPage : pageInfoWithNextPage; @@ -2357,7 +2281,7 @@ describe( 'dev-stale-bot/lib', () => { entryIndex } ); - stubs.GraphQLClient.request.onCall( entryIndex ).resolves( result ); + vi.mocked( graphQLClientRequestMock ).mockResolvedValueOnce( result ); } } } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js b/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js index 53fbed7ea..6195a76dd 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js @@ -3,8 +3,8 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const findStaleDate = require( '../../lib/utils/findstaledate' ); +import { describe, it, expect, beforeEach } from 'vitest'; +import findStaleDate from '../../lib/utils/findstaledate'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'findStaleDate', () => { @@ -17,7 +17,7 @@ describe( 'dev-stale-bot/lib/utils', () => { } ); it( 'should be a function', () => { - expect( findStaleDate ).to.be.a( 'function' ); + expect( findStaleDate ).toBeInstanceOf( Function ); } ); it( 'should return date when stale label was set', () => { @@ -27,7 +27,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ] }; - expect( findStaleDate( issue, optionsBase ) ).to.equal( '2022-12-01T00:00:00Z' ); + expect( findStaleDate( issue, optionsBase ) ).toEqual( '2022-12-01T00:00:00Z' ); } ); it( 'should return date when stale label was set if issue has multiple different events', () => { @@ -42,7 +42,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ] }; - expect( findStaleDate( issue, optionsBase ) ).to.equal( '2022-12-01T00:00:00Z' ); + expect( findStaleDate( issue, optionsBase ) ).toEqual( '2022-12-01T00:00:00Z' ); } ); it( 'should return most recent date when stale label was set if issue has multiple stale label events', () => { @@ -57,7 +57,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ] }; - expect( findStaleDate( issue, optionsBase ) ).to.equal( '2022-12-06T00:00:00Z' ); + expect( findStaleDate( issue, optionsBase ) ).toEqual( '2022-12-06T00:00:00Z' ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js index 7d17211d8..9dea900de 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js @@ -3,8 +3,8 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const isIssueOrPullRequestActive = require( '../../lib/utils/isissueorpullrequestactive' ); +import { describe, it, expect, beforeEach } from 'vitest'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'isIssueOrPullRequestActive', () => { @@ -29,7 +29,7 @@ describe( 'dev-stale-bot/lib/utils', () => { } ); it( 'should be a function', () => { - expect( isIssueOrPullRequestActive ).to.be.a( 'function' ); + expect( isIssueOrPullRequestActive ).toBeInstanceOf( Function ); } ); it( 'should return true for issue created after stale date', () => { @@ -38,7 +38,7 @@ describe( 'dev-stale-bot/lib/utils', () => { createdAt: afterStaleDate }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.true; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( true ); } ); it( 'should return false for issue created before stale date', () => { @@ -47,7 +47,7 @@ describe( 'dev-stale-bot/lib/utils', () => { createdAt: beforeStaleDate }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( false ); } ); it( 'should return true for issue edited after stale date', () => { @@ -56,7 +56,7 @@ describe( 'dev-stale-bot/lib/utils', () => { lastEditedAt: afterStaleDate }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.true; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( true ); } ); it( 'should return false for issue edited before stale date', () => { @@ -65,7 +65,7 @@ describe( 'dev-stale-bot/lib/utils', () => { lastEditedAt: beforeStaleDate }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( false ); } ); it( 'should return true for issue with reaction after stale date', () => { @@ -74,7 +74,7 @@ describe( 'dev-stale-bot/lib/utils', () => { lastReactedAt: afterStaleDate }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.true; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( true ); } ); it( 'should return false for issue with reaction before stale date', () => { @@ -83,7 +83,7 @@ describe( 'dev-stale-bot/lib/utils', () => { lastReactedAt: beforeStaleDate }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( false ); } ); it( 'should return true for issue with activity after stale date', () => { @@ -95,7 +95,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ] }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.true; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( true ); } ); it( 'should return false for issue without activity after stale date', () => { @@ -107,7 +107,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ] }; - expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestActive( issue, staleDate, optionsBase ) ).toEqual( false ); } ); it( 'should return true for issue with activity after stale date and its author is not ignored', () => { @@ -124,7 +124,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ignoredActivityLogins: [ 'CKEditorBot' ] }; - expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).to.be.true; + expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).toEqual( true ); } ); it( 'should return false for issue with activity after stale date but its author is ignored', () => { @@ -141,7 +141,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ignoredActivityLogins: [ 'CKEditorBot' ] }; - expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).to.be.false; + expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).toEqual( false ); } ); it( 'should return true for issue with activity after stale date and label is not ignored', () => { @@ -158,7 +158,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ignoredActivityLabels: [ 'status:stale' ] }; - expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).to.be.true; + expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).toEqual( true ); } ); it( 'should return false for issue with activity after stale date but label is ignored', () => { @@ -175,7 +175,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ignoredActivityLabels: [ 'status:stale' ] }; - expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).to.be.false; + expect( isIssueOrPullRequestActive( issue, staleDate, options ) ).toEqual( false ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js index c578a0310..a61d5dc71 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js @@ -3,13 +3,17 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import isIssueOrPullRequestToClose from '../../lib/utils/isissueorpullrequesttoclose'; +import findStaleDate from '../../lib/utils/findstaledate'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; + +vi.mock( '../../lib/utils/findstaledate' ); +vi.mock( '../../lib/utils/isissueorpullrequestactive' ); describe( 'dev-stale-bot/lib/utils', () => { describe( 'isIssueOrPullRequestToClose', () => { - let isIssueOrPullRequestToClose, staleDate, afterStaleDate, beforeStaleDate, issueBase, optionsBase, stubs; + let staleDate, afterStaleDate, beforeStaleDate, issueBase, optionsBase; beforeEach( () => { staleDate = '2022-12-01T00:00:00Z'; @@ -22,27 +26,18 @@ describe( 'dev-stale-bot/lib/utils', () => { closeDate: staleDate }; - stubs = { - findStaleDate: sinon.stub().returns( staleDate ), - isIssueOrPullRequestActive: sinon.stub() - }; - - isIssueOrPullRequestToClose = proxyquire( '../../lib/utils/isissueorpullrequesttoclose', { - './findstaledate': stubs.findStaleDate, - './isissueorpullrequestactive': stubs.isIssueOrPullRequestActive - } ); + vi.mocked( findStaleDate ).mockReturnValue( staleDate ); } ); it( 'should be a function', () => { - expect( isIssueOrPullRequestToClose ).to.be.a( 'function' ); + expect( isIssueOrPullRequestToClose ).toBeInstanceOf( Function ); } ); it( 'should get the stale date from issue activity', () => { isIssueOrPullRequestToClose( issueBase, optionsBase ); - expect( stubs.findStaleDate.calledOnce ).to.equal( true ); - expect( stubs.findStaleDate.getCall( 0 ).args[ 0 ] ).to.equal( issueBase ); - expect( stubs.findStaleDate.getCall( 0 ).args[ 1 ] ).to.equal( optionsBase ); + expect( vi.mocked( findStaleDate ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( findStaleDate ) ).toHaveBeenCalledWith( issueBase, optionsBase ); } ); it( 'should not check issue activity if time to close has not passed', () => { @@ -50,7 +45,7 @@ describe( 'dev-stale-bot/lib/utils', () => { isIssueOrPullRequestToClose( issueBase, optionsBase ); - expect( stubs.isIssueOrPullRequestActive.called ).to.equal( false ); + expect( vi.mocked( isIssueOrPullRequestActive ) ).not.toHaveBeenCalled(); } ); it( 'should check issue activity if time to close has passed', () => { @@ -58,38 +53,36 @@ describe( 'dev-stale-bot/lib/utils', () => { isIssueOrPullRequestToClose( issueBase, optionsBase ); - expect( stubs.isIssueOrPullRequestActive.calledOnce ).to.equal( true ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 0 ] ).to.equal( issueBase ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 1 ] ).to.equal( staleDate ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 2 ] ).to.equal( optionsBase ); + expect( vi.mocked( isIssueOrPullRequestActive ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( isIssueOrPullRequestActive ) ).toHaveBeenCalledWith( issueBase, staleDate, optionsBase ); } ); it( 'should return true if issue is not active after stale date and time to close has passed', () => { optionsBase.closeDate = afterStaleDate; - stubs.isIssueOrPullRequestActive.returns( false ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( false ); - expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).to.be.true; + expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).toEqual( true ); } ); it( 'should return false if issue is not active after stale date and time to close has not passed', () => { optionsBase.closeDate = beforeStaleDate; - stubs.isIssueOrPullRequestActive.returns( false ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( false ); - expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).toEqual( false ); } ); it( 'should return false if issue is active after stale date and time to close has passed', () => { optionsBase.closeDate = afterStaleDate; - stubs.isIssueOrPullRequestActive.returns( true ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( true ); - expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).toEqual( false ); } ); it( 'should return false if issue is active after stale date and time to close has not passed', () => { optionsBase.closeDate = beforeStaleDate; - stubs.isIssueOrPullRequestActive.returns( true ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( true ); - expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestToClose( issueBase, optionsBase ) ).toEqual( false ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js index 36a6bd09b..dbef32a07 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js @@ -3,13 +3,15 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; +import isIssueOrPullRequestToStale from '../../lib/utils/isissueorpullrequesttostale'; + +vi.mock( '../../lib/utils/isissueorpullrequestactive' ); describe( 'dev-stale-bot/lib/utils', () => { describe( 'isIssueOrPullRequestToStale', () => { - let isIssueOrPullRequestToStale, staleDate, issueBase, optionsBase, stubs; + let staleDate, issueBase, optionsBase; beforeEach( () => { staleDate = '2022-12-01T00:00:00Z'; @@ -19,39 +21,29 @@ describe( 'dev-stale-bot/lib/utils', () => { optionsBase = { staleDate }; - - stubs = { - isIssueOrPullRequestActive: sinon.stub() - }; - - isIssueOrPullRequestToStale = proxyquire( '../../lib/utils/isissueorpullrequesttostale', { - './isissueorpullrequestactive': stubs.isIssueOrPullRequestActive - } ); } ); it( 'should be a function', () => { - expect( isIssueOrPullRequestToStale ).to.be.a( 'function' ); + expect( isIssueOrPullRequestToStale ).toBeInstanceOf( Function ); } ); it( 'should check issue activity', () => { isIssueOrPullRequestToStale( issueBase, optionsBase ); - expect( stubs.isIssueOrPullRequestActive.calledOnce ).to.equal( true ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 0 ] ).to.equal( issueBase ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 1 ] ).to.equal( staleDate ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 2 ] ).to.equal( optionsBase ); + expect( vi.mocked( isIssueOrPullRequestActive ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( isIssueOrPullRequestActive ) ).toHaveBeenLastCalledWith( issueBase, staleDate, optionsBase ); } ); it( 'should return true if issue is not active after stale date', () => { - stubs.isIssueOrPullRequestActive.returns( false ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( false ); - expect( isIssueOrPullRequestToStale( issueBase, optionsBase ) ).to.be.true; + expect( isIssueOrPullRequestToStale( issueBase, optionsBase ) ).toEqual( true ); } ); it( 'should return false if issue is active after stale date', () => { - stubs.isIssueOrPullRequestActive.returns( true ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( true ); - expect( isIssueOrPullRequestToStale( issueBase, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestToStale( issueBase, optionsBase ) ).toEqual( false ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js index 82ccc69ab..7df5df171 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js @@ -3,13 +3,17 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import findStaleDate from '../../lib/utils/findstaledate'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; +import isIssueOrPullRequestToUnstale from '../../lib/utils/isissueorpullrequesttounstale'; + +vi.mock( '../../lib/utils/findstaledate' ); +vi.mock( '../../lib/utils/isissueorpullrequestactive' ); describe( 'dev-stale-bot/lib/utils', () => { describe( 'isIssueOrPullRequestToUnstale', () => { - let isIssueOrPullRequestToUnstale, staleDate, issueBase, optionsBase, stubs; + let staleDate, issueBase, optionsBase; beforeEach( () => { staleDate = '2022-12-01T00:00:00Z'; @@ -18,48 +22,37 @@ describe( 'dev-stale-bot/lib/utils', () => { optionsBase = {}; - stubs = { - findStaleDate: sinon.stub().returns( staleDate ), - isIssueOrPullRequestActive: sinon.stub() - }; - - isIssueOrPullRequestToUnstale = proxyquire( '../../lib/utils/isissueorpullrequesttounstale', { - './findstaledate': stubs.findStaleDate, - './isissueorpullrequestactive': stubs.isIssueOrPullRequestActive - } ); + vi.mocked( findStaleDate ).mockReturnValue( staleDate ); } ); it( 'should be a function', () => { - expect( isIssueOrPullRequestToUnstale ).to.be.a( 'function' ); + expect( isIssueOrPullRequestToUnstale ).toBeInstanceOf( Function ); } ); it( 'should get the stale date from issue activity', () => { isIssueOrPullRequestToUnstale( issueBase, optionsBase ); - expect( stubs.findStaleDate.calledOnce ).to.equal( true ); - expect( stubs.findStaleDate.getCall( 0 ).args[ 0 ] ).to.equal( issueBase ); - expect( stubs.findStaleDate.getCall( 0 ).args[ 1 ] ).to.equal( optionsBase ); + expect( vi.mocked( findStaleDate ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( findStaleDate ) ).toHaveBeenCalledWith( issueBase, optionsBase ); } ); it( 'should check issue activity', () => { isIssueOrPullRequestToUnstale( issueBase, optionsBase ); - expect( stubs.isIssueOrPullRequestActive.calledOnce ).to.equal( true ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 0 ] ).to.equal( issueBase ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 1 ] ).to.equal( staleDate ); - expect( stubs.isIssueOrPullRequestActive.getCall( 0 ).args[ 2 ] ).to.equal( optionsBase ); + expect( vi.mocked( isIssueOrPullRequestActive ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( isIssueOrPullRequestActive ) ).toHaveBeenCalledWith( issueBase, staleDate, optionsBase ); } ); it( 'should return true if issue is active after stale date', () => { - stubs.isIssueOrPullRequestActive.returns( true ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( true ); - expect( isIssueOrPullRequestToUnstale( issueBase, optionsBase ) ).to.be.true; + expect( isIssueOrPullRequestToUnstale( issueBase, optionsBase ) ).toEqual( true ); } ); it( 'should return false if issue is active after stale date', () => { - stubs.isIssueOrPullRequestActive.returns( false ); + vi.mocked( isIssueOrPullRequestActive ).mockReturnValue( false ); - expect( isIssueOrPullRequestToUnstale( issueBase, optionsBase ) ).to.be.false; + expect( isIssueOrPullRequestToUnstale( issueBase, optionsBase ) ).toEqual( false ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js index 7dad5db54..ef25a8e8f 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js @@ -3,13 +3,13 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const isPendingIssueStale = require( '../../lib/utils/ispendingissuestale' ); +import { describe, it, expect } from 'vitest'; +import isPendingIssueStale from '../../lib/utils/ispendingissuestale'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'isPendingIssueStale', () => { it( 'should be a function', () => { - expect( isPendingIssueStale ).to.be.a( 'function' ); + expect( isPendingIssueStale ).toBeInstanceOf( Function ); } ); it( 'should return false if issue does not have any label', () => { @@ -20,7 +20,7 @@ describe( 'dev-stale-bot/lib/utils', () => { staleLabels: [ 'pending:feedback' ] }; - expect( isPendingIssueStale( issue, options ) ).to.be.false; + expect( isPendingIssueStale( issue, options ) ).toEqual( false ); } ); it( 'should return false if issue does not have a pending label', () => { @@ -31,7 +31,7 @@ describe( 'dev-stale-bot/lib/utils', () => { staleLabels: [ 'pending:feedback' ] }; - expect( isPendingIssueStale( issue, options ) ).to.be.false; + expect( isPendingIssueStale( issue, options ) ).toEqual( false ); } ); it( 'should return false if issue does not have all pending labels', () => { @@ -42,7 +42,7 @@ describe( 'dev-stale-bot/lib/utils', () => { staleLabels: [ 'pending:feedback', 'pending:even-more-feedback' ] }; - expect( isPendingIssueStale( issue, options ) ).to.be.false; + expect( isPendingIssueStale( issue, options ) ).toEqual( false ); } ); it( 'should return true if issue have all pending labels - single label', () => { @@ -53,7 +53,7 @@ describe( 'dev-stale-bot/lib/utils', () => { staleLabels: [ 'pending:feedback' ] }; - expect( isPendingIssueStale( issue, options ) ).to.be.true; + expect( isPendingIssueStale( issue, options ) ).toEqual( true ); } ); it( 'should return true if issue have all pending labels - multiple labels', () => { @@ -64,7 +64,7 @@ describe( 'dev-stale-bot/lib/utils', () => { staleLabels: [ 'pending:feedback', 'pending:even-more-feedback' ] }; - expect( isPendingIssueStale( issue, options ) ).to.be.true; + expect( isPendingIssueStale( issue, options ) ).toEqual( true ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js index 4f506d831..0d086a6be 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js @@ -3,14 +3,15 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import isPendingIssueToStale from '../../lib/utils/ispendingissuetostale'; +import isPendingIssueStale from '../../lib/utils/ispendingissuestale'; + +vi.mock( '../../lib/utils/ispendingissuestale' ); describe( 'dev-stale-bot/lib/utils', () => { describe( 'isPendingIssueToStale', () => { - let isPendingIssueToStale, issueBase, optionsBase, stubs; - let staleDatePendingIssue, afterStaleDatePendingIssue, beforeStaleDatePendingIssue; + let issueBase, optionsBase, staleDatePendingIssue, afterStaleDatePendingIssue, beforeStaleDatePendingIssue; beforeEach( () => { staleDatePendingIssue = '2022-12-01T00:00:00Z'; @@ -24,67 +25,58 @@ describe( 'dev-stale-bot/lib/utils', () => { optionsBase = { staleDatePendingIssue }; - - stubs = { - isPendingIssueStale: sinon.stub() - }; - - isPendingIssueToStale = proxyquire( '../../lib/utils/ispendingissuetostale', { - './ispendingissuestale': stubs.isPendingIssueStale - } ); } ); it( 'should be a function', () => { - expect( isPendingIssueToStale ).to.be.a( 'function' ); + expect( isPendingIssueToStale ).toBeInstanceOf( Function ); } ); it( 'should check if issue is stale', () => { isPendingIssueToStale( issueBase, optionsBase ); - expect( stubs.isPendingIssueStale.calledOnce ).to.equal( true ); - expect( stubs.isPendingIssueStale.getCall( 0 ).args[ 0 ] ).to.equal( issueBase ); - expect( stubs.isPendingIssueStale.getCall( 0 ).args[ 1 ] ).to.equal( optionsBase ); + expect( vi.mocked( isPendingIssueStale ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( isPendingIssueStale ) ).toHaveBeenCalledWith( issueBase, optionsBase ); } ); it( 'should return false if issue is already stale', () => { - stubs.isPendingIssueStale.returns( true ); + vi.mocked( isPendingIssueStale ).mockReturnValue( true ); - expect( isPendingIssueToStale( issueBase, optionsBase ) ).to.be.false; + expect( isPendingIssueToStale( issueBase, optionsBase ) ).toEqual( false ); } ); it( 'should return false if issue does not have any comment', () => { - stubs.isPendingIssueStale.returns( false ); + vi.mocked( isPendingIssueStale ).mockReturnValue( false ); - expect( isPendingIssueToStale( issueBase, optionsBase ) ).to.be.false; + expect( isPendingIssueToStale( issueBase, optionsBase ) ).toEqual( false ); } ); it( 'should return false if last comment was created by a community member', () => { - stubs.isPendingIssueStale.returns( false ); + vi.mocked( isPendingIssueStale ).mockReturnValue( false ); issueBase.lastComment = { isExternal: true }; - expect( isPendingIssueToStale( issueBase, optionsBase ) ).to.be.false; + expect( isPendingIssueToStale( issueBase, optionsBase ) ).toEqual( false ); } ); it( 'should return false if last comment was created by a team member and time to stale has not passed', () => { - stubs.isPendingIssueStale.returns( false ); + vi.mocked( isPendingIssueStale ).mockReturnValue( false ); issueBase.lastComment = { isExternal: false, createdAt: afterStaleDatePendingIssue }; - expect( isPendingIssueToStale( issueBase, optionsBase ) ).to.be.false; + expect( isPendingIssueToStale( issueBase, optionsBase ) ).toEqual( false ); } ); it( 'should return true if last comment was created by a team member and time to stale has passed', () => { - stubs.isPendingIssueStale.returns( false ); + vi.mocked( isPendingIssueStale ).mockReturnValue( false ); issueBase.lastComment = { isExternal: false, createdAt: beforeStaleDatePendingIssue }; - expect( isPendingIssueToStale( issueBase, optionsBase ) ).to.be.true; + expect( isPendingIssueToStale( issueBase, optionsBase ) ).toEqual( true ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js index c5676427f..395dcc7f7 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js @@ -3,13 +3,13 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const isPendingIssueToUnlabel = require( '../../lib/utils/ispendingissuetounlabel' ); +import { describe, it, expect } from 'vitest'; +import isPendingIssueToUnlabel from '../../lib/utils/ispendingissuetounlabel'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'isPendingIssueToUnlabel', () => { it( 'should be a function', () => { - expect( isPendingIssueToUnlabel ).to.be.a( 'function' ); + expect( isPendingIssueToUnlabel ).toBeInstanceOf( Function ); } ); it( 'should return false if issue does not have any comment', () => { @@ -17,7 +17,7 @@ describe( 'dev-stale-bot/lib/utils', () => { lastComment: null }; - expect( isPendingIssueToUnlabel( issue ) ).to.be.false; + expect( isPendingIssueToUnlabel( issue ) ).toEqual( false ); } ); it( 'should return false if last comment was created by a team member', () => { @@ -27,7 +27,7 @@ describe( 'dev-stale-bot/lib/utils', () => { } }; - expect( isPendingIssueToUnlabel( issue ) ).to.be.false; + expect( isPendingIssueToUnlabel( issue ) ).toEqual( false ); } ); it( 'should return true if last comment was created by a community member', () => { @@ -37,7 +37,7 @@ describe( 'dev-stale-bot/lib/utils', () => { } }; - expect( isPendingIssueToUnlabel( issue ) ).to.be.true; + expect( isPendingIssueToUnlabel( issue ) ).toEqual( true ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js b/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js index 0a804bde5..39672a809 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js @@ -3,45 +3,45 @@ * For licensing, see LICENSE.md. */ -const expect = require( 'chai' ).expect; -const prepareSearchQuery = require( '../../lib/utils/preparesearchquery' ); +import { describe, it, expect } from 'vitest'; +import prepareSearchQuery from '../../lib/utils/preparesearchquery'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'prepareSearchQuery', () => { it( 'should be a function', () => { - expect( prepareSearchQuery ).to.be.a( 'function' ); + expect( prepareSearchQuery ).toBeInstanceOf( Function ); } ); it( 'should prepare a query with repository slug', () => { - expect( prepareSearchQuery( { repositorySlug: 'ckeditor/ckeditor5' } ) ).to.include( 'repo:ckeditor/ckeditor5' ); + expect( prepareSearchQuery( { repositorySlug: 'ckeditor/ckeditor5' } ) ).toContain( 'repo:ckeditor/ckeditor5' ); } ); it( 'should prepare a query for issue', () => { - expect( prepareSearchQuery( { type: 'Issue' } ) ).to.include( 'type:issue' ); + expect( prepareSearchQuery( { type: 'Issue' } ) ).toContain( 'type:issue' ); } ); it( 'should prepare a query for pull request', () => { - expect( prepareSearchQuery( { type: 'PullRequest' } ) ).to.include( 'type:pr' ); + expect( prepareSearchQuery( { type: 'PullRequest' } ) ).toContain( 'type:pr' ); } ); it( 'should prepare a query for issue or pull request', () => { - expect( prepareSearchQuery( {} ) ).to.not.include( 'type:' ); + expect( prepareSearchQuery( {} ) ).not.toContain( 'type:' ); } ); it( 'should prepare a query from specified date', () => { - expect( prepareSearchQuery( { searchDate: '2022-12-01' } ) ).to.include( 'created:<2022-12-01' ); + expect( prepareSearchQuery( { searchDate: '2022-12-01' } ) ).toContain( 'created:<2022-12-01' ); } ); it( 'should prepare a query without specifying a start date', () => { - expect( prepareSearchQuery( {} ) ).to.not.include( 'created:' ); + expect( prepareSearchQuery( {} ) ).not.toContain( 'created:' ); } ); it( 'should prepare a query for open items', () => { - expect( prepareSearchQuery( {} ) ).to.include( 'state:open' ); + expect( prepareSearchQuery( {} ) ).toContain( 'state:open' ); } ); it( 'should prepare a query sorted in descending order by creation date', () => { - expect( prepareSearchQuery( {} ) ).to.include( 'sort:created-desc' ); + expect( prepareSearchQuery( {} ) ).toContain( 'sort:created-desc' ); } ); it( 'should prepare a query with ignored labels', () => { @@ -53,7 +53,7 @@ describe( 'dev-stale-bot/lib/utils', () => { 'domain:accessibility' ]; - expect( prepareSearchQuery( { ignoredLabels } ) ).to.include( + expect( prepareSearchQuery( { ignoredLabels } ) ).toContain( '-label:status:stale -label:support:1 -label:support:2 -label:support:3 -label:domain:accessibility' ); } ); @@ -64,7 +64,7 @@ describe( 'dev-stale-bot/lib/utils', () => { 'type:bug' ]; - expect( prepareSearchQuery( { labels } ) ).to.include( 'label:status:stale label:type:bug' ); + expect( prepareSearchQuery( { labels } ) ).toContain( 'label:status:stale label:type:bug' ); } ); it( 'should prepare a query with all fields separated by space', () => { @@ -76,7 +76,7 @@ describe( 'dev-stale-bot/lib/utils', () => { ignoredLabels: [ 'status:stale' ] }; - expect( prepareSearchQuery( options ) ).to.include( + expect( prepareSearchQuery( options ) ).toContain( 'repo:ckeditor/ckeditor5 created:<2022-12-01 type:issue state:open sort:created-desc label:type:bug -label:status:stale' ); } ); diff --git a/packages/ckeditor5-dev-stale-bot/vitest.config.js b/packages/ckeditor5-dev-stale-bot/vitest.config.js new file mode 100644 index 000000000..d54e69b19 --- /dev/null +++ b/packages/ckeditor5-dev-stale-bot/vitest.config.js @@ -0,0 +1,23 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + testTimeout: 10000, + restoreMocks: true, + include: [ + './tests/githubrepository.js' + ], + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From 28d0a4751e8511ceba8db1534a09eec844e8c62f Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:34:34 +0200 Subject: [PATCH 027/172] Cleanup. --- packages/ckeditor5-dev-stale-bot/bin/stale-bot.js | 2 +- packages/ckeditor5-dev-stale-bot/lib/githubrepository.js | 4 ++-- packages/ckeditor5-dev-stale-bot/vitest.config.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js index 951d3f4b6..d89413a17 100755 --- a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js +++ b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js @@ -32,7 +32,7 @@ async function main() { throw new Error( 'Missing or invalid CLI argument: --config-path' ); } - const config = require( configPath ); + const config = await import( configPath ); validateConfig( config ); diff --git a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js index a5f9bde09..1efc8b5cf 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js +++ b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js @@ -7,12 +7,12 @@ import upath from 'upath'; import fs from 'fs-extra'; import { GraphQLClient } from 'graphql-request'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; -const { +import { addSeconds, fromUnixTime, formatDistanceToNow, differenceInSeconds -} = require( 'date-fns' ); +} from 'date-fns'; import prepareSearchQuery from './utils/preparesearchquery'; import isIssueOrPullRequestToStale from './utils/isissueorpullrequesttostale'; import isIssueOrPullRequestToUnstale from './utils/isissueorpullrequesttounstale'; diff --git a/packages/ckeditor5-dev-stale-bot/vitest.config.js b/packages/ckeditor5-dev-stale-bot/vitest.config.js index d54e69b19..5ad784a28 100644 --- a/packages/ckeditor5-dev-stale-bot/vitest.config.js +++ b/packages/ckeditor5-dev-stale-bot/vitest.config.js @@ -10,7 +10,7 @@ export default defineConfig( { testTimeout: 10000, restoreMocks: true, include: [ - './tests/githubrepository.js' + 'tests/**/*.js' ], coverage: { provider: 'v8', From c6f427d34fc5c380a3e02cda951e58921a7accb2 Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:47:09 +0200 Subject: [PATCH 028/172] Review requests. --- .../bin/circle-workflow-notifier.js | 1 - .../bin/notify-circle-status.js | 1 - .../lib/circle-update-auto-cancel-builds.js | 4 +- packages/ckeditor5-dev-ci/lib/data/index.js | 10 +++++ .../ckeditor5-dev-ci/lib/format-message.js | 6 +-- packages/ckeditor5-dev-ci/lib/index.js | 6 +-- .../lib/trigger-circle-build.js | 4 +- .../lib/utils/get-job-approver.js | 6 +-- packages/ckeditor5-dev-ci/package.json | 1 - .../tests/circle-update-auto-cancel-builds.js | 15 +++---- .../ckeditor5-dev-ci/tests/data/members.js | 2 +- .../ckeditor5-dev-ci/tests/format-message.js | 32 +++++++++----- .../tests/trigger-circle-build.js | 40 ++++++++++-------- .../tests/utils/get-job-approver.js | 42 ++++++++++--------- 14 files changed, 91 insertions(+), 79 deletions(-) create mode 100644 packages/ckeditor5-dev-ci/lib/data/index.js diff --git a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js index f71e8dcc1..e5f517008 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js +++ b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js @@ -8,7 +8,6 @@ /* eslint-env node */ import { execSync } from 'child_process'; -import fetch from 'node-fetch'; import minimist from 'minimist'; import processJobStatuses from '../lib/process-job-statuses'; diff --git a/packages/ckeditor5-dev-ci/bin/notify-circle-status.js b/packages/ckeditor5-dev-ci/bin/notify-circle-status.js index 9cb6bc9d8..ca9fd7f9c 100755 --- a/packages/ckeditor5-dev-ci/bin/notify-circle-status.js +++ b/packages/ckeditor5-dev-ci/bin/notify-circle-status.js @@ -7,7 +7,6 @@ /* eslint-env node */ -import fetch from 'node-fetch'; import slackNotify from 'slack-notify'; import formatMessage from '../lib/format-message'; diff --git a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js index 9526fcee8..6528ebf4e 100644 --- a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -import nodeFetch from 'node-fetch'; - /** * @param options * @param {String} options.circleToken @@ -36,6 +34,6 @@ export default async function circleUpdateAutoCancelBuilds( options ) { const settingsUpdateUrl = `https://circleci.com/api/v2/project/github/${ githubOrganization }/${ githubRepository }/settings`; - return nodeFetch( settingsUpdateUrl, circleRequestOptions ) + return fetch( settingsUpdateUrl, circleRequestOptions ) .then( r => r.json() ); } diff --git a/packages/ckeditor5-dev-ci/lib/data/index.js b/packages/ckeditor5-dev-ci/lib/data/index.js new file mode 100644 index 000000000..f3dd882ac --- /dev/null +++ b/packages/ckeditor5-dev-ci/lib/data/index.js @@ -0,0 +1,10 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { default as _members } from './members.json' with { type: 'json' }; +import { default as _bots } from './bots.json' with { type: 'json' }; + +export const members = _members; +export const bots = _bots; diff --git a/packages/ckeditor5-dev-ci/lib/format-message.js b/packages/ckeditor5-dev-ci/lib/format-message.js index 3faa50c70..e22262e0c 100644 --- a/packages/ckeditor5-dev-ci/lib/format-message.js +++ b/packages/ckeditor5-dev-ci/lib/format-message.js @@ -5,9 +5,7 @@ /* eslint-env node */ -import nodeFetch from 'node-fetch'; -import bots from './data/bots.json'; -import members from './data/members.json'; +import { bots, members } from './data/index'; const REPOSITORY_REGEXP = /github\.com\/([^/]+)\/([^/]+)/; @@ -194,7 +192,7 @@ function getCommitDetails( triggeringCommitUrl, githubToken ) { } }; - return nodeFetch( apiGithubUrlCommit, options ) + return fetch( apiGithubUrlCommit, options ) .then( response => response.json() ) .then( json => ( { githubAccount: json.author ? json.author.login : null, diff --git a/packages/ckeditor5-dev-ci/lib/index.js b/packages/ckeditor5-dev-ci/lib/index.js index 45193e6d8..ba6e9e0af 100644 --- a/packages/ckeditor5-dev-ci/lib/index.js +++ b/packages/ckeditor5-dev-ci/lib/index.js @@ -3,7 +3,5 @@ * For licensing, see LICENSE.md. */ -export default { - getJobApprover: require( './utils/get-job-approver' ), - members: require( './data/members.json' ) -}; +export { default as getJobApprover } from './utils/get-job-approver'; +export { members } from './data/index'; diff --git a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js index f1a0f8cad..8e2810864 100644 --- a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js @@ -5,8 +5,6 @@ /* eslint-env node */ -import nodeFetch from 'node-fetch'; - /** * @param options * @param {String} options.circleToken @@ -51,7 +49,7 @@ export default async function triggerCircleBuild( options ) { body: JSON.stringify( { branch, parameters } ) }; - return nodeFetch( requestUrl, requestOptions ) + return fetch( requestUrl, requestOptions ) .then( res => res.json() ) .then( response => { if ( response.error_message ) { diff --git a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js index 393239d96..3eb0fa713 100644 --- a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -import nodeFetch from 'node-fetch'; - /** * Returns a promise that resolves to GitHub name of a developer who approved the `jobName` job. * @@ -25,12 +23,12 @@ export default async function getJobApprover( circleCiToken, workflowId, jobName // Find an identifier of a developer who approved an approval job. const workflowJobsUrl = `https://circleci.com/api/v2/workflow/${ workflowId }/job`; - const workflowJobs = await nodeFetch( workflowJobsUrl, circleRequestOptions ).then( r => r.json() ); + const workflowJobs = await fetch( workflowJobsUrl, circleRequestOptions ).then( r => r.json() ); const { approved_by: approvedBy } = workflowJobs.items.find( job => job.name === jobName ); // Find a username based on the identifier. const userDetailsUrl = `https://circleci.com/api/v2/user/${ approvedBy }`; - const { login } = await nodeFetch( userDetailsUrl, circleRequestOptions ).then( r => r.json() ); + const { login } = await fetch( userDetailsUrl, circleRequestOptions ).then( r => r.json() ); return login; } diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 3863a715d..3d2d2f0e0 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -36,7 +36,6 @@ "dependencies": { "@octokit/rest": "^19.0.0", "minimist": "^1.2.8", - "node-fetch": "^2.6.7", "slack-notify": "^2.0.6" }, "devDependencies": { diff --git a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js index 266104aed..42cec6550 100644 --- a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js @@ -5,18 +5,15 @@ import { describe, expect, it, vi } from 'vitest'; import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds'; -import nodeFetch from 'node-fetch'; - -vi.mock( 'node-fetch' ); describe( 'lib/circleUpdateAutoCancelBuilds', () => { it( 'should send a request to CircleCI to update the redundant workflows option', async () => { const response = { foo: 'bar' }; - vi.mocked( nodeFetch ) - .mockResolvedValue( { - json: () => Promise.resolve( response ) - } ); + const fetchMock = vi.fn(); + vi.stubGlobal( 'fetch', fetchMock ); + + fetchMock.mockResolvedValue( { json: () => Promise.resolve( response ) } ); const results = await circleUpdateAutoCancelBuilds( { circleToken: 'circle-token', @@ -27,8 +24,8 @@ describe( 'lib/circleUpdateAutoCancelBuilds', () => { expect( results ).to.deep.equal( response ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-foo/settings', { method: 'patch', diff --git a/packages/ckeditor5-dev-ci/tests/data/members.js b/packages/ckeditor5-dev-ci/tests/data/members.js index 1801c0b0e..9206b8133 100644 --- a/packages/ckeditor5-dev-ci/tests/data/members.js +++ b/packages/ckeditor5-dev-ci/tests/data/members.js @@ -4,7 +4,7 @@ */ import { describe, expect, it } from 'vitest'; -import members from '../../lib/data/members.json'; +import { members } from '../../lib/data/index'; describe( 'lib/data/members', () => { it( 'should be an object', () => { diff --git a/packages/ckeditor5-dev-ci/tests/format-message.js b/packages/ckeditor5-dev-ci/tests/format-message.js index e97e807fd..ebe77c711 100644 --- a/packages/ckeditor5-dev-ci/tests/format-message.js +++ b/packages/ckeditor5-dev-ci/tests/format-message.js @@ -5,26 +5,36 @@ /* eslint-env node */ -import { describe, expect, it, vi } from 'vitest'; -import nodeFetch from 'node-fetch'; -import formatMessage from '../lib/format-message'; +import { describe, expect, it, vi, beforeEach } from 'vitest'; -vi.mock( 'node-fetch' ); +import formatMessage from '../lib/format-message'; -vi.mock( './data/members.json', () => { +vi.mock( '../lib/data', () => { return { - ExampleNick: 'slackId' + members: { + ExampleNick: 'slackId' + }, + bots: [ + 'CKCSBot' + ] }; } ); describe( 'lib/format-message', () => { describe( 'formatMessage()', () => { + let fetchMock; + + beforeEach( () => { + fetchMock = vi.fn(); + vi.stubGlobal( 'fetch', fetchMock ); + } ); + it( 'should be a function', () => { expect( formatMessage ).toBeInstanceOf( Function ); } ); it( 'should display a message for bot if a login is included in the "bots" array', async () => { - vi.mocked( nodeFetch ).mockResolvedValueOnce( { + vi.mocked( fetchMock ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: { @@ -63,7 +73,7 @@ describe( 'lib/format-message', () => { } ); it( 'should display a message for bot if a login is unavailable but author name is included in the "bots" array', async () => { - vi.mocked( nodeFetch ).mockResolvedValueOnce( { + vi.mocked( fetchMock ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: null, @@ -100,7 +110,7 @@ describe( 'lib/format-message', () => { } ); it( 'should mention the channel if a login is unavailable and author name is not included in the "bots" array', async () => { - vi.mocked( nodeFetch ).mockResolvedValueOnce( { + vi.mocked( fetchMock ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: null, @@ -137,7 +147,7 @@ describe( 'lib/format-message', () => { } ); it( 'should find a Slack account based on a GitHub account case-insensitive', async () => { - vi.mocked( nodeFetch ).mockResolvedValueOnce( { + vi.mocked( fetchMock ).mockResolvedValueOnce( { json() { return Promise.resolve( { author: { @@ -172,7 +182,7 @@ describe( 'lib/format-message', () => { expect( message ).to.be.an( 'object' ); expect( message ).to.have.property( 'text' ); - expect( message.text ).toEqual( ' (Example Nick), could you take a look?' ); + expect( message.text ).toEqual( '<@slackId>, could you take a look?' ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js index 5d99bb219..02d9646c7 100644 --- a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js @@ -3,15 +3,19 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi } from 'vitest'; -import nodeFetch from 'node-fetch'; +import { describe, expect, it, vi, beforeEach } from 'vitest'; import triggerCircleBuild from '../lib/trigger-circle-build'; -vi.mock( 'node-fetch' ); - describe( 'lib/triggerCircleBuild', () => { + let fetchMock; + + beforeEach( () => { + fetchMock = vi.fn(); + vi.stubGlobal( 'fetch', fetchMock ); + } ); + it( 'should send a POST request to the CircleCI service', async () => { - vi.mocked( nodeFetch ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -24,8 +28,8 @@ describe( 'lib/triggerCircleBuild', () => { repositorySlug: 'ckeditor/ckeditor5-dev' } ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { method: 'post', @@ -45,7 +49,7 @@ describe( 'lib/triggerCircleBuild', () => { } ); it( 'should include the "isRelease=true" parameter when passing the `releaseBranch` option (the same release branch)', async () => { - vi.mocked( nodeFetch ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -59,8 +63,8 @@ describe( 'lib/triggerCircleBuild', () => { releaseBranch: 'master' } ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { method: 'post', @@ -81,7 +85,7 @@ describe( 'lib/triggerCircleBuild', () => { } ); it( 'should include the "isRelease=false" parameter when passing the `releaseBranch` option', async () => { - vi.mocked( nodeFetch ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -95,8 +99,8 @@ describe( 'lib/triggerCircleBuild', () => { releaseBranch: 'release' } ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { method: 'post', @@ -117,7 +121,7 @@ describe( 'lib/triggerCircleBuild', () => { } ); it( 'should include the "triggerRepositorySlug" parameter when passing the `releaseBranch` option', async () => { - vi.mocked( nodeFetch ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { json: () => Promise.resolve( { error_message: null } ) @@ -131,8 +135,8 @@ describe( 'lib/triggerCircleBuild', () => { triggerRepositorySlug: 'ckeditor/ckeditor5' } ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledWith( + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { method: 'post', @@ -153,7 +157,7 @@ describe( 'lib/triggerCircleBuild', () => { } ); it( 'should reject a promise when CircleCI responds with an error containing error_message property', async () => { - vi.mocked( nodeFetch ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { json: () => Promise.resolve( { error_message: 'HTTP 404' } ) @@ -176,7 +180,7 @@ describe( 'lib/triggerCircleBuild', () => { } ); it( 'should reject a promise when CircleCI responds with an error containing message property', async () => { - vi.mocked( nodeFetch ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { json: () => Promise.resolve( { message: 'HTTP 404' } ) diff --git a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js index 48093b910..e809d3807 100644 --- a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js @@ -5,13 +5,13 @@ import { describe, expect, it, vi } from 'vitest'; import getJobApprover from '../../lib/utils/get-job-approver'; -import nodeFetch from 'node-fetch'; - -vi.mock( 'node-fetch' ); describe( 'lib/utils/getJobApprover', () => { it( 'should return a GitHub login name of a user who approved a job in given workflow', async () => { - vi.mocked( nodeFetch ) + const fetchMock = vi.fn(); + vi.stubGlobal( 'fetch', fetchMock ); + + fetchMock .mockResolvedValueOnce( { json: () => Promise.resolve( { items: [ @@ -30,20 +30,24 @@ describe( 'lib/utils/getJobApprover', () => { expect( login ).to.equal( 'foo' ); - expect( vi.mocked( nodeFetch ) ).toHaveBeenCalledTimes( 2 ); - - const [ firstUrl, firstOptions ] = vi.mocked( nodeFetch ).mock.calls[ 0 ]; - - expect( firstUrl ).to.equal( 'https://circleci.com/api/v2/workflow/abc-123-abc-456/job' ); - expect( firstOptions ).to.have.property( 'method', 'get' ); - expect( firstOptions ).to.have.property( 'headers' ); - expect( firstOptions.headers ).to.have.property( 'Circle-Token', 'circle-token' ); - - const [ secondUrl, secondOptions ] = vi.mocked( nodeFetch ).mock.calls[ 1 ]; - - expect( secondUrl ).to.equal( 'https://circleci.com/api/v2/user/foo-unique-id' ); - expect( secondOptions ).to.have.property( 'method', 'get' ); - expect( secondOptions ).to.have.property( 'headers' ); - expect( secondOptions.headers ).to.have.property( 'Circle-Token', 'circle-token' ); + expect( vi.mocked( fetchMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fetchMock ) ).toHaveBeenNthCalledWith( 1, + 'https://circleci.com/api/v2/workflow/abc-123-abc-456/job', + { + 'method': 'get', + 'headers': expect.objectContaining( { + 'Circle-Token': 'circle-token' + } ) + } + ); + expect( vi.mocked( fetchMock ) ).toHaveBeenNthCalledWith( 2, + 'https://circleci.com/api/v2/user/foo-unique-id', + { + 'method': 'get', + 'headers': expect.objectContaining( { + 'Circle-Token': 'circle-token' + } ) + } + ); } ); } ); From dc00635feea9c0a16471c91d8380430971ed504c Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:49:09 +0200 Subject: [PATCH 029/172] Updated eslint config. --- .eslintrc.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 22527e8f2..799de05f1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -13,7 +13,11 @@ module.exports = { ignorePatterns: [ '**/dist/*', '**/coverage/**', - '**/node_modules/**' + '**/node_modules/**', + + // ESLint does not understand `import ... with { ... }`. + // See: https://github.com/eslint/eslint/discussions/15305. + 'packages/ckeditor5-dev-ci/lib/data/index.js' ], rules: { 'no-console': 'off', From 3680010a22f6269cb97dd459b3d7fb5302f92b46 Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:38:55 +0200 Subject: [PATCH 030/172] Added file extensions to imports. --- .eslintrc.js | 14 ++++++++++++-- .../bin/circle-disable-auto-cancel-builds.js | 2 +- .../bin/circle-enable-auto-cancel-builds.js | 2 +- .../bin/circle-workflow-notifier.js | 2 +- .../bin/is-job-triggered-by-member.js | 2 +- .../ckeditor5-dev-ci/bin/notify-circle-status.js | 2 +- .../ckeditor5-dev-ci/bin/notify-travis-status.js | 2 +- .../ckeditor5-dev-ci/bin/trigger-circle-build.js | 2 +- packages/ckeditor5-dev-ci/lib/format-message.js | 2 +- packages/ckeditor5-dev-ci/lib/index.js | 4 ++-- .../lib/is-job-triggered-by-member.js | 2 +- .../tests/circle-update-auto-cancel-builds.js | 2 +- packages/ckeditor5-dev-ci/tests/data/members.js | 2 +- packages/ckeditor5-dev-ci/tests/format-message.js | 2 +- .../tests/is-job-triggered-by-member.js | 4 ++-- .../ckeditor5-dev-ci/tests/process-job-statuses.js | 2 +- .../ckeditor5-dev-ci/tests/trigger-circle-build.js | 2 +- .../tests/utils/get-job-approver.js | 2 +- 18 files changed, 31 insertions(+), 21 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 799de05f1..ff627feb1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,6 +22,7 @@ module.exports = { rules: { 'no-console': 'off', 'ckeditor5-rules/require-file-extensions-in-imports': 'off', + 'mocha/no-global-tests': 'off', // TODO: remove when all mocha tests are removed. 'ckeditor5-rules/license-header': [ 'error', { headerLines: [ '/**', @@ -33,9 +34,18 @@ module.exports = { }, overrides: [ { - files: [ './packages/ckeditor5-dev-build-tools/tests/**/*' ], + files: [ + // TODO: add packages as they are migrated to ESM. + './packages/ckeditor5-dev-ci/**/*' + ], rules: { - 'mocha/no-global-tests': 'off' + 'mocha/no-global-tests': 'error', + 'ckeditor5-rules/require-file-extensions-in-imports': [ + 'error', + { + extensions: [ '.ts', '.js', '.json' ] + } + ] } } ] diff --git a/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js index 16e0c926e..7e778b867 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/bin/circle-disable-auto-cancel-builds.js @@ -5,7 +5,7 @@ * For licensing, see LICENSE.md. */ -import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds'; +import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds.js'; /** * This script updates CircleCI settings to disable the "Auto-cancel redundant workflows" option. diff --git a/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js index f4cfbef89..15ae8cf69 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/bin/circle-enable-auto-cancel-builds.js @@ -5,7 +5,7 @@ * For licensing, see LICENSE.md. */ -import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds'; +import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds.js'; /** * This script updates CircleCI settings to enable the "Auto-cancel redundant workflows" option. diff --git a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js index e5f517008..9454dcf0d 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js +++ b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js @@ -9,7 +9,7 @@ import { execSync } from 'child_process'; import minimist from 'minimist'; -import processJobStatuses from '../lib/process-job-statuses'; +import processJobStatuses from '../lib/process-job-statuses.js'; // This script allows the creation of a new job within a workflow that will be executed // in the end, when all other jobs will be finished or errored. diff --git a/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js b/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js index 77e171659..adc296d0a 100755 --- a/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js +++ b/packages/ckeditor5-dev-ci/bin/is-job-triggered-by-member.js @@ -5,7 +5,7 @@ * For licensing, see LICENSE.md. */ -import isJobTriggeredByMember from '../lib/is-job-triggered-by-member'; +import isJobTriggeredByMember from '../lib/is-job-triggered-by-member.js'; /** * This script checks if a user that approved an approval job could do that. diff --git a/packages/ckeditor5-dev-ci/bin/notify-circle-status.js b/packages/ckeditor5-dev-ci/bin/notify-circle-status.js index ca9fd7f9c..3aed9a6cb 100755 --- a/packages/ckeditor5-dev-ci/bin/notify-circle-status.js +++ b/packages/ckeditor5-dev-ci/bin/notify-circle-status.js @@ -8,7 +8,7 @@ /* eslint-env node */ import slackNotify from 'slack-notify'; -import formatMessage from '../lib/format-message'; +import formatMessage from '../lib/format-message.js'; // This script assumes that is being executed on Circle CI. // Step it is used on should have set value: `when: on_fail`, since it does not diff --git a/packages/ckeditor5-dev-ci/bin/notify-travis-status.js b/packages/ckeditor5-dev-ci/bin/notify-travis-status.js index 0eea57603..93b6923f9 100755 --- a/packages/ckeditor5-dev-ci/bin/notify-travis-status.js +++ b/packages/ckeditor5-dev-ci/bin/notify-travis-status.js @@ -7,7 +7,7 @@ /* eslint-env node */ -import formatMessage from '../lib/format-message'; +import formatMessage from '../lib/format-message.js'; import slackNotify from 'slack-notify'; const ALLOWED_BRANCHES = [ diff --git a/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js b/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js index 290e35d87..17fd1da10 100755 --- a/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/bin/trigger-circle-build.js @@ -5,7 +5,7 @@ * For licensing, see LICENSE.md. */ -import triggerCircleBuild from '../lib/trigger-circle-build'; +import triggerCircleBuild from '../lib/trigger-circle-build.js'; /** * This script triggers a new CircleCI build. diff --git a/packages/ckeditor5-dev-ci/lib/format-message.js b/packages/ckeditor5-dev-ci/lib/format-message.js index e22262e0c..ce9758ad3 100644 --- a/packages/ckeditor5-dev-ci/lib/format-message.js +++ b/packages/ckeditor5-dev-ci/lib/format-message.js @@ -5,7 +5,7 @@ /* eslint-env node */ -import { bots, members } from './data/index'; +import { bots, members } from './data/index.js'; const REPOSITORY_REGEXP = /github\.com\/([^/]+)\/([^/]+)/; diff --git a/packages/ckeditor5-dev-ci/lib/index.js b/packages/ckeditor5-dev-ci/lib/index.js index ba6e9e0af..a31027300 100644 --- a/packages/ckeditor5-dev-ci/lib/index.js +++ b/packages/ckeditor5-dev-ci/lib/index.js @@ -3,5 +3,5 @@ * For licensing, see LICENSE.md. */ -export { default as getJobApprover } from './utils/get-job-approver'; -export { members } from './data/index'; +export { default as getJobApprover } from './utils/get-job-approver.js'; +export { members } from './data/index.js'; diff --git a/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js b/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js index 4964fd2b6..e50ff62f1 100644 --- a/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js +++ b/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js @@ -4,7 +4,7 @@ */ import { Octokit } from '@octokit/rest'; -import getJobApprover from './utils/get-job-approver'; +import getJobApprover from './utils/get-job-approver.js'; /** * @param options diff --git a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js index 42cec6550..0850440ee 100644 --- a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js @@ -4,7 +4,7 @@ */ import { describe, expect, it, vi } from 'vitest'; -import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds'; +import circleUpdateAutoCancelBuilds from '../lib/circle-update-auto-cancel-builds.js'; describe( 'lib/circleUpdateAutoCancelBuilds', () => { it( 'should send a request to CircleCI to update the redundant workflows option', async () => { diff --git a/packages/ckeditor5-dev-ci/tests/data/members.js b/packages/ckeditor5-dev-ci/tests/data/members.js index 9206b8133..250eba439 100644 --- a/packages/ckeditor5-dev-ci/tests/data/members.js +++ b/packages/ckeditor5-dev-ci/tests/data/members.js @@ -4,7 +4,7 @@ */ import { describe, expect, it } from 'vitest'; -import { members } from '../../lib/data/index'; +import { members } from '../../lib/data/index.js'; describe( 'lib/data/members', () => { it( 'should be an object', () => { diff --git a/packages/ckeditor5-dev-ci/tests/format-message.js b/packages/ckeditor5-dev-ci/tests/format-message.js index ebe77c711..3e6ae167d 100644 --- a/packages/ckeditor5-dev-ci/tests/format-message.js +++ b/packages/ckeditor5-dev-ci/tests/format-message.js @@ -7,7 +7,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest'; -import formatMessage from '../lib/format-message'; +import formatMessage from '../lib/format-message.js'; vi.mock( '../lib/data', () => { return { diff --git a/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js b/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js index f51ec35b5..c5b18e61e 100644 --- a/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js +++ b/packages/ckeditor5-dev-ci/tests/is-job-triggered-by-member.js @@ -4,8 +4,8 @@ */ import { describe, expect, it, vi } from 'vitest'; -import isJobTriggeredByMember from '../lib/is-job-triggered-by-member'; -import getJobApprover from '../lib/utils/get-job-approver'; +import isJobTriggeredByMember from '../lib/is-job-triggered-by-member.js'; +import getJobApprover from '../lib/utils/get-job-approver.js'; const { octokitRequestMock, diff --git a/packages/ckeditor5-dev-ci/tests/process-job-statuses.js b/packages/ckeditor5-dev-ci/tests/process-job-statuses.js index d25a32d97..0c0ade4a0 100644 --- a/packages/ckeditor5-dev-ci/tests/process-job-statuses.js +++ b/packages/ckeditor5-dev-ci/tests/process-job-statuses.js @@ -6,7 +6,7 @@ /* eslint-env node */ import { describe, expect, it } from 'vitest'; -import processJobStatuses from '../lib/process-job-statuses'; +import processJobStatuses from '../lib/process-job-statuses.js'; describe( 'lib/process-job-statuses', () => { describe( 'processJobStatuses()', () => { diff --git a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js index 02d9646c7..4cb85bc3f 100644 --- a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js @@ -4,7 +4,7 @@ */ import { describe, expect, it, vi, beforeEach } from 'vitest'; -import triggerCircleBuild from '../lib/trigger-circle-build'; +import triggerCircleBuild from '../lib/trigger-circle-build.js'; describe( 'lib/triggerCircleBuild', () => { let fetchMock; diff --git a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js index e809d3807..4267e0968 100644 --- a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js @@ -4,7 +4,7 @@ */ import { describe, expect, it, vi } from 'vitest'; -import getJobApprover from '../../lib/utils/get-job-approver'; +import getJobApprover from '../../lib/utils/get-job-approver.js'; describe( 'lib/utils/getJobApprover', () => { it( 'should return a GitHub login name of a user who approved a job in given workflow', async () => { From a2bdbe7eca3b8c4c522e14841b6fc6a1fc135b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 09:44:06 +0200 Subject: [PATCH 031/172] Aligned tests in `utils/versions.js`. --- .../lib/utils/versions.js | 6 +- .../ckeditor5-dev-release-tools/package.json | 4 +- .../tests/utils/versions.js | 225 +++++++++--------- 3 files changed, 117 insertions(+), 118 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js index 07fd3a76f..8437682f5 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js @@ -4,8 +4,8 @@ */ import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import changelogUtils from './changelog'; -import getPackageJson from './getpackagejson'; +import { getChangelog } from './changelog.js'; +import { getPackageJson } from './getpackagejson.js'; /** * Returns a last created version in changelog file. @@ -14,7 +14,7 @@ import getPackageJson from './getpackagejson'; * @returns {String|null} */ export function getLastFromChangelog( cwd = process.cwd() ) { - const changelog = changelogUtils.getChangelog( cwd ); + const changelog = getChangelog( cwd ); if ( !changelog ) { return null; diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 380595be8..768904e97 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -49,7 +49,7 @@ "mock-fs": "^5.1.2" }, "scripts": { - "test": "vitest run --config vitest.config.ts", - "coverage": "vitest run --config vitest.config.ts --coverage" + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.js --coverage" } } diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js index 9bf982b01..69ec1d53a 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js @@ -3,141 +3,127 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { getChangelog } from '../../lib/utils/changelog.js'; +import { getPackageJson } from '../../lib/utils/getpackagejson.js'; + +import { + getLastFromChangelog, + getLastPreRelease, + getLastNightly, + getNextPreRelease, + getNextNightly, + getLastTagFromGit, + getCurrent +} from '../../lib/utils/versions.js'; + +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( '../../lib/utils/changelog.js' ); +vi.mock( '../../lib/utils/getpackagejson.js' ); describe( 'dev-release-tools/utils', () => { - let version, sandbox, changelogStub, getPackageJsonStub; - describe( 'versions', () => { - beforeEach( () => { - sandbox = sinon.createSandbox(); - - changelogStub = sandbox.stub(); - getPackageJsonStub = sandbox.stub(); - - version = proxyquire( '../../lib/utils/versions', { - '@ckeditor/ckeditor5-dev-utils': { - tools - }, - './getpackagejson': getPackageJsonStub, - './changelog': { - getChangelog: changelogStub - } - } ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - describe( 'getLastFromChangelog()', () => { it( 'returns null if the changelog is invalid', () => { - changelogStub.returns( 'Example changelog.' ); + vi.mocked( getChangelog ).mockReturnValue( 'Example changelog.' ); - expect( version.getLastFromChangelog() ).to.equal( null ); + expect( getLastFromChangelog() ).to.equal( null ); } ); it( 'returns version from changelog #1', () => { - changelogStub.returns( '\n## [1.0.0](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0' ); } ); it( 'returns version from changelog #2', () => { - changelogStub.returns( '\n## 1.0.0 (2017-04-05)\nSome changelog entry.' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0 (2017-04-05)\nSome changelog entry.' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0' ); } ); it( 'returns version from changelog #3', () => { - changelogStub.returns( '\n## [1.0.0-alpha](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-alpha](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); } ); it( 'returns version from changelog #4', () => { - changelogStub.returns( '\n## 1.0.0-alpha (2017-04-05)\nSome changelog entry.' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-alpha (2017-04-05)\nSome changelog entry.' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); } ); it( 'returns version from changelog #5', () => { - changelogStub.returns( '\n## [1.0.0-alpha+001](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-alpha+001](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); } ); it( 'returns version from changelog #6', () => { - changelogStub.returns( '\n## 1.0.0-alpha+001 (2017-04-05)\nSome changelog entry.' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-alpha+001 (2017-04-05)\nSome changelog entry.' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); } ); it( 'returns version from changelog #7', () => { - changelogStub.returns( '\n## [1.0.0-beta.2](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-beta.2](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); } ); it( 'returns version from changelog #8', () => { - changelogStub.returns( '\n## 1.0.0-beta.2 (2017-04-05)\nSome changelog entry.' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-beta.2 (2017-04-05)\nSome changelog entry.' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); } ); it( 'returns version from changelog #9', () => { - changelogStub.returns( '\n## 1.0.0\nSome changelog entry.' ); + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0\nSome changelog entry.' ); - expect( version.getLastFromChangelog() ).to.equal( '1.0.0' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0' ); } ); it( 'returns null for empty changelog', () => { - changelogStub.returns( '' ); + vi.mocked( getChangelog ).mockReturnValue( '' ); - expect( version.getLastFromChangelog() ).to.equal( null ); + expect( getLastFromChangelog() ).to.equal( null ); } ); it( 'returns null if changelog does not exist', () => { - changelogStub.returns( null ); + vi.mocked( getChangelog ).mockReturnValue( null ); - expect( version.getLastFromChangelog() ).to.equal( null ); + expect( getLastFromChangelog() ).to.equal( null ); } ); } ); describe( 'getLastPreRelease()', () => { - let shExecStub; - beforeEach( () => { - shExecStub = sandbox.stub( tools, 'shExec' ); - getPackageJsonStub.returns( { name: 'ckeditor5' } ); + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); } ); it( 'asks npm for all versions of a package', () => { - shExecStub.resolves( JSON.stringify( [] ) ); + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [] ) ); - return version.getLastPreRelease( '42.0.0-alpha' ) + return getLastPreRelease( '42.0.0-alpha' ) .then( () => { - expect( shExecStub.callCount ).to.equal( 1 ); - expect( shExecStub.firstCall.args[ 0 ] ).to.equal( 'npm view ckeditor5 versions --json' ); + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( 'npm view ckeditor5 versions --json', expect.anything() ); } ); } ); it( 'returns null if there is no version for a package', () => { - shExecStub.rejects(); + vi.mocked( tools.shExec ).mockRejectedValue(); - return version.getLastPreRelease( '42.0.0-alpha' ) + return getLastPreRelease( '42.0.0-alpha' ) .then( result => { expect( result ).to.equal( null ); } ); } ); it( 'returns null if there is no pre-release version matching the release identifier', () => { - shExecStub.resolves( JSON.stringify( [ + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ '0.0.0-nightly-20230615.0', '37.0.0-alpha.0', '37.0.0-alpha.1', @@ -145,14 +131,14 @@ describe( 'dev-release-tools/utils', () => { '42.0.0' ] ) ); - return version.getLastPreRelease( '42.0.0-alpha' ) + return getLastPreRelease( '42.0.0-alpha' ) .then( result => { expect( result ).to.equal( null ); } ); } ); it( 'returns last pre-release version matching the release identifier', () => { - shExecStub.resolves( JSON.stringify( [ + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ '0.0.0-nightly-20230615.0', '37.0.0-alpha.0', '37.0.0-alpha.1', @@ -160,14 +146,14 @@ describe( 'dev-release-tools/utils', () => { '42.0.0' ] ) ); - return version.getLastPreRelease( '37.0.0-alpha' ) + return getLastPreRelease( '37.0.0-alpha' ) .then( result => { expect( result ).to.equal( '37.0.0-alpha.1' ); } ); } ); it( 'returns last pre-release version matching the release identifier (non-chronological versions order)', () => { - shExecStub.resolves( JSON.stringify( [ + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ '0.0.0-nightly-20230615.0', '37.0.0-alpha.0', '37.0.0-alpha.2', @@ -176,14 +162,14 @@ describe( 'dev-release-tools/utils', () => { '37.0.0-alpha.1' ] ) ); - return version.getLastPreRelease( '37.0.0-alpha' ) + return getLastPreRelease( '37.0.0-alpha' ) .then( result => { expect( result ).to.equal( '37.0.0-alpha.2' ); } ); } ); it( 'returns last pre-release version matching the release identifier (sequence numbers greater than 10)', () => { - shExecStub.resolves( JSON.stringify( [ + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ '0.0.0-nightly-20230615.0', '37.0.0-alpha.1', '37.0.0-alpha.2', @@ -193,14 +179,14 @@ describe( 'dev-release-tools/utils', () => { '37.0.0-alpha.11' ] ) ); - return version.getLastPreRelease( '37.0.0-alpha' ) + return getLastPreRelease( '37.0.0-alpha' ) .then( result => { expect( result ).to.equal( '37.0.0-alpha.11' ); } ); } ); it( 'returns last nightly version', () => { - shExecStub.resolves( JSON.stringify( [ + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ '0.0.0-nightly-20230614.0', '0.0.0-nightly-20230615.0', '0.0.0-nightly-20230615.1', @@ -212,14 +198,14 @@ describe( 'dev-release-tools/utils', () => { '42.0.0' ] ) ); - return version.getLastPreRelease( '0.0.0-nightly' ) + return getLastPreRelease( '0.0.0-nightly' ) .then( result => { expect( result ).to.equal( '0.0.0-nightly-20230616.0' ); } ); } ); it( 'returns last nightly version from a specified day', () => { - shExecStub.resolves( JSON.stringify( [ + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ '0.0.0-nightly-20230614.0', '0.0.0-nightly-20230615.0', '0.0.0-nightly-20230615.1', @@ -231,7 +217,7 @@ describe( 'dev-release-tools/utils', () => { '42.0.0' ] ) ); - return version.getLastPreRelease( '0.0.0-nightly-20230615' ) + return getLastPreRelease( '0.0.0-nightly-20230615' ) .then( result => { expect( result ).to.equal( '0.0.0-nightly-20230615.2' ); } ); @@ -239,54 +225,67 @@ describe( 'dev-release-tools/utils', () => { } ); describe( 'getLastNightly()', () => { - beforeEach( () => { - sandbox.stub( version, 'getLastPreRelease' ).resolves( '0.0.0-nightly-20230615.0' ); + beforeEach( async () => { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); } ); - it( 'asks for a last nightly pre-release version', () => { - return version.getLastNightly() - .then( result => { - expect( version.getLastPreRelease.callCount ).to.equal( 1 ); - expect( version.getLastPreRelease.firstCall.args[ 0 ] ).to.equal( '0.0.0-nightly' ); + it( 'returns last nightly pre-release version', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230613.0', + '0.0.0-nightly-20230614.0', + '0.0.0-nightly-20230614.1', + '0.0.0-nightly-20230614.2', + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); + return getLastNightly() + .then( result => { expect( result ).to.equal( '0.0.0-nightly-20230615.0' ); } ); } ); } ); describe( 'getNextPreRelease()', () => { - it( 'asks for a last pre-release version', () => { - sandbox.stub( version, 'getLastPreRelease' ).resolves( null ); - - return version.getNextPreRelease( '42.0.0-alpha' ) - .then( () => { - expect( version.getLastPreRelease.calledOnce ).to.equal( true ); - expect( version.getLastPreRelease.firstCall.args[ 0 ] ).to.equal( '42.0.0-alpha' ); - } ); + beforeEach( async () => { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); } ); it( 'returns pre-release version with id = 0 if pre-release version was never published for the package yet', () => { - sandbox.stub( version, 'getLastPreRelease' ).resolves( null ); + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); - return version.getNextPreRelease( '42.0.0-alpha' ) + return getNextPreRelease( '42.0.0-alpha' ) .then( result => { expect( result ).to.equal( '42.0.0-alpha.0' ); } ); } ); it( 'returns pre-release version with incremented id if older pre-release version was already published', () => { - sandbox.stub( version, 'getLastPreRelease' ).resolves( '42.0.0-alpha.5' ); + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0-alpha.5' + ] ) ); - return version.getNextPreRelease( '42.0.0-alpha' ) + return getNextPreRelease( '42.0.0-alpha' ) .then( result => { expect( result ).to.equal( '42.0.0-alpha.6' ); } ); } ); it( 'returns nightly version with incremented id if older nightly version was already published', () => { - sandbox.stub( version, 'getLastPreRelease' ).resolves( '0.0.0-nightly-20230615.5' ); + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.5', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); - return version.getNextPreRelease( '0.0.0-nightly' ) + return getNextPreRelease( '0.0.0-nightly' ) .then( result => { expect( result ).to.equal( '0.0.0-nightly-20230615.6' ); } ); @@ -294,26 +293,26 @@ describe( 'dev-release-tools/utils', () => { } ); describe( 'getNextNightly()', () => { - let clock; - beforeEach( () => { - sandbox.stub( version, 'getNextPreRelease' ).resolves( '0.0.0-nightly-20230615.1' ); + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); - clock = sinon.useFakeTimers( { - now: new Date( '2023-06-15 12:00:00' ) - } ); + vi.useFakeTimers(); + vi.setSystemTime( new Date( '2023-06-15 12:00:00' ) ); } ); afterEach( () => { - clock.restore(); + vi.useRealTimers(); } ); it( 'asks for a last nightly pre-release version', () => { - return version.getNextNightly() - .then( result => { - expect( version.getNextPreRelease.calledOnce ).to.equal( true ); - expect( version.getNextPreRelease.firstCall.args[ 0 ] ).to.equal( '0.0.0-nightly-20230615' ); + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); + return getNextNightly() + .then( result => { expect( result ).to.equal( '0.0.0-nightly-20230615.1' ); } ); } ); @@ -321,23 +320,23 @@ describe( 'dev-release-tools/utils', () => { describe( 'getLastTagFromGit()', () => { it( 'returns last tag if exists', () => { - sandbox.stub( tools, 'shExec' ).returns( 'v1.0.0' ); + vi.mocked( tools.shExec ).mockReturnValue( 'v1.0.0' ); - expect( version.getLastTagFromGit() ).to.equal( '1.0.0' ); + expect( getLastTagFromGit() ).to.equal( '1.0.0' ); } ); it( 'returns null if tags do not exist', () => { - sandbox.stub( tools, 'shExec' ).returns( '' ); + vi.mocked( tools.shExec ).mockReturnValue( '' ); - expect( version.getLastTagFromGit() ).to.equal( null ); + expect( getLastTagFromGit() ).to.equal( null ); } ); } ); describe( 'getCurrent()', () => { it( 'returns current version from "package.json"', () => { - getPackageJsonStub.returns( { version: '0.1.2' } ); + vi.mocked( getPackageJson ).mockReturnValue( { version: '0.1.2' } ); - expect( version.getCurrent() ).to.equal( '0.1.2' ); + expect( getCurrent() ).to.equal( '0.1.2' ); } ); } ); } ); From 15cd226602f18ec4b38bc0cdb3829b494855a19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 09:49:00 +0200 Subject: [PATCH 032/172] Aligned tests in `utils/validaterepositorytorelease.js`. --- .../utils/validaterepositorytorelease.js | 62 +++++-------------- 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js b/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js index 5e1a82811..c62f654c9 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js @@ -3,45 +3,17 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, vi } from 'vitest'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +import validateRepositoryToRelease from '../../lib/utils/validaterepositorytorelease.js'; + +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); describe( 'dev-release-tools/utils', () => { describe( 'validateRepositoryToRelease()', () => { - let validateRepositoryToRelease, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - devUtils: { - tools: { - shExec: sandbox.stub() - } - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', stubs.devUtils ); - - validateRepositoryToRelease = require( '../../lib/utils/validaterepositorytorelease' ); - } ); - - afterEach( () => { - sandbox.restore(); - mockery.disable(); - } ); - it( 'resolves an empty array if validation passes (remote branch exists)', async () => { - stubs.devUtils.tools.shExec.resolves( '## master...origin/master' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); @@ -50,7 +22,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an empty array if validation passes (missing remote branch)', async () => { - stubs.devUtils.tools.shExec.resolves( '## master' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## master' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); @@ -59,7 +31,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an array with errors if the release changes are not defined', async () => { - stubs.devUtils.tools.shExec.resolves( '## master...origin/master' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); const errors = await validateRepositoryToRelease( { changes: null, version: '1.0.0' } ); @@ -68,7 +40,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an array with errors if the specified version is not a string', async () => { - stubs.devUtils.tools.shExec.resolves( '## master...origin/master' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: null } ); @@ -77,7 +49,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an array with errors if the specified version is empty string', async () => { - stubs.devUtils.tools.shExec.resolves( '## master...origin/master' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '' } ); @@ -86,7 +58,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an array with errors if current branch is not "master" (remote branch exists)', async () => { - stubs.devUtils.tools.shExec.resolves( '## develop...origin/develop' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); @@ -95,7 +67,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an array with errors if current branch is not "master" (missing remote branch)', async () => { - stubs.devUtils.tools.shExec.resolves( '## develop' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## develop' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); @@ -104,7 +76,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an array with errors if master is behind with origin (remote branch exists)', async () => { - stubs.devUtils.tools.shExec.resolves( '## master...origin/master [behind 2]' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master [behind 2]' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); @@ -113,7 +85,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'resolves an array with errors if master is behind with origin (missing remote branch)', async () => { - stubs.devUtils.tools.shExec.resolves( '## master [behind 2]' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## master [behind 2]' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); @@ -122,7 +94,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'allows skipping the branch check', async () => { - stubs.devUtils.tools.shExec.resolves( '## develop...origin/develop' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0', ignoreBranchCheck: true } ); @@ -130,7 +102,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'uses non-master branch for releasing if specified', async () => { - stubs.devUtils.tools.shExec.resolves( '## release...origin/release' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## release...origin/release' ); const errors = await validateRepositoryToRelease( { branch: 'release', changes: 'Some changes.', version: '1.0.0' } ); @@ -139,7 +111,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'allows skipping the branch check (even if specified)', async () => { - stubs.devUtils.tools.shExec.resolves( '## develop...origin/develop' ); + vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); const errors = await validateRepositoryToRelease( { branch: 'release', From 2c2f982ea3165659a2e2414234cb96d9448882bd Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:52:39 +0200 Subject: [PATCH 033/172] Added file extensions to imports. --- .eslintrc.js | 14 ++++++++++++-- packages/ckeditor5-dev-stale-bot/bin/stale-bot.js | 10 +++++----- .../lib/githubrepository.js | 12 ++++++------ .../lib/utils/isissueorpullrequesttoclose.js | 4 ++-- .../lib/utils/isissueorpullrequesttostale.js | 2 +- .../lib/utils/isissueorpullrequesttounstale.js | 4 ++-- .../lib/utils/ispendingissuetostale.js | 2 +- .../tests/githubrepository.js | 14 +++++++------- .../tests/utils/findstaledate.js | 2 +- .../tests/utils/isissueorpullrequestactive.js | 2 +- .../tests/utils/isissueorpullrequesttoclose.js | 6 +++--- .../tests/utils/isissueorpullrequesttostale.js | 4 ++-- .../tests/utils/isissueorpullrequesttounstale.js | 6 +++--- .../tests/utils/ispendingissuestale.js | 2 +- .../tests/utils/ispendingissuetostale.js | 4 ++-- .../tests/utils/ispendingissuetounlabel.js | 2 +- .../tests/utils/preparesearchquery.js | 2 +- 17 files changed, 51 insertions(+), 41 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 22527e8f2..433f0b828 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,6 +18,7 @@ module.exports = { rules: { 'no-console': 'off', 'ckeditor5-rules/require-file-extensions-in-imports': 'off', + 'mocha/no-global-tests': 'off', // TODO: remove when all mocha tests are removed. 'ckeditor5-rules/license-header': [ 'error', { headerLines: [ '/**', @@ -29,9 +30,18 @@ module.exports = { }, overrides: [ { - files: [ './packages/ckeditor5-dev-build-tools/tests/**/*' ], + files: [ + // TODO: add packages as they are migrated to ESM. + './packages/ckeditor5-dev-stale-bot/**/*' + ], rules: { - 'mocha/no-global-tests': 'off' + 'mocha/no-global-tests': 'error', + 'ckeditor5-rules/require-file-extensions-in-imports': [ + 'error', + { + extensions: [ '.ts', '.js', '.json' ] + } + ] } } ] diff --git a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js index d89413a17..759f420e1 100755 --- a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js +++ b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js @@ -7,11 +7,11 @@ import fs from 'fs-extra'; import chalk from 'chalk'; -import createSpinner from './utils/createspinner'; -import parseArguments from './utils/parsearguments'; -import validateConfig from './utils/validateconfig'; -import parseConfig from './utils/parseconfig'; -import GitHubRepository from '../lib/githubrepository'; +import createSpinner from './utils/createspinner.js'; +import parseArguments from './utils/parsearguments.js'; +import validateConfig from './utils/validateconfig.js'; +import parseConfig from './utils/parseconfig.js'; +import GitHubRepository from '../lib/githubrepository.js'; main().catch( error => { console.error( '\nđŸ”Ĩ Unable to process stale issues and pull requests.\n', error ); diff --git a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js index 1efc8b5cf..189373de4 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js +++ b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js @@ -13,12 +13,12 @@ import { formatDistanceToNow, differenceInSeconds } from 'date-fns'; -import prepareSearchQuery from './utils/preparesearchquery'; -import isIssueOrPullRequestToStale from './utils/isissueorpullrequesttostale'; -import isIssueOrPullRequestToUnstale from './utils/isissueorpullrequesttounstale'; -import isIssueOrPullRequestToClose from './utils/isissueorpullrequesttoclose'; -import isPendingIssueToStale from './utils/ispendingissuetostale'; -import isPendingIssueToUnlabel from './utils/ispendingissuetounlabel'; +import prepareSearchQuery from './utils/preparesearchquery.js'; +import isIssueOrPullRequestToStale from './utils/isissueorpullrequesttostale.js'; +import isIssueOrPullRequestToUnstale from './utils/isissueorpullrequesttounstale.js'; +import isIssueOrPullRequestToClose from './utils/isissueorpullrequesttoclose.js'; +import isPendingIssueToStale from './utils/ispendingissuetostale.js'; +import isPendingIssueToUnlabel from './utils/ispendingissuetounlabel.js'; const GRAPHQL_PATH = upath.join( __dirname, 'graphql' ); diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js index 64c614091..97b5b70a9 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js @@ -4,8 +4,8 @@ */ import { isAfter, parseISO } from 'date-fns'; -import isIssueOrPullRequestActive from './isissueorpullrequestactive'; -import findStaleDate from './findstaledate'; +import isIssueOrPullRequestActive from './isissueorpullrequestactive.js'; +import findStaleDate from './findstaledate.js'; /** * Checks whether the time to close a stale issue or pull request has passed and whether it is still inactive. diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js index 634e4561e..12c6c12cf 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import isIssueOrPullRequestActive from './isissueorpullrequestactive'; +import isIssueOrPullRequestActive from './isissueorpullrequestactive.js'; /** * Checks whether issue or pull request should be staled, because it was not active since the defined moment of time. diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js index 859290402..7d0d04348 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js @@ -3,8 +3,8 @@ * For licensing, see LICENSE.md. */ -import isIssueOrPullRequestActive from './isissueorpullrequestactive'; -import findStaleDate from './findstaledate'; +import isIssueOrPullRequestActive from './isissueorpullrequestactive.js'; +import findStaleDate from './findstaledate.js'; /** * Checks whether issue or pull request should be unstaled, because it was active after it was staled. diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js index 5faa39340..049e28712 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js @@ -4,7 +4,7 @@ */ import { isBefore, parseISO } from 'date-fns'; -import isPendingIssueStale from './ispendingissuestale'; +import isPendingIssueStale from './ispendingissuestale.js'; /** * Checks whether pending issue should be staled, because it was not answered by a community member since the defined moment of time. diff --git a/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js b/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js index a64210ae6..9757e6ed4 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js +++ b/packages/ckeditor5-dev-stale-bot/tests/githubrepository.js @@ -5,13 +5,13 @@ import upath from 'upath'; import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import prepareSearchQuery from '../lib/utils/preparesearchquery'; -import isIssueOrPullRequestToStale from '../lib/utils/isissueorpullrequesttostale'; -import isIssueOrPullRequestToUnstale from '../lib/utils/isissueorpullrequesttounstale'; -import isIssueOrPullRequestToClose from '../lib/utils/isissueorpullrequesttoclose'; -import isPendingIssueToStale from '../lib/utils/ispendingissuetostale'; -import isPendingIssueToUnlabel from '../lib/utils/ispendingissuetounlabel'; -import GitHubRepository from '../lib/githubrepository'; +import prepareSearchQuery from '../lib/utils/preparesearchquery.js'; +import isIssueOrPullRequestToStale from '../lib/utils/isissueorpullrequesttostale.js'; +import isIssueOrPullRequestToUnstale from '../lib/utils/isissueorpullrequesttounstale.js'; +import isIssueOrPullRequestToClose from '../lib/utils/isissueorpullrequesttoclose.js'; +import isPendingIssueToStale from '../lib/utils/ispendingissuetostale.js'; +import isPendingIssueToUnlabel from '../lib/utils/ispendingissuetounlabel.js'; +import GitHubRepository from '../lib/githubrepository.js'; vi.mock( '../lib/utils/preparesearchquery' ); vi.mock( '../lib/utils/isissueorpullrequesttostale' ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js b/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js index 6195a76dd..ec04151b2 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/findstaledate.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; -import findStaleDate from '../../lib/utils/findstaledate'; +import findStaleDate from '../../lib/utils/findstaledate.js'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'findStaleDate', () => { diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js index 9dea900de..716163c4e 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequestactive.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; -import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive.js'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'isIssueOrPullRequestActive', () => { diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js index a61d5dc71..5ef1a0208 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttoclose.js @@ -4,9 +4,9 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import isIssueOrPullRequestToClose from '../../lib/utils/isissueorpullrequesttoclose'; -import findStaleDate from '../../lib/utils/findstaledate'; -import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; +import isIssueOrPullRequestToClose from '../../lib/utils/isissueorpullrequesttoclose.js'; +import findStaleDate from '../../lib/utils/findstaledate.js'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive.js'; vi.mock( '../../lib/utils/findstaledate' ); vi.mock( '../../lib/utils/isissueorpullrequestactive' ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js index dbef32a07..7d5821801 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttostale.js @@ -4,8 +4,8 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; -import isIssueOrPullRequestToStale from '../../lib/utils/isissueorpullrequesttostale'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive.js'; +import isIssueOrPullRequestToStale from '../../lib/utils/isissueorpullrequesttostale.js'; vi.mock( '../../lib/utils/isissueorpullrequestactive' ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js index 7df5df171..03be2cb2b 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/isissueorpullrequesttounstale.js @@ -4,9 +4,9 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import findStaleDate from '../../lib/utils/findstaledate'; -import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive'; -import isIssueOrPullRequestToUnstale from '../../lib/utils/isissueorpullrequesttounstale'; +import findStaleDate from '../../lib/utils/findstaledate.js'; +import isIssueOrPullRequestActive from '../../lib/utils/isissueorpullrequestactive.js'; +import isIssueOrPullRequestToUnstale from '../../lib/utils/isissueorpullrequesttounstale.js'; vi.mock( '../../lib/utils/findstaledate' ); vi.mock( '../../lib/utils/isissueorpullrequestactive' ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js index ef25a8e8f..455121f47 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuestale.js @@ -4,7 +4,7 @@ */ import { describe, it, expect } from 'vitest'; -import isPendingIssueStale from '../../lib/utils/ispendingissuestale'; +import isPendingIssueStale from '../../lib/utils/ispendingissuestale.js'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'isPendingIssueStale', () => { diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js index 0d086a6be..380756baa 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetostale.js @@ -4,8 +4,8 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import isPendingIssueToStale from '../../lib/utils/ispendingissuetostale'; -import isPendingIssueStale from '../../lib/utils/ispendingissuestale'; +import isPendingIssueToStale from '../../lib/utils/ispendingissuetostale.js'; +import isPendingIssueStale from '../../lib/utils/ispendingissuestale.js'; vi.mock( '../../lib/utils/ispendingissuestale' ); diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js index 395dcc7f7..af739f753 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/ispendingissuetounlabel.js @@ -4,7 +4,7 @@ */ import { describe, it, expect } from 'vitest'; -import isPendingIssueToUnlabel from '../../lib/utils/ispendingissuetounlabel'; +import isPendingIssueToUnlabel from '../../lib/utils/ispendingissuetounlabel.js'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'isPendingIssueToUnlabel', () => { diff --git a/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js b/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js index 39672a809..f44b0d0f7 100644 --- a/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js +++ b/packages/ckeditor5-dev-stale-bot/tests/utils/preparesearchquery.js @@ -4,7 +4,7 @@ */ import { describe, it, expect } from 'vitest'; -import prepareSearchQuery from '../../lib/utils/preparesearchquery'; +import prepareSearchQuery from '../../lib/utils/preparesearchquery.js'; describe( 'dev-stale-bot/lib/utils', () => { describe( 'prepareSearchQuery', () => { From ea67c8e28e0de4b87c6ba636e361d1ee31c9b341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 10:04:16 +0200 Subject: [PATCH 034/172] Aligned tests in `utils/transformcommitutils.js`. --- .../lib/utils/transformcommitutils.js | 2 +- .../tests/utils/transformcommitutils.js | 57 +++++-------------- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js index 473ca7e12..b55946c4a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import getPackageJson from './getpackagejson'; +import { getPackageJson } from './getpackagejson.js'; const transformCommitUtils = { /** diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js index 37f3087f4..7be312f96 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js @@ -3,32 +3,15 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, vi } from 'vitest'; +import { getPackageJson } from '../../lib/utils/getpackagejson.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import transformCommitUtils from '../../lib/utils/transformcommitutils.js'; -describe( 'dev-release-tools/utils', () => { - let transformCommitUtils, sandbox, stubs; +vi.mock( '../../lib/utils/getpackagejson.js' ); +describe( 'dev-release-tools/utils', () => { describe( 'transformCommitUtils', () => { - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - getPackageJson: sandbox.stub() - }; - - transformCommitUtils = proxyquire( '../../lib/utils/transformcommitutils', { - './getpackagejson': stubs.getPackageJson - } ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - describe( 'availableTypes', () => { it( 'should be defined', () => { expect( transformCommitUtils.availableCommitTypes ).to.be.a( 'Map' ); @@ -125,7 +108,7 @@ describe( 'dev-release-tools/utils', () => { describe( 'linkToGithubIssue()', () => { it( 'replaces "#ID" with a link to GitHub issue (packageJson.repository as a string)', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package', repository: 'https://github.com/ckeditor/ckeditor5-dev' } ); @@ -155,21 +138,11 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'does not make a link from a comment which contains color hex code with letters and numbers', () => { - stubs.getPackageJson.returns( { - name: 'test-package', - repository: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - expect( transformCommitUtils.linkToGithubIssue( 'Colors: first: `#8da47e`, second: `#f7ce76`.' ) ) .to.equal( 'Colors: first: `#8da47e`, second: `#f7ce76`.' ); } ); it( 'does not make a link from a comment which contains color hex code with letters or numbers only', () => { - stubs.getPackageJson.returns( { - name: 'test-package', - repository: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - expect( transformCommitUtils.linkToGithubIssue( 'Colors: first: `#000000`, second: `#ffffff`.' ) ) .to.equal( 'Colors: first: `#000000`, second: `#ffffff`.' ); } ); @@ -204,7 +177,7 @@ describe( 'dev-release-tools/utils', () => { describe( 'getRepositoryUrl()', () => { it( 'throws an error if package.json does not contain the "repository" property', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package' } ); @@ -213,19 +186,19 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'passes specified `cwd` to `getPackageJson()` util', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package', repository: 'https://github.com/ckeditor/ckeditor5-dev/issues' } ); transformCommitUtils.getRepositoryUrl( 'foo' ); - expect( stubs.getPackageJson.calledOnce ).to.equal( true ); - expect( stubs.getPackageJson.firstCall.args[ 0 ] ).to.equal( 'foo' ); + expect( getPackageJson ).toHaveBeenCalledTimes( 1 ); + expect( getPackageJson ).toHaveBeenCalledWith( 'foo' ); } ); it( 'returns the repository URL (packageJson.repository as a string, contains "/issues")', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package', repository: 'https://github.com/ckeditor/ckeditor5-dev/issues' } ); @@ -234,7 +207,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'returns the repository URL (packageJson.repository as a string, ends with ".git")', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package', repository: 'https://github.com/ckeditor/ckeditor5-dev.git' } ); @@ -243,7 +216,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'returns the repository URL (packageJson.repository as an object)', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package', repository: { url: 'https://github.com/ckeditor/ckeditor5-dev' @@ -254,7 +227,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'returns the repository URL (packageJson.repository as an object, contains "/issues")', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package', repository: { url: 'https://github.com/ckeditor/ckeditor5-dev/issues', @@ -266,7 +239,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'returns the repository URL (packageJson.repository as an object, ends with ".git")', () => { - stubs.getPackageJson.returns( { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'test-package', repository: { url: 'https://github.com/ckeditor/ckeditor5-dev.git', From 0cb884b013bbb50d9dd2a96b156b600613d2eff6 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 9 Sep 2024 10:27:53 +0200 Subject: [PATCH 035/172] Update .eslintrc.js --- .eslintrc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 14d1f4f06..d84357e75 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,7 +37,8 @@ module.exports = { files: [ // TODO: add packages as they are migrated to ESM. './packages/ckeditor5-dev-stale-bot/**/*', - './packages/ckeditor5-dev-ci/**/*' + './packages/ckeditor5-dev-ci/**/*', + './packages/ckeditor5-dev-docs/**/*' ], rules: { 'mocha/no-global-tests': 'error', From 7becfebf56c774bb3fc61357323ac1c8a8a38178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 10:30:21 +0200 Subject: [PATCH 036/172] Aligned tests in `utils/transformcommitfactory.js`. --- .../lib/utils/transformcommitfactory.js | 4 +- .../tests/utils/transformcommitfactory.js | 51 +++++-------------- 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js index 41233f5f9..b4177ccbe 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js @@ -4,8 +4,8 @@ */ import { cloneDeepWith } from 'lodash'; -import utils from './transformcommitutils'; -import getChangedFilesForCommit from './getchangedfilesforcommit'; +import utils from './transformcommitutils.js'; +import { getChangedFilesForCommit } from './getchangedfilesforcommit.js'; // Squash commit follows the pattern: "A pull request title (#{number})". const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js index 2df92a0f3..6dec93802 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js @@ -3,45 +3,20 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { getChangedFilesForCommit } from '../../lib/utils/getchangedfilesforcommit.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +import { transformCommitFactory } from '../../lib/utils/transformcommitfactory.js'; + +vi.mock( '../../lib/utils/getchangedfilesforcommit.js' ); +vi.mock( '../../lib/utils/getpackagejson.js', () => ( { + getPackageJson: vi.fn( () => ( { + repository: 'https://github.com/ckeditor/ckeditor5-dev' + } ) ) +} ) ); describe( 'dev-release-tools/utils', () => { describe( 'transformCommitFactory()', () => { - let transformCommitFactory, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - getPackageJson: () => { - return { - repository: 'https://github.com/ckeditor/ckeditor5-dev' - }; - }, - getChangedFilesForCommit: sandbox.stub() - }; - - mockery.registerMock( './getpackagejson', stubs.getPackageJson ); - mockery.registerMock( './getchangedfilesforcommit', stubs.getChangedFilesForCommit ); - - transformCommitFactory = require( '../../lib/utils/transformcommitfactory' ); - } ); - - afterEach( () => { - sandbox.restore(); - mockery.disable(); - } ); - it( 'returns a function', () => { expect( transformCommitFactory() ).to.be.a( 'function' ); } ); @@ -119,11 +94,11 @@ describe( 'dev-release-tools/utils', () => { 'c/d/z.md' ]; - stubs.getChangedFilesForCommit.returns( files ); + vi.mocked( getChangedFilesForCommit ).mockReturnValue( files ); const commit = transformCommit( rawCommit ); - expect( stubs.getChangedFilesForCommit.calledOnce ).to.equal( true ); + expect( getChangedFilesForCommit ).toHaveBeenCalledTimes( 1 ); expect( commit.files ).to.deep.equal( files ); } ); @@ -1053,7 +1028,7 @@ describe( 'dev-release-tools/utils', () => { it( 'copies an array with changed files across all commits', () => { const files = [ 'a', 'b', 'c' ]; - stubs.getChangedFilesForCommit.returns( files ); + vi.mocked( getChangedFilesForCommit ).mockReturnValue( files ); const rawCommit = { hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', From 1099314dc4e95cc79bd253c4193465930b9f159e Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:57:13 +0200 Subject: [PATCH 037/172] Migrated to ESM. --- .eslintrc.js | 1 + .../bin/dependencychecker.js | 10 ++++------ .../lib/checkdependencies.js | 16 +++++++--------- .../lib/index.js | 6 ++---- .../package.json | 1 + 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index d84357e75..0d2a667a1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { { files: [ // TODO: add packages as they are migrated to ESM. + './packages/ckeditor5-dev-dependency-checker/**/*', './packages/ckeditor5-dev-stale-bot/**/*', './packages/ckeditor5-dev-ci/**/*', './packages/ckeditor5-dev-docs/**/*' diff --git a/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js b/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js index 5ea8a1556..d231072ef 100755 --- a/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js +++ b/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js @@ -5,12 +5,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const minimist = require( 'minimist' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const checkDependencies = require( '../lib/checkdependencies' ); +import path from 'path'; +import minimist from 'minimist'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import checkDependencies from '../lib/checkdependencies.js'; const { packagePaths, options } = parseArguments( process.argv.slice( 2 ) ); diff --git a/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js b/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js index 810a16141..84a136776 100644 --- a/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js +++ b/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js @@ -3,13 +3,11 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const upath = require( 'upath' ); -const { globSync } = require( 'glob' ); -const depCheck = require( 'depcheck' ); -const chalk = require( 'chalk' ); +import fs from 'fs'; +import upath from 'upath'; +import { globSync } from 'glob'; +import depCheck from 'depcheck'; +import chalk from 'chalk'; /** * Checks dependencies sequentially in all provided packages. @@ -19,7 +17,7 @@ const chalk = require( 'chalk' ); * @param {Boolean} [options.quiet=false] Whether to inform about the progress. * @returns {Promise.} Resolves a promise with a flag informing whether detected an error. */ -module.exports = async function checkDependencies( packagePaths, options ) { +export default async function checkDependencies( packagePaths, options ) { let foundError = false; for ( const packagePath of packagePaths ) { @@ -34,7 +32,7 @@ module.exports = async function checkDependencies( packagePaths, options ) { } return Promise.resolve( foundError ); -}; +} /** * Checks dependencies in provided package. If the folder does not contain a package.json file the function quits with success. diff --git a/packages/ckeditor5-dev-dependency-checker/lib/index.js b/packages/ckeditor5-dev-dependency-checker/lib/index.js index 5b097fdda..464670608 100644 --- a/packages/ckeditor5-dev-dependency-checker/lib/index.js +++ b/packages/ckeditor5-dev-dependency-checker/lib/index.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import checkDependencies from './checkdependencies.js'; -const checkDependencies = require( './checkdependencies' ); - -module.exports = { +export default { checkDependencies }; diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index 5baa31d90..fbd0ae7be 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -16,6 +16,7 @@ "node": ">=18.0.0", "npm": ">=5.7.1" }, + "type": "module", "files": [ "bin", "lib" From 274a7647cb529a07963dd3d0e47d10c86be43570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 11:20:56 +0200 Subject: [PATCH 038/172] Aligned tests in `utils/publishpackageonnpmcallback.js`. --- .../utils/publishpackageonnpmcallback.js | 89 ++++++++----------- 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js index 88ed0fcbe..d38e6f42d 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js @@ -3,46 +3,19 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import fs from 'fs-extra'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import publishPackageOnNpmCallback from '../../lib/utils/publishpackageonnpmcallback.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'fs-extra' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); describe( 'dev-release-tools/utils', () => { describe( 'publishPackageOnNpmCallback()', () => { - let publishPackageOnNpmCallback, sandbox, stubs; - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - fs: { - remove: sandbox.stub().resolves() - }, - devUtils: { - tools: { - shExec: sandbox.stub().resolves() - } - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', stubs.devUtils ); - - publishPackageOnNpmCallback = require( '../../lib/utils/publishpackageonnpmcallback' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); + vi.mocked( tools.shExec ).mockResolvedValue(); + vi.mocked( fs.remove ).mockResolvedValue(); } ); it( 'should publish package on npm with provided npm tag', () => { @@ -50,9 +23,13 @@ describe( 'dev-release-tools/utils', () => { return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) .then( () => { - expect( stubs.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stubs.devUtils.tools.shExec.firstCall.args[ 0 ] ).to.equal( 'npm publish --access=public --tag nightly' ); - expect( stubs.devUtils.tools.shExec.firstCall.args[ 1 ] ).to.have.property( 'cwd', packagePath ); + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( + 'npm publish --access=public --tag nightly', + expect.objectContaining( { + cwd: packagePath + } ) + ); } ); } ); @@ -61,8 +38,13 @@ describe( 'dev-release-tools/utils', () => { return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) .then( () => { - expect( stubs.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stubs.devUtils.tools.shExec.firstCall.args[ 1 ] ).to.have.property( 'async', true ); + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining( { + async: true + } ) + ); } ); } ); @@ -71,8 +53,13 @@ describe( 'dev-release-tools/utils', () => { return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) .then( () => { - expect( stubs.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stubs.devUtils.tools.shExec.firstCall.args[ 1 ] ).to.have.property( 'verbosity', 'error' ); + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining( { + verbosity: 'error' + } ) + ); } ); } ); @@ -81,13 +68,13 @@ describe( 'dev-release-tools/utils', () => { return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) .then( () => { - expect( stubs.fs.remove.callCount ).to.equal( 1 ); - expect( stubs.fs.remove.firstCall.args[ 0 ] ).to.equal( packagePath ); + expect( fs.remove ).toHaveBeenCalledTimes( 1 ); + expect( fs.remove ).toHaveBeenCalledWith( packagePath ); } ); } ); it( 'should throw when publishing on npm failed', () => { - stubs.devUtils.tools.shExec.rejects(); + vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; @@ -97,14 +84,14 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Expected to be rejected.' ); }, error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( 'Unable to publish "ckeditor5-foo" package.' ); + expect( error ).toBeInstanceOf( Error ); + expect( error.message ).toEqual( 'Unable to publish "ckeditor5-foo" package.' ); } ); } ); it( 'should not remove a package directory when publishing on npm failed', () => { - stubs.devUtils.tools.shExec.rejects(); + vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; @@ -114,19 +101,19 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.fs.remove.callCount ).to.equal( 0 ); + expect( fs.remove ).not.toHaveBeenCalled(); } ); } ); it( 'should not remove a package directory and not throw error when publishing on npm failed with code 409', async () => { - stubs.devUtils.tools.shExec.rejects( new Error( 'code E409' ) ); + vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'code E409' ) ); const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; await publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ); - expect( stubs.fs.remove.callCount ).to.equal( 0 ); + expect( fs.remove ).not.toHaveBeenCalled(); } ); } ); } ); From 92ff55dda96d4f0aeebc8078382326ab1e2fa7e4 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 9 Sep 2024 11:21:55 +0200 Subject: [PATCH 039/172] Resolved WIP. --- .../tests/utils/assertfilestopublish.js | 224 +++++++++--------- 1 file changed, 115 insertions(+), 109 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js b/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js index 1039fd13e..1bef66874 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/assertfilestopublish.js @@ -126,7 +126,7 @@ describe( 'assertFilesToPublish()', () => { return Promise.resolve( [ 'src/index.ts' ] ); } - return Promise.resolve( [ ] ); + return Promise.resolve( [] ); } ); const optionalEntries = { @@ -193,7 +193,7 @@ describe( 'assertFilesToPublish()', () => { ); } ); - it( 'should consider entry as required if there are not matches in optional entries', () => { + it( 'should consider entry as required if there are not matches in optional entries', async () => { vi.mocked( fs ).readJson.mockResolvedValue( { name: 'ckeditor5-foo', files: [ @@ -217,15 +217,20 @@ describe( 'assertFilesToPublish()', () => { ] }; - return assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 2 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); - } ); + await assertFilesToPublish( [ 'ckeditor5-foo' ], optionalEntries ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'src', 'src/**' ], + expect.any( Object ) + ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'README.md', 'README.md/**' ], + expect.any( Object ) + ); } ); - it( 'should not throw if `main` file exists', () => { + it( 'should not throw if `main` file exists', async () => { vi.mocked( fs ).readJson.mockResolvedValue( { name: 'ckeditor5-foo', main: 'src/index.ts', @@ -235,21 +240,35 @@ describe( 'assertFilesToPublish()', () => { ] } ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'src/index.ts', 'src/index.ts/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 3 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src/index.ts', 'src/index.ts/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.thirdCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); - } ); + vi.mocked( glob ).mockImplementation( input => { + if ( input[ 0 ] === 'src' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } + if ( input[ 0 ] === 'src/index.ts' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } + + return Promise.resolve( [ 'README.md' ] ); + } ); + + await assertFilesToPublish( [ 'ckeditor5-foo' ] ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'src', 'src/**' ], + expect.any( Object ) + ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'src/index.ts', 'src/index.ts/**' ], + expect.any( Object ) + ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'README.md', 'README.md/**' ], + expect.any( Object ) + ); } ); - it( 'should not throw if `types` file exists', () => { + it( 'should not throw if `types` file exists', async () => { vi.mocked( fs ).readJson.mockResolvedValue( { name: 'ckeditor5-foo', types: 'src/index.d.ts', @@ -259,21 +278,35 @@ describe( 'assertFilesToPublish()', () => { ] } ); - stubs.glob.glob - .withArgs( [ 'src', 'src/**' ] ).resolves( [ 'src/index.ts' ] ) - .withArgs( [ 'src/index.d.ts', 'src/index.d.ts/**' ] ).resolves( [ 'src/index.d.ts' ] ) - .withArgs( [ 'README.md', 'README.md/**' ] ).resolves( [ 'README.md' ] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( () => { - expect( stubs.glob.glob.callCount ).to.equal( 3 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.deep.equal( [ 'src/index.d.ts', 'src/index.d.ts/**' ] ); - expect( stubs.glob.glob.secondCall.args[ 0 ] ).to.deep.equal( [ 'src', 'src/**' ] ); - expect( stubs.glob.glob.thirdCall.args[ 0 ] ).to.deep.equal( [ 'README.md', 'README.md/**' ] ); - } ); + vi.mocked( glob ).mockImplementation( input => { + if ( input[ 0 ] === 'src' ) { + return Promise.resolve( [ 'src/index.ts' ] ); + } + if ( input[ 0 ] === 'src/index.d.ts' ) { + return Promise.resolve( [ 'src/index.d.ts' ] ); + } + + return Promise.resolve( [ 'README.md' ] ); + } ); + + await assertFilesToPublish( [ 'ckeditor5-foo' ] ); + + expect( vi.mocked( glob ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'src', 'src/**' ], + expect.any( Object ) + ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'src/index.d.ts', 'src/index.d.ts/**' ], + expect.any( Object ) + ); + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( + [ 'README.md', 'README.md/**' ], + expect.any( Object ) + ); } ); - it( 'should throw if not all files from `files` field exist', () => { + it( 'should throw if not all files from `files` field exist', async () => { vi.mocked( fs ).readJson.mockResolvedValue( { name: 'ckeditor5-foo', files: [ @@ -283,101 +316,74 @@ describe( 'assertFilesToPublish()', () => { ] } ); - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src", "LICENSE.md", "README.md"' - ); - } ); + vi.mocked( glob ).mockResolvedValue( [] ); + + await expect( assertFilesToPublish( [ 'ckeditor5-foo' ] ) ) + .rejects.toThrow( 'Missing files in "ckeditor5-foo" package for entries: "src", "LICENSE.md", "README.md"' ); } ); - it( 'should throw if file from `main` field does not exist', () => { + it( 'should throw if file from `main` field does not exist', async () => { vi.mocked( fs ).readJson.mockResolvedValue( { name: 'ckeditor5-foo', main: 'src/index.ts' } ); - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src/index.ts"' - ); - } ); + vi.mocked( glob ).mockResolvedValue( [] ); + + await expect( assertFilesToPublish( [ 'ckeditor5-foo' ] ) ) + .rejects.toThrow( 'Missing files in "ckeditor5-foo" package for entries: "src/index.ts"' ); } ); - it( 'should throw if file from `types` field does not exist', () => { + it( 'should throw if file from `types` field does not exist', async () => { vi.mocked( fs ).readJson.mockResolvedValue( { name: 'ckeditor5-foo', types: 'src/index.d.ts' } ); - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src/index.d.ts"' - ); - } ); + vi.mocked( glob ).mockResolvedValue( [] ); + + await expect( assertFilesToPublish( [ 'ckeditor5-foo' ] ) ) + .rejects.toThrow( 'Missing files in "ckeditor5-foo" package for entries: "src/index.d.ts"' ); } ); - it( 'should throw one error for all packages with missing files', () => { - stubs.fs.readJson - .withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - files: [ - 'src' - ] - } ) - .withArgs( 'ckeditor5-bar/package.json' ).resolves( { - name: 'ckeditor5-bar', + it( 'should throw one error for all packages with missing files', async () => { + vi.mocked( fs ).readJson.mockImplementation( input => { + if ( input === 'ckeditor5-foo/package.json' ) { + return Promise.resolve( { + name: 'ckeditor5-foo', + files: [ + 'src' + ] + } ); + } + + if ( input === 'ckeditor5-bar/package.json' ) { + return Promise.resolve( { + name: 'ckeditor5-bar', + files: [ + 'src', + 'README.md' + ] + } ); + } + + return Promise.resolve( { + name: 'ckeditor5-baz', files: [ 'src', + 'LICENSE.md', 'README.md' ] - } ) - .withArgs( 'ckeditor5-baz/package.json' ).resolves( { - name: 'ckeditor5-baz', - files: [ - 'src', - 'LICENSE.md', - 'README.md' - ] + } ); } ); - stubs.glob.glob.resolves( [] ); - - return assertFilesToPublish( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ] ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'Missing files in "ckeditor5-foo" package for entries: "src"\n' + - 'Missing files in "ckeditor5-bar" package for entries: "src", "README.md"\n' + - 'Missing files in "ckeditor5-baz" package for entries: "src", "LICENSE.md", "README.md"' - ); - } ); + vi.mocked( glob ).mockResolvedValue( [] ); + + const errorMessage = 'Missing files in "ckeditor5-foo" package for entries: "src"\n' + + 'Missing files in "ckeditor5-bar" package for entries: "src", "README.md"\n' + + 'Missing files in "ckeditor5-baz" package for entries: "src", "LICENSE.md", "README.md"'; + + await expect( assertFilesToPublish( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ] ) ) + .rejects.toThrow( errorMessage ); } ); } ); From e36df8d26ab04be849ab13b4f25ac1534a70057c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 11:23:54 +0200 Subject: [PATCH 040/172] Aligned tests in `utils/parseroptions.js`. --- .../tests/utils/parseroptions.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js b/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js index f6844b789..6557daa96 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js @@ -3,17 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; +import { describe, it, expect } from 'vitest'; +import parserOptions from '../../lib/utils/parseroptions.js'; describe( 'dev-release-tools/utils', () => { - let parserOptions; - - beforeEach( () => { - parserOptions = require( '../../lib/utils/parseroptions' ); - } ); - describe( 'parser-options', () => { it( 'should not hoist closed tickets', () => { expect( parserOptions.referenceActions ).to.deep.equal( [] ); From 1b3f6ee2cb2f374c5241e8933069ffa750d3d18a Mon Sep 17 00:00:00 2001 From: Marcin Panek Date: Mon, 9 Sep 2024 11:37:51 +0200 Subject: [PATCH 041/172] Converted `ckeditor5-dev-bump-year` to ESM. --- .eslintrc.js | 1 + packages/ckeditor5-dev-bump-year/lib/bumpyear.js | 10 +++++----- packages/ckeditor5-dev-bump-year/lib/index.js | 4 ++-- packages/ckeditor5-dev-bump-year/package.json | 1 + 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index d84357e75..f966c1807 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { { files: [ // TODO: add packages as they are migrated to ESM. + './packages/ckeditor5-dev-bump-year/**/*', './packages/ckeditor5-dev-stale-bot/**/*', './packages/ckeditor5-dev-ci/**/*', './packages/ckeditor5-dev-docs/**/*' diff --git a/packages/ckeditor5-dev-bump-year/lib/bumpyear.js b/packages/ckeditor5-dev-bump-year/lib/bumpyear.js index f10dfe25f..a37f28e93 100644 --- a/packages/ckeditor5-dev-bump-year/lib/bumpyear.js +++ b/packages/ckeditor5-dev-bump-year/lib/bumpyear.js @@ -5,9 +5,9 @@ 'use strict'; -const chalk = require( 'chalk' ); -const fs = require( 'fs' ); -const { globSync } = require( 'glob' ); +import chalk from 'chalk'; +import fs from 'fs'; +import { globSync } from 'glob'; /** * Updates year in all licenses in the provided directory, based on provided glob patterns. @@ -24,7 +24,7 @@ const { globSync } = require( 'glob' ); * and optionally `options` property for this `glob` pattern. * @param {String} [params.initialYear='2003'] Year from which the licenses should begin. */ -module.exports = function bumpYear( params ) { +export default function bumpYear( params ) { if ( !params.initialYear ) { params.initialYear = '2003'; } @@ -92,7 +92,7 @@ module.exports = function bumpYear( params ) { console.log( file ); } } -}; +} /** * License headers are only required in JS and TS files. diff --git a/packages/ckeditor5-dev-bump-year/lib/index.js b/packages/ckeditor5-dev-bump-year/lib/index.js index 4e20ce9e0..77ac60e0b 100644 --- a/packages/ckeditor5-dev-bump-year/lib/index.js +++ b/packages/ckeditor5-dev-bump-year/lib/index.js @@ -5,8 +5,8 @@ 'use strict'; -const bumpYear = require( './bumpyear' ); +import bumpYear from './bumpyear.js'; -module.exports = { +export default { bumpYear }; diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index 0784b1a2e..f795a5dd3 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -17,6 +17,7 @@ "npm": ">=5.7.1" }, "main": "lib/index.js", + "type": "module", "files": [ "lib" ], From e25f3d672a6755047d85aa67d604780f78a6bfab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 11:43:02 +0200 Subject: [PATCH 042/172] Aligned tests in `utils/isversionpublishablefortag.js`. --- .../tests/utils/isversionpublishablefortag.js | 86 +++++++------------ 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js b/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js index 37e025cc0..e66e35fba 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js @@ -3,91 +3,67 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import semver from 'semver'; +import shellEscape from 'shell-escape'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +import isVersionPublishableForTag from '../../lib/utils/isversionpublishablefortag.js'; -describe( 'dev-release-tools/isVersionPublishableForTag', () => { - let stub, isVersionPublishableForTag; +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'semver' ); +vi.mock( 'shell-escape' ); +describe( 'dev-release-tools/isVersionPublishableForTag', () => { beforeEach( () => { - stub = { - semver: { - lte: sinon.stub() - }, - devUtils: { - tools: { - shExec: sinon.stub() - } - }, - shellEscape: sinon.stub().callsFake( v => v[ 0 ] ) - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'semver', stub.semver ); - mockery.registerMock( 'shell-escape', stub.shellEscape ); - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', stub.devUtils ); - - isVersionPublishableForTag = require( '../../lib/utils/isversionpublishablefortag' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); + vi.mocked( shellEscape ).mockImplementation( v => v[ 0 ] ); } ); it( 'should return true if given version is available', async () => { - stub.semver.lte.returns( false ); - stub.devUtils.tools.shExec.resolves( '1.0.0\n' ); + vi.mocked( semver.lte ).mockReturnValue( false ); + vi.mocked( tools.shExec ).mockResolvedValue( '1.0.0\n' ); const result = await isVersionPublishableForTag( 'package-name', '1.0.1', 'latest' ); expect( result ).to.equal( true ); - expect( stub.semver.lte.callCount ).to.equal( 1 ); - expect( stub.semver.lte.firstCall.args ).to.deep.equal( [ '1.0.1', '1.0.0' ] ); - expect( stub.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stub.devUtils.tools.shExec.firstCall.firstArg ).to.equal( 'npm view package-name@latest version --silent' ); + expect( semver.lte ).toHaveBeenCalledTimes( 1 ); + expect( semver.lte ).toHaveBeenCalledWith( '1.0.1', '1.0.0' ); + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( 'npm view package-name@latest version --silent', expect.anything() ); } ); it( 'should return false if given version is not available', async () => { - stub.semver.lte.returns( true ); - stub.devUtils.tools.shExec.resolves( '1.0.0\n' ); + vi.mocked( semver.lte ).mockReturnValue( true ); + vi.mocked( tools.shExec ).mockResolvedValue( '1.0.0\n' ); const result = await isVersionPublishableForTag( 'package-name', '1.0.0', 'latest' ); expect( result ).to.equal( false ); - expect( stub.semver.lte.callCount ).to.equal( 1 ); - expect( stub.semver.lte.firstCall.args ).to.deep.equal( [ '1.0.0', '1.0.0' ] ); - expect( stub.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stub.devUtils.tools.shExec.firstCall.firstArg ).to.equal( 'npm view package-name@latest version --silent' ); + expect( semver.lte ).toHaveBeenCalledTimes( 1 ); + expect( semver.lte ).toHaveBeenCalledWith( '1.0.0', '1.0.0' ); + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( 'npm view package-name@latest version --silent', expect.anything() ); } ); it( 'should return true if given npm tag is not published yet', async () => { - stub.devUtils.tools.shExec.rejects( 'E404' ); + vi.mocked( tools.shExec ).mockRejectedValue( 'E404' ); const result = await isVersionPublishableForTag( 'package-name', '1.0.0', 'alpha' ); expect( result ).to.equal( true ); - expect( stub.semver.lte.callCount ).to.equal( 0 ); - expect( stub.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stub.devUtils.tools.shExec.firstCall.firstArg ).to.equal( 'npm view package-name@alpha version --silent' ); + expect( semver.lte ).not.toHaveBeenCalled(); + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( 'npm view package-name@alpha version --silent', expect.anything() ); } ); it( 'should escape arguments passed to a shell command', async () => { - stub.semver.lte.returns( false ); - stub.devUtils.tools.shExec.resolves( '1.0.0\n' ); + vi.mocked( semver.lte ).mockReturnValue( false ); + vi.mocked( tools.shExec ).mockResolvedValue( '1.0.0\n' ); await isVersionPublishableForTag( 'package-name', '1.0.0', 'alpha' ); - expect( stub.shellEscape.callCount ).to.equal( 2 ); - expect( stub.shellEscape.firstCall.firstArg ).to.deep.equal( [ 'package-name' ] ); - expect( stub.shellEscape.secondCall.firstArg ).to.deep.equal( [ 'alpha' ] ); + expect( shellEscape ).toHaveBeenCalledTimes( 2 ); + expect( shellEscape ).toHaveBeenNthCalledWith( 1, [ 'package-name' ] ); + expect( shellEscape ).toHaveBeenNthCalledWith( 2, [ 'alpha' ] ); } ); } ); From 011fbb1c2f1bcc886a98d6ff650811d644109c6c Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:46:58 +0200 Subject: [PATCH 043/172] Migrated to ESM. --- .eslintrc.js | 1 + .../lib/constants.js | 6 +++-- .../ckeditor5-dev-web-crawler/lib/index.js | 8 +++---- .../lib/runcrawler.js | 22 +++++++++---------- .../ckeditor5-dev-web-crawler/lib/spinner.js | 6 ++--- .../ckeditor5-dev-web-crawler/lib/utils.js | 2 +- .../ckeditor5-dev-web-crawler/package.json | 1 + 7 files changed, 25 insertions(+), 21 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index d84357e75..3ddbcf41f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -38,6 +38,7 @@ module.exports = { // TODO: add packages as they are migrated to ESM. './packages/ckeditor5-dev-stale-bot/**/*', './packages/ckeditor5-dev-ci/**/*', + './packages/ckeditor5-dev-web-crawler/**/*', './packages/ckeditor5-dev-docs/**/*' ], rules: { diff --git a/packages/ckeditor5-dev-web-crawler/lib/constants.js b/packages/ckeditor5-dev-web-crawler/lib/constants.js index 73e387bf3..9d1f3ac95 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/constants.js +++ b/packages/ckeditor5-dev-web-crawler/lib/constants.js @@ -7,7 +7,9 @@ /* eslint-env node */ -const DEFAULT_CONCURRENCY = require( 'os' ).cpus().length / 2; +import { cpus } from 'os'; + +const DEFAULT_CONCURRENCY = cpus().length / 2; const DEFAULT_TIMEOUT = 15 * 1000; @@ -58,7 +60,7 @@ const META_TAG_NAME = 'x-cke-crawler-ignore-patterns'; const DATA_ATTRIBUTE_NAME = 'data-cke-crawler-skip'; -module.exports = { +export default { DEFAULT_CONCURRENCY, DEFAULT_TIMEOUT, DEFAULT_RESPONSIVENESS_CHECK_TIMEOUT, diff --git a/packages/ckeditor5-dev-web-crawler/lib/index.js b/packages/ckeditor5-dev-web-crawler/lib/index.js index e4880d3eb..4a5e28e0e 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/index.js +++ b/packages/ckeditor5-dev-web-crawler/lib/index.js @@ -7,11 +7,11 @@ /* eslint-env node */ -const runCrawler = require( './runcrawler' ); -const { getBaseUrl, isUrlValid, toArray } = require( './utils' ); -const { DEFAULT_CONCURRENCY } = require( './constants' ); +import runCrawler from './runcrawler.js'; +import { getBaseUrl, isUrlValid, toArray } from './utils.js'; +import { DEFAULT_CONCURRENCY } from './constants.js'; -module.exports = { +export default { DEFAULT_CONCURRENCY, runCrawler, getBaseUrl, diff --git a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js index 2c3e35f99..a51b0c8d0 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js +++ b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js @@ -7,14 +7,14 @@ /* eslint-env node */ -const puppeteer = require( 'puppeteer' ); -const chalk = require( 'chalk' ); -const util = require( 'util' ); -const stripAnsiEscapeCodes = require( 'strip-ansi' ); -const { getBaseUrl, toArray } = require( './utils' ); -const { createSpinner, getProgressHandler } = require( './spinner' ); - -const { +import puppeteer from 'puppeteer'; +import chalk from 'chalk'; +import util from 'util'; +import stripAnsiEscapeCodes from 'strip-ansi'; +import { getBaseUrl, toArray } from './utils.js'; +import { createSpinner, getProgressHandler } from './spinner.js'; + +import { DEFAULT_TIMEOUT, DEFAULT_RESPONSIVENESS_CHECK_TIMEOUT, DEFAULT_REMAINING_ATTEMPTS, @@ -23,7 +23,7 @@ const { IGNORE_ALL_ERRORS_WILDCARD, META_TAG_NAME, DATA_ATTRIBUTE_NAME -} = require( './constants' ); +} from './constants.js'; /** * Main crawler function. Its purpose is to: @@ -42,7 +42,7 @@ const { * @param {Boolean} [options.ignoreHTTPSErrors=false] Whether the browser should ignore invalid (self-signed) certificates. * @returns {Promise} Promise is resolved, when the crawler has finished the whole crawling procedure. */ -module.exports = async function runCrawler( options ) { +export default async function runCrawler( options ) { const { url, depth = Infinity, @@ -101,7 +101,7 @@ module.exports = async function runCrawler( options ) { // Always exit the script because `spinner` can freeze the process of the crawler if it is executed in the `noSpinner:true` mode. process.exit( errors.size ? 1 : 0 ); -}; +} /** * Creates a new browser instance and closes the default blank page. diff --git a/packages/ckeditor5-dev-web-crawler/lib/spinner.js b/packages/ckeditor5-dev-web-crawler/lib/spinner.js index 6475b466b..0ca74bd0b 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/spinner.js +++ b/packages/ckeditor5-dev-web-crawler/lib/spinner.js @@ -7,8 +7,8 @@ /* eslint-env node */ -const chalk = require( 'chalk' ); -const ora = require( 'ora' ); +import chalk from 'chalk'; +import ora from 'ora'; /** * Creates nice-looking CLI spinner. @@ -51,7 +51,7 @@ function getProgressHandler( spinner, { verbose } ) { }; } -module.exports = { +export default { createSpinner, getProgressHandler }; diff --git a/packages/ckeditor5-dev-web-crawler/lib/utils.js b/packages/ckeditor5-dev-web-crawler/lib/utils.js index 75473a46c..8b78f1720 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/utils.js +++ b/packages/ckeditor5-dev-web-crawler/lib/utils.js @@ -44,7 +44,7 @@ function toArray( data ) { return Array.isArray( data ) ? data : [ data ]; } -module.exports = { +export default { getBaseUrl, isUrlValid, toArray diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index 12803d775..a6f395c8b 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -17,6 +17,7 @@ "npm": ">=5.7.1" }, "main": "lib/index.js", + "type": "module", "files": [ "lib" ], From ec3f84c7bb66f6d49b126f6b61bf61b6adc01340 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 9 Sep 2024 11:51:40 +0200 Subject: [PATCH 044/172] The transformCommitUtils module now exports things instead of a default object. --- .../ckeditor5-dev-release-tools/lib/index.js | 38 +-- .../lib/utils/transformcommitutils.js | 283 +++++++++--------- .../tests/utils/transformcommitutils.js | 2 +- 3 files changed, 160 insertions(+), 163 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/index.js b/packages/ckeditor5-dev-release-tools/lib/index.js index f8e7f6909..1a14aea99 100644 --- a/packages/ckeditor5-dev-release-tools/lib/index.js +++ b/packages/ckeditor5-dev-release-tools/lib/index.js @@ -3,17 +3,17 @@ * For licensing, see LICENSE.md. */ -export { default as generateChangelogForSinglePackage } from './tasks/generatechangelogforsinglepackage'; -export { default as generateChangelogForMonoRepository } from './tasks/generatechangelogformonorepository'; -export { default as updateDependencies } from './tasks/updatedependencies'; -export { default as commitAndTag } from './tasks/commitandtag'; -export { default as createGithubRelease } from './tasks/creategithubrelease'; -export { default as reassignNpmTags } from './tasks/reassignnpmtags'; -export { default as prepareRepository } from './tasks/preparerepository'; -export { default as push } from './tasks/push'; -export { default as publishPackages } from './tasks/publishpackages'; -export { default as updateVersions } from './tasks/updateversions'; -export { default as cleanUpPackages } from './tasks/cleanuppackages'; +export { default as generateChangelogForSinglePackage } from './tasks/generatechangelogforsinglepackage.js'; +export { default as generateChangelogForMonoRepository } from './tasks/generatechangelogformonorepository.js'; +export { default as updateDependencies } from './tasks/updatedependencies.js'; +export { default as commitAndTag } from './tasks/commitandtag.js'; +export { default as createGithubRelease } from './tasks/creategithubrelease.js'; +export { default as reassignNpmTags } from './tasks/reassignnpmtags.js'; +export { default as prepareRepository } from './tasks/preparerepository.js'; +export { default as push } from './tasks/push.js'; +export { default as publishPackages } from './tasks/publishpackages.js'; +export { default as updateVersions } from './tasks/updateversions.js'; +export { default as cleanUpPackages } from './tasks/cleanuppackages.js'; export { getLastFromChangelog, getLastPreRelease, @@ -22,11 +22,11 @@ export { getNextNightly, getCurrent, getLastTagFromGit -} from './utils/versions'; -export { getChangesForVersion, getChangelog, saveChangelog } from './utils/changelog'; -export { default as executeInParallel } from './utils/executeinparallel'; -export { default as validateRepositoryToRelease } from './utils/validaterepositorytorelease'; -export { default as checkVersionAvailability } from './utils/checkversionavailability'; -export { default as verifyPackagesPublishedCorrectly } from './tasks/verifypackagespublishedcorrectly'; -export { default as getNpmTagFromVersion } from './utils/getnpmtagfromversion'; -export { default as isVersionPublishableForTag } from './utils/isversionpublishablefortag'; +} from './utils/versions.js'; +export { getChangesForVersion, getChangelog, saveChangelog } from './utils/changelog.js'; +export { default as executeInParallel } from './utils/executeinparallel.js'; +export { default as validateRepositoryToRelease } from './utils/validaterepositorytorelease.js'; +export { default as checkVersionAvailability } from './utils/checkversionavailability.js'; +export { default as verifyPackagesPublishedCorrectly } from './tasks/verifypackagespublishedcorrectly.js'; +export { default as getNpmTagFromVersion } from './utils/getnpmtagfromversion.js'; +export { default as isVersionPublishableForTag } from './utils/isversionpublishablefortag.js'; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js index b55946c4a..ee0ef2860 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js @@ -5,157 +5,154 @@ import { getPackageJson } from './getpackagejson.js'; -const transformCommitUtils = { - /** - * A regexp for extracting additional changelog entries from the single commit. - * Prefixes of the commit must be synchronized the `getCommitType()` util. - */ - MULTI_ENTRIES_COMMIT_REGEXP: /(?:Feature|Other|Fix|Docs|Internal|Tests|Revert|Release)(?: \([\w\-, ]+?\))?:/g, - - /** - * Map of available types of the commits. - * Types marked as `false` will be ignored during generating the changelog. - */ - availableCommitTypes: new Map( [ - [ 'Fix', true ], - [ 'Feature', true ], - [ 'Other', true ], - - [ 'Docs', false ], - [ 'Internal', false ], - [ 'Tests', false ], - [ 'Revert', false ], - [ 'Release', false ] - ] ), - - /** - * Order of messages generated in changelog. - */ - typesOrder: { - 'Features': 1, - 'Bug fixes': 2, - 'Other changes': 3, - - 'MAJOR BREAKING CHANGES': 1, - 'MINOR BREAKING CHANGES': 2, - 'BREAKING CHANGES': 3 - }, - - /** - * Returns an order of a message in the changelog. - * - * @param {String} title - * @returns {Number} - */ - getTypeOrder( title ) { - for ( const typeTitle of Object.keys( transformCommitUtils.typesOrder ) ) { - if ( title.startsWith( typeTitle ) ) { - return transformCommitUtils.typesOrder[ typeTitle ]; - } +/** + * A regexp for extracting additional changelog entries from the single commit. + * Prefixes of the commit must be synchronized the `getCommitType()` util. + */ +export const MULTI_ENTRIES_COMMIT_REGEXP = /(?:Feature|Other|Fix|Docs|Internal|Tests|Revert|Release)(?: \([\w\-, ]+?\))?:/g; + +/** + * Map of available types of the commits. + * Types marked as `false` will be ignored during generating the changelog. + */ +export const availableCommitTypes = new Map( [ + [ 'Fix', true ], + [ 'Feature', true ], + [ 'Other', true ], + + [ 'Docs', false ], + [ 'Internal', false ], + [ 'Tests', false ], + [ 'Revert', false ], + [ 'Release', false ] +] ); + +/** + * Order of messages generated in changelog. + */ +export const typesOrder = { + 'Features': 1, + 'Bug fixes': 2, + 'Other changes': 3, + + 'MAJOR BREAKING CHANGES': 1, + 'MINOR BREAKING CHANGES': 2, + 'BREAKING CHANGES': 3 +}; + +/** + * Returns an order of a message in the changelog. + * + * @param {String} title + * @returns {Number} + */ +export function getTypeOrder( title ) { + for ( const typeTitle of Object.keys( typesOrder ) ) { + if ( title.startsWith( typeTitle ) ) { + return typesOrder[ typeTitle ]; } + } + + return 10; +} + +/** + * Replaces reference to the user (`@name`) with a link to the user's profile. + * + * @param {String} comment + * @returns {String} + */ +export function linkToGithubUser( comment ) { + return comment.replace( /(^|[\s(])@([\w-]+)(?![/\w-])/ig, ( matchedText, charBefore, nickName ) => { + return `${ charBefore }[@${ nickName }](https://github.com/${ nickName })`; + } ); +} - return 10; - }, - - /** - * Replaces reference to the user (`@name`) with a link to the user's profile. - * - * @param {String} comment - * @returns {String} - */ - linkToGithubUser( comment ) { - return comment.replace( /(^|[\s(])@([\w-]+)(?![/\w-])/ig, ( matchedText, charBefore, nickName ) => { - return `${ charBefore }[@${ nickName }](https://github.com/${ nickName })`; - } ); - }, - - /** - * Replaces reference to issue (#ID) with a link to the issue. - * If comment matches to "organization/repository#ID", link will lead to the specified repository. - * - * @param {String} comment - * @returns {String} - */ - linkToGithubIssue( comment ) { - return comment.replace( /(\/?[\w-]+\/[\w-]+)?#([\d]+)(?=$|[\s,.)\]])/igm, ( matchedText, maybeRepository, issueId ) => { - if ( maybeRepository ) { - if ( maybeRepository.startsWith( '/' ) ) { - return matchedText; - } - - return `[${ maybeRepository }#${ issueId }](https://github.com/${ maybeRepository }/issues/${ issueId })`; +/** + * Replaces reference to issue (#ID) with a link to the issue. + * If comment matches to "organization/repository#ID", link will lead to the specified repository. + * + * @param {String} comment + * @returns {String} + */ +export function linkToGithubIssue( comment ) { + return comment.replace( /(\/?[\w-]+\/[\w-]+)?#([\d]+)(?=$|[\s,.)\]])/igm, ( matchedText, maybeRepository, issueId ) => { + if ( maybeRepository ) { + if ( maybeRepository.startsWith( '/' ) ) { + return matchedText; } - const repositoryUrl = transformCommitUtils.getRepositoryUrl(); - - // But if doesn't, let's add it. - return `[#${ issueId }](${ repositoryUrl }/issues/${ issueId })`; - } ); - }, - - /** - * Changes a singular type of commit to plural which will be displayed in a changelog. - * - * The switch cases must be synchronized with the `MULTI_ENTRIES_COMMIT_REGEXP` regexp. - * - * @param {String} commitType - * @returns {String} - */ - getCommitType( commitType ) { - switch ( commitType ) { - case 'Feature': - return 'Features'; - - case 'Fix': - return 'Bug fixes'; - - case 'Other': - return 'Other changes'; - - default: - throw new Error( `Given invalid type of commit ("${ commitType }").` ); - } - }, - - /** - * @param {String} sentence - * @param {Number} length - * @returns {String} - */ - truncate( sentence, length ) { - if ( sentence.length <= length ) { - return sentence; + return `[${ maybeRepository }#${ issueId }](https://github.com/${ maybeRepository }/issues/${ issueId })`; } - return sentence.slice( 0, length - 3 ).trim() + '...'; - }, - - /** - * Returns a URL to the repository whether the commit is being parsed. - * - * @param {String} [cwd=process.cwd()] - * @returns {String} - */ - getRepositoryUrl( cwd = process.cwd() ) { - const packageJson = getPackageJson( cwd ); - - // Due to merging our issue trackers, `packageJson.bugs` will point to the same place for every package. - // We cannot rely on this value anymore. See: https://github.com/ckeditor/ckeditor5/issues/1988. - // Instead of we can take a value from `packageJson.repository` and adjust it to match to our requirements. - let repositoryUrl = ( typeof packageJson.repository === 'object' ) ? packageJson.repository.url : packageJson.repository; - - if ( !repositoryUrl ) { - throw new Error( `The package.json for "${ packageJson.name }" must contain the "repository" property.` ); - } + const repositoryUrl = getRepositoryUrl(); + + // But if doesn't, let's add it. + return `[#${ issueId }](${ repositoryUrl }/issues/${ issueId })`; + } ); +} + +/** + * Changes a singular type of commit to plural which will be displayed in a changelog. + * + * The switch cases must be synchronized with the `MULTI_ENTRIES_COMMIT_REGEXP` regexp. + * + * @param {String} commitType + * @returns {String} + */ +export function getCommitType( commitType ) { + switch ( commitType ) { + case 'Feature': + return 'Features'; - // If the value ends with ".git", we need to remove it. - repositoryUrl = repositoryUrl.replace( /\.git$/, '' ); + case 'Fix': + return 'Bug fixes'; - // Remove "/issues" suffix as well. - repositoryUrl = repositoryUrl.replace( /\/issues/, '' ); + case 'Other': + return 'Other changes'; - return repositoryUrl; + default: + throw new Error( `Given invalid type of commit ("${ commitType }").` ); } -}; +} + +/** + * @param {String} sentence + * @param {Number} length + * @returns {String} + */ +export function truncate( sentence, length ) { + if ( sentence.length <= length ) { + return sentence; + } + + return sentence.slice( 0, length - 3 ).trim() + '...'; +} + +/** + * Returns a URL to the repository whether the commit is being parsed. + * + * @param {String} [cwd=process.cwd()] + * @returns {String} + */ +export function getRepositoryUrl( cwd = process.cwd() ) { + const packageJson = getPackageJson( cwd ); + + // Due to merging our issue trackers, `packageJson.bugs` will point to the same place for every package. + // We cannot rely on this value anymore. See: https://github.com/ckeditor/ckeditor5/issues/1988. + // Instead of we can take a value from `packageJson.repository` and adjust it to match to our requirements. + let repositoryUrl = ( typeof packageJson.repository === 'object' ) ? packageJson.repository.url : packageJson.repository; + + if ( !repositoryUrl ) { + throw new Error( `The package.json for "${ packageJson.name }" must contain the "repository" property.` ); + } + + // If the value ends with ".git", we need to remove it. + repositoryUrl = repositoryUrl.replace( /\.git$/, '' ); + + // Remove "/issues" suffix as well. + repositoryUrl = repositoryUrl.replace( /\/issues/, '' ); + + return repositoryUrl; +} -export default transformCommitUtils; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js index 7be312f96..8382d1560 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js @@ -6,7 +6,7 @@ import { describe, it, expect, vi } from 'vitest'; import { getPackageJson } from '../../lib/utils/getpackagejson.js'; -import transformCommitUtils from '../../lib/utils/transformcommitutils.js'; +import * as transformCommitUtils from '../../lib/utils/transformcommitutils.js'; vi.mock( '../../lib/utils/getpackagejson.js' ); From 2370ade2baccd10c39b85dc537b90a332804d9cb Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 9 Sep 2024 11:52:31 +0200 Subject: [PATCH 045/172] More tests. --- .../tests/utils/assertnpmauthorization.js | 109 +++----- .../tests/utils/assertnpmtag.js | 257 +++++++----------- .../tests/utils/assertpackages.js | 164 +++++------ 3 files changed, 193 insertions(+), 337 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmauthorization.js b/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmauthorization.js index 5d5bb931e..fe75a5ac8 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmauthorization.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmauthorization.js @@ -3,92 +3,45 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); -describe( 'dev-release-tools/utils', () => { - describe( 'assertNpmAuthorization()', () => { - let assertNpmAuthorization, sandbox, stubs; +describe( 'assertNpmAuthorization()', () => { + it( 'should not throw if user is logged to npm as the provided account name', async () => { + vi.mocked( tools ).shExec.mockResolvedValue( 'pepe' ); - beforeEach( () => { - sandbox = sinon.createSandbox(); + await assertNpmAuthorization( 'pepe' ); - stubs = { - devUtils: { - tools: { - shExec: sandbox.stub().resolves() - } - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', stubs.devUtils ); - - assertNpmAuthorization = require( '../../lib/utils/assertnpmauthorization' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); - } ); - - it( 'should not throw if user is logged to npm as the provided account name', () => { - stubs.devUtils.tools.shExec.resolves( 'pepe' ); - - return assertNpmAuthorization( 'pepe' ) - .then( () => { - expect( stubs.devUtils.tools.shExec.callCount ).to.equal( 1 ); - expect( stubs.devUtils.tools.shExec.firstCall.args[ 0 ] ).to.equal( 'npm whoami' ); - expect( stubs.devUtils.tools.shExec.firstCall.args[ 1 ] ).to.have.property( 'verbosity', 'error' ); - expect( stubs.devUtils.tools.shExec.firstCall.args[ 1 ] ).to.have.property( 'async', true ); - } ); - } ); + expect( vi.mocked( tools ).shExec ).toHaveBeenCalledExactlyOnceWith( + 'npm whoami', + expect.objectContaining( { + verbosity: 'error', + async: true + } ) + ); + } ); - it( 'should trim whitespace characters from the command output before checking the name', () => { - stubs.devUtils.tools.shExec.resolves( '\t pepe \n' ); + it( 'should trim whitespace characters from the command output before checking the name', async () => { + vi.mocked( tools ).shExec.mockResolvedValue( '\t pepe \n' ); - return assertNpmAuthorization( 'pepe' ); - } ); + await assertNpmAuthorization( 'pepe' ); + } ); - it( 'should throw if user is not logged to npm', () => { - stubs.devUtils.tools.shExec.rejects(); + it( 'should throw if user is not logged to npm', async () => { + vi.mocked( tools ).shExec.mockRejectedValue( new Error( 'not logged' ) ); - return assertNpmAuthorization( 'pepe' ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'You must be logged to npm as "pepe" to execute this release step.' - ); - } ); - } ); + await expect( assertNpmAuthorization( 'pepe' ) ) + .rejects.toThrow( 'You must be logged to npm as "pepe" to execute this release step.' ); + } ); - it( 'should throw if user is logged to npm on different account name', () => { - stubs.devUtils.tools.shExec.resolves( 'john' ); + it( 'should throw if user is logged to npm on different account name', async () => { + vi.mocked( tools ).shExec.mockResolvedValue( 'john' ); - return assertNpmAuthorization( 'pepe' ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'You must be logged to npm as "pepe" to execute this release step.' - ); - } ); - } ); + await expect( assertNpmAuthorization( 'pepe' ) ) + .rejects.toThrow( 'You must be logged to npm as "pepe" to execute this release step.' ); } ); -} ); +} ) +; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmtag.js b/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmtag.js index 90c3cacb0..716ca19e1 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmtag.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/assertnpmtag.js @@ -3,205 +3,142 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import assertNpmTag from '../../lib/utils/assertnpmtag.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'fs-extra' ); -describe( 'dev-release-tools/utils', () => { - describe( 'assertNpmTag()', () => { - let assertNpmTag, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - fs: { - readJson: sandbox.stub() - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'fs-extra', stubs.fs ); - - assertNpmTag = require( '../../lib/utils/assertnpmtag' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); - } ); - - it( 'should resolve the promise if list of packages is empty', () => { - return assertNpmTag( [] ); - } ); +describe( 'assertNpmTag()', () => { + it( 'should resolve the promise if list of packages is empty', async () => { + await assertNpmTag( [] ); + } ); - it( 'should read `package.json` from each package', () => { - stubs.fs.readJson - .withArgs( 'ckeditor5-foo/package.json' ).resolves( { + it( 'should read `package.json` from each package', async () => { + vi.mocked( fs ).readJson.mockImplementation( input => { + if ( input === 'ckeditor5-foo/package.json' ) { + return Promise.resolve( { name: 'ckeditor5-foo', version: '1.0.0' - } ) - .withArgs( 'ckeditor5-bar/package.json' ).resolves( { - name: 'ckeditor5-bar', - version: '0.0.1' } ); + } - return assertNpmTag( [ 'ckeditor5-foo', 'ckeditor5-bar' ], 'latest' ) - .then( () => { - expect( stubs.fs.readJson.callCount ).to.equal( 2 ); - expect( stubs.fs.readJson.firstCall.args[ 0 ] ).to.equal( 'ckeditor5-foo/package.json' ); - expect( stubs.fs.readJson.secondCall.args[ 0 ] ).to.equal( 'ckeditor5-bar/package.json' ); - } ); + return Promise.resolve( { + name: 'ckeditor5-bar', + version: '0.0.1' + } ); } ); - it( 'should not throw if version tag matches npm tag (both "latest")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '1.0.0' - } ); + await assertNpmTag( [ 'ckeditor5-foo', 'ckeditor5-bar' ], 'latest' ); + + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( 'ckeditor5-foo/package.json' ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( 'ckeditor5-bar/package.json' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'latest' ); + it( 'should not throw if version tag matches npm tag (both "latest")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '1.0.0' } ); - it( 'should not throw if version tag matches npm tag (version tag = "latest", npm tag = "staging")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '1.0.0' - } ); + await assertNpmTag( [ 'ckeditor5-foo' ], 'latest' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'staging' ); + it( 'should not throw if version tag matches npm tag (version tag = "latest", npm tag = "staging")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '1.0.0' } ); - it( 'should not throw if version tag matches npm tag (both "alpha")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '1.0.0-alpha.0' - } ); + await assertNpmTag( [ 'ckeditor5-foo' ], 'staging' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'alpha' ); + it( 'should not throw if version tag matches npm tag (both "alpha")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '1.0.0-alpha.0' } ); - it( 'should not throw if version tag matches npm tag (both "nightly")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '0.0.0-nightly-20230517.0' - } ); + await assertNpmTag( [ 'ckeditor5-foo' ], 'alpha' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'nightly' ); + it( 'should not throw if version tag matches npm tag (both "nightly")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '0.0.0-nightly-20230517.0' } ); - it( 'should throw if version tag does not match npm tag (version tag = "latest", npm tag = "alpha")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '1.0.0' - } ); + await assertNpmTag( [ 'ckeditor5-foo' ], 'nightly' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'alpha' ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The version tag "latest" from "ckeditor5-foo" package does not match the npm tag "alpha".' - ); - } ); + it( 'should throw if version tag does not match npm tag (version tag = "latest", npm tag = "alpha")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '1.0.0' } ); - it( 'should throw if version tag does not match npm tag (version tag = "latest", npm tag = "nightly")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '1.0.0' - } ); + await expect( assertNpmTag( [ 'ckeditor5-foo' ], 'alpha' ) ) + .rejects.toThrow( 'The version tag "latest" from "ckeditor5-foo" package does not match the npm tag "alpha".' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'nightly' ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The version tag "latest" from "ckeditor5-foo" package does not match the npm tag "nightly".' - ); - } ); + it( 'should throw if version tag does not match npm tag (version tag = "latest", npm tag = "nightly")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '1.0.0' } ); - it( 'should throw if version tag does not match npm tag (version tag = "alpha", npm tag = "staging")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '1.0.0-alpha.0' - } ); + await expect( assertNpmTag( [ 'ckeditor5-foo' ], 'nightly' ) ) + .rejects.toThrow( 'The version tag "latest" from "ckeditor5-foo" package does not match the npm tag "nightly".' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'staging' ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The version tag "alpha" from "ckeditor5-foo" package does not match the npm tag "staging".' - ); - } ); + it( 'should throw if version tag does not match npm tag (version tag = "alpha", npm tag = "staging")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '1.0.0-alpha.0' } ); - it( 'should throw if version tag does not match npm tag (version tag = "nightly", npm tag = "staging")', () => { - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: 'ckeditor5-foo', - version: '0.0.0-nightly-20230517.0' - } ); + await expect( assertNpmTag( [ 'ckeditor5-foo' ], 'staging' ) ) + .rejects.toThrow( 'The version tag "alpha" from "ckeditor5-foo" package does not match the npm tag "staging".' ); + } ); - return assertNpmTag( [ 'ckeditor5-foo' ], 'staging' ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The version tag "nightly" from "ckeditor5-foo" package does not match the npm tag "staging".' - ); - } ); + it( 'should throw if version tag does not match npm tag (version tag = "nightly", npm tag = "staging")', async () => { + vi.mocked( fs ).readJson.mockResolvedValue( { + name: 'ckeditor5-foo', + version: '0.0.0-nightly-20230517.0' } ); - it( 'should throw one error for all packages with incorrect tags', () => { - stubs.fs.readJson - .withArgs( 'ckeditor5-foo/package.json' ).resolves( { + await expect( assertNpmTag( [ 'ckeditor5-foo' ], 'staging' ) ) + .rejects.toThrow( 'The version tag "nightly" from "ckeditor5-foo" package does not match the npm tag "staging".' ); + } ); + + it( 'should throw one error for all packages with incorrect tags', async () => { + vi.mocked( fs ).readJson.mockImplementation( input => { + if ( input === 'ckeditor5-foo/package.json' ) { + return Promise.resolve( { name: 'ckeditor5-foo', version: '1.0.0-alpha' - } ) - .withArgs( 'ckeditor5-bar/package.json' ).resolves( { + } ); + } + + if ( input === 'ckeditor5-bar/package.json' ) { + return Promise.resolve( { name: 'ckeditor5-bar', version: '0.0.0-nightly-20230517.0' - } ) - .withArgs( 'ckeditor5-baz/package.json' ).resolves( { - name: 'ckeditor5-baz', - version: '0.0.1-rc.5' } ); + } - return assertNpmTag( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], 'latest' ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The version tag "alpha" from "ckeditor5-foo" package does not match the npm tag "latest".\n' + - 'The version tag "nightly" from "ckeditor5-bar" package does not match the npm tag "latest".\n' + - 'The version tag "rc" from "ckeditor5-baz" package does not match the npm tag "latest".' - ); - } ); + return Promise.resolve( { + name: 'ckeditor5-baz', + version: '0.0.1-rc.5' + } ); } ); + + const errorMessage = 'The version tag "alpha" from "ckeditor5-foo" package does not match the npm tag "latest".\n' + + 'The version tag "nightly" from "ckeditor5-bar" package does not match the npm tag "latest".\n' + + 'The version tag "rc" from "ckeditor5-baz" package does not match the npm tag "latest".'; + + await expect( assertNpmTag( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], 'latest' ) ) + .rejects.toThrow( errorMessage ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/assertpackages.js b/packages/ckeditor5-dev-release-tools/tests/utils/assertpackages.js index f23cebbb0..7d80a4d37 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/assertpackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/assertpackages.js @@ -3,126 +3,92 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import assertPackages from '../../lib/utils/assertpackages.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +vi.mock( 'fs-extra' ); -describe( 'dev-release-tools/utils', () => { - describe( 'assertPackages()', () => { - let assertPackages, sandbox, stubs; +describe( 'assertPackages()', () => { + const disableMainValidatorOptions = { + requireEntryPoint: false, + optionalEntryPointPackages: [] + }; - const disableMainValidatorOptions = { - requireEntryPoint: false, - optionalEntryPointPackages: [] - }; + it( 'should resolve the promise if list of packages is empty', async () => { + await assertPackages( [], { ...disableMainValidatorOptions } ); + } ); - beforeEach( () => { - sandbox = sinon.createSandbox(); + it( 'should check if `package.json` exists in each package', async () => { + vi.mocked( fs ).pathExists.mockResolvedValue( true ); - stubs = { - fs: { - pathExists: sandbox.stub(), - readJson: sandbox.stub() - } - }; + await assertPackages( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], { ...disableMainValidatorOptions } ); - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); + expect( vi.mocked( fs ).pathExists ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs ).pathExists ).toHaveBeenCalledWith( 'ckeditor5-foo/package.json' ); + expect( vi.mocked( fs ).pathExists ).toHaveBeenCalledWith( 'ckeditor5-bar/package.json' ); + expect( vi.mocked( fs ).pathExists ).toHaveBeenCalledWith( 'ckeditor5-baz/package.json' ); + } ); - mockery.registerMock( 'fs-extra', stubs.fs ); + it( 'should throw one error for all packages with missing `package.json` file', async () => { + vi.mocked( fs ).pathExists.mockImplementation( input => { + if ( input === 'ckeditor5-bar/package.json' ) { + return Promise.resolve( true ); + } - assertPackages = require( '../../lib/utils/assertpackages' ); + return Promise.resolve( false ); } ); - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); - } ); - - it( 'should resolve the promise if list of packages is empty', () => { - return assertPackages( [], { ...disableMainValidatorOptions } ); - } ); + await expect( assertPackages( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], { ...disableMainValidatorOptions } ) ) + .rejects.toThrow( + 'The "package.json" file is missing in the "ckeditor5-foo" package.\n' + + 'The "package.json" file is missing in the "ckeditor5-baz" package.' + ); + } ); - it( 'should check if `package.json` exists in each package', () => { - stubs.fs.pathExists.resolves( true ); + // See: https://github.com/ckeditor/ckeditor5/issues/15127. + describe( 'the entry package point validator', () => { + const enableMainValidatorOptions = { + requireEntryPoint: true, + optionalEntryPointPackages: [ + '@ckeditor/ckeditor5-bar' + ] + }; - return assertPackages( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], { ...disableMainValidatorOptions } ) - .then( () => { - expect( stubs.fs.pathExists.callCount ).to.equal( 3 ); - expect( stubs.fs.pathExists.firstCall.args[ 0 ] ).to.equal( 'ckeditor5-foo/package.json' ); - expect( stubs.fs.pathExists.secondCall.args[ 0 ] ).to.equal( 'ckeditor5-bar/package.json' ); - expect( stubs.fs.pathExists.thirdCall.args[ 0 ] ).to.equal( 'ckeditor5-baz/package.json' ); - } ); - } ); + it( 'should throw if a package misses its entry point', async () => { + vi.mocked( fs ).pathExists.mockResolvedValue( true ); + vi.mocked( fs ).readJson.mockImplementation( input => { + if ( input === 'ckeditor5-foo/package.json' ) { + return Promise.resolve( { + name: '@ckeditor/ckeditor5-foo', + main: 'src/index.ts' + } ); + } - it( 'should throw one error for all packages with missing `package.json` file', () => { - stubs.fs.pathExists - .resolves( false ) - .withArgs( 'ckeditor5-bar/package.json' ).resolves( true ); - - return assertPackages( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], { ...disableMainValidatorOptions } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The "package.json" file is missing in the "ckeditor5-foo" package.\n' + - 'The "package.json" file is missing in the "ckeditor5-baz" package.' - ); + if ( input === 'ckeditor5-bar/package.json' ) { + return Promise.resolve( { + name: '@ckeditor/ckeditor5-bar' } ); - } ); + } - // See: https://github.com/ckeditor/ckeditor5/issues/15127. - describe( 'the entry package point validator', () => { - const enableMainValidatorOptions = { - requireEntryPoint: true, - optionalEntryPointPackages: [ - '@ckeditor/ckeditor5-bar' - ] - }; - - it( 'should throw if a package misses its entry point', () => { - stubs.fs.pathExists.resolves( true ); - stubs.fs.readJson.withArgs( 'ckeditor5-foo/package.json' ).resolves( { - name: '@ckeditor/ckeditor5-foo', - main: 'src/index.ts' - } ); - stubs.fs.readJson.withArgs( 'ckeditor5-bar/package.json' ).resolves( { - name: '@ckeditor/ckeditor5-bar' - } ); - stubs.fs.readJson.withArgs( 'ckeditor5-baz/package.json' ).resolves( { + return Promise.resolve( { name: '@ckeditor/ckeditor5-baz' } ); - - return assertPackages( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], { ...enableMainValidatorOptions } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).to.be.an( 'Error' ); - expect( error.message ).to.equal( - 'The "@ckeditor/ckeditor5-baz" package misses the entry point ("main") definition in its "package.json".' - ); - } ); } ); - it( 'should pass the validator if specified package does not have to define the entry point', () => { - stubs.fs.pathExists.resolves( true ); - stubs.fs.readJson.withArgs( 'ckeditor5-bar/package.json' ).resolves( { - name: '@ckeditor/ckeditor5-bar' - } ); + await expect( assertPackages( [ 'ckeditor5-foo', 'ckeditor5-bar', 'ckeditor5-baz' ], { ...enableMainValidatorOptions } ) ) + .rejects.toThrow( + 'The "@ckeditor/ckeditor5-baz" package misses the entry point ("main") definition in its "package.json".' + ); + } ); - return assertPackages( [ 'ckeditor5-bar' ], { ...enableMainValidatorOptions } ); + it( 'should pass the validator if specified package does not have to define the entry point', async () => { + vi.mocked( fs ).pathExists.mockResolvedValue( true ); + vi.mocked( fs ).readJson.mockResolvedValue( { + name: '@ckeditor/ckeditor5-bar' } ); + + await assertPackages( [ 'ckeditor5-bar' ], { ...enableMainValidatorOptions } ); } ); } ); } ); From ddbcf91f2d5a44f08740c557745fe88a860b129a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 11:52:52 +0200 Subject: [PATCH 046/172] Aligned tests in `utils/getwriteroptions.js`. --- .../lib/utils/getwriteroptions.js | 5 +++-- .../tests/utils/getwriteroptions.js | 21 ++++--------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js index ca6a31638..e2c02ba9e 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js @@ -5,8 +5,9 @@ import fs from 'fs'; import path from 'path'; +import utils from './transformcommitutils.js'; + const templatePath = path.join( __dirname, '..', 'templates' ); -import { getTypeOrder } from './transformcommitutils'; /** * @param {Function|Object} transform @@ -27,5 +28,5 @@ export function getWriterOptions( transform ) { } function sortFunction( a, b ) { - return getTypeOrder( a.title ) - getTypeOrder( b.title ); + return utils.getTypeOrder( a.title ) - utils.getTypeOrder( b.title ); } diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js index 2dc30cb87..a943aeac8 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js @@ -3,26 +3,13 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); +import { describe, it, expect, vi } from 'vitest'; +import { getWriterOptions } from '../../lib/utils/getwriteroptions.js'; describe( 'dev-release-tools/utils', () => { - let getWriterOptions, sandbox, transformSpy; - - beforeEach( () => { - transformSpy = sinon.spy(); - sandbox = sinon.createSandbox(); - - getWriterOptions = require( '../../lib/utils/getwriteroptions' ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - describe( 'getWriterOptions()', () => { + const transformSpy = vi.fn(); + it( 'returns an object with writer options', () => { const writerOptions = getWriterOptions( transformSpy ); From f5fbeeaee84c6ac1965fea380ec8babe96b20720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 12:16:39 +0200 Subject: [PATCH 047/172] Aligned tests in `utils/getpackagespaths.js`. --- .../lib/utils/getpackagespaths.js | 4 +- .../tests/utils/getpackagespaths.js | 245 +++++++++--------- 2 files changed, 119 insertions(+), 130 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js index 224b468c3..1348ac913 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js @@ -6,14 +6,14 @@ import path from 'path'; import minimatch from 'minimatch'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import getPackageJson from './getpackagejson'; +import { getPackageJson } from './getpackagejson.js'; /** * Returns an object with two collections of paths to packages which are located in single repository. * Those packages must be defined as dependencies in the repository found in `options.cwd`. * * - The first one is marked as `matched` and means that packages specified in a path (which is a combination of values specified as - * `options.cwd` and `options.packages`) match to given criteria. + * `options.cwd` and `options.packages`) match to given criteria. * - The second one is marked as `skipped` and means that packages should not be processed. They were listed as packages to skip * (`options.skipPackages` or don't mach to `options.scope`). * diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js index 51ff6ff5c..13313f55e 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js @@ -3,46 +3,33 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import { describe, it, expect, vi } from 'vitest'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { getPackageJson } from '../../lib/utils/getpackagejson.js'; +import { getPackagesPaths } from '../../lib/utils/getpackagespaths.js'; + +vi.mock( 'path', () => ( { + default: { + join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ) + } +} ) ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( '../../lib/utils/getpackagejson.js' ); describe( 'dev-release-tools/utils', () => { describe( 'getPackagesPaths()', () => { - let getPackagesPaths, sandbox, getPackageJsonStub, getDirectoriesStub; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - getPackageJsonStub = sandbox.stub(); - getDirectoriesStub = sandbox.stub(); - - sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); - - getPackagesPaths = proxyquire( '../../lib/utils/getpackagespaths', { - './getpackagejson': getPackageJsonStub, - '@ckeditor/ckeditor5-dev-utils': { - tools: { - getDirectories: getDirectoriesStub - } - } - } ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - it( 'returns all found packages', () => { - getDirectoriesStub.returns( [ + vi.mocked( tools.getDirectories ).mockReturnValue( [ 'ckeditor5-core', 'ckeditor5-engine', 'ckeditor5-utils' ] ); + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); + const options = { cwd: '/tmp', packages: 'packages', @@ -50,30 +37,31 @@ describe( 'dev-release-tools/utils', () => { skipMainRepository: true }; - getPackageJsonStub.onCall( 0 ).returns( { name: '@ckeditor/ckeditor5-core' } ); - getPackageJsonStub.onCall( 1 ).returns( { name: '@ckeditor/ckeditor5-engine' } ); - getPackageJsonStub.onCall( 2 ).returns( { name: '@ckeditor/ckeditor5-utils' } ); - const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 3 ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-core' ) ).to.equal( true ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-engine' ) ).to.equal( true ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-utils' ) ).to.equal( true ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 3 ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 1 ); - expect( pathsCollection.skipped.has( '/tmp' ) ).to.equal( true ); + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 1 ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); } ); it( 'allows ignoring specified packages (specified as array)', () => { - getDirectoriesStub.returns( [ + vi.mocked( tools.getDirectories ).mockReturnValue( [ 'ckeditor5-core', 'ckeditor5-engine', 'ckeditor5-utils' ] ); + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); + const options = { cwd: '/tmp', packages: 'packages', @@ -83,51 +71,49 @@ describe( 'dev-release-tools/utils', () => { skipMainRepository: true }; - getPackageJsonStub.onCall( 0 ).returns( { name: '@ckeditor/ckeditor5-core' } ); - getPackageJsonStub.onCall( 1 ).returns( { name: '@ckeditor/ckeditor5-engine' } ); - getPackageJsonStub.onCall( 2 ).returns( { name: '@ckeditor/ckeditor5-utils' } ); - const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 2 ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 2 ); - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 2 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).to.equal( true ); + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 2 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); } ); it( 'allows ignoring specified packages (specified as string)', () => { - getDirectoriesStub.returns( [ + vi.mocked( tools.getDirectories ).mockReturnValue( [ 'ckeditor5-core', 'ckeditor5-engine', 'ckeditor5-utils' ] ); + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); + const options = { cwd: '/tmp', packages: 'packages', skipPackages: '@ckeditor/ckeditor5-u*', skipMainRepository: true }; - getPackageJsonStub.onCall( 0 ).returns( { name: '@ckeditor/ckeditor5-core' } ); - getPackageJsonStub.onCall( 1 ).returns( { name: '@ckeditor/ckeditor5-engine' } ); - getPackageJsonStub.onCall( 2 ).returns( { name: '@ckeditor/ckeditor5-utils' } ); const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 2 ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 2 ); - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 2 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).to.equal( true ); + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 2 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); } ); it( 'allows restricting the scope for packages', () => { - getDirectoriesStub.returns( [ + vi.mocked( tools.getDirectories ).mockReturnValue( [ 'ckeditor5-core', 'ckeditor5-engine', 'ckeditor5-utils', @@ -135,6 +121,13 @@ describe( 'dev-release-tools/utils', () => { 'ckeditor5-build-inline' ] ); + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); + const options = { cwd: '/tmp', packages: 'packages', @@ -143,29 +136,23 @@ describe( 'dev-release-tools/utils', () => { skipMainRepository: true }; - getPackageJsonStub.onCall( 0 ).returns( { name: '@ckeditor/ckeditor5-core' } ); - getPackageJsonStub.onCall( 1 ).returns( { name: '@ckeditor/ckeditor5-engine' } ); - getPackageJsonStub.onCall( 2 ).returns( { name: '@ckeditor/ckeditor5-utils' } ); - getPackageJsonStub.onCall( 3 ).returns( { name: '@ckeditor/ckeditor5-build-classic' } ); - getPackageJsonStub.onCall( 4 ).returns( { name: '@ckeditor/ckeditor5-build-inline' } ); - const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 2 ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).to.equal( true ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-inline' ) ).to.equal( true ); - - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 4 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).to.equal( true ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 2 ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 4 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); } ); it( 'allows restricting the scope for packages and works fine with "skipPackages" option', () => { - getDirectoriesStub.returns( [ + vi.mocked( tools.getDirectories ).mockReturnValue( [ 'ckeditor5-core', 'ckeditor5-engine', 'ckeditor5-utils', @@ -173,6 +160,13 @@ describe( 'dev-release-tools/utils', () => { 'ckeditor5-build-inline' ] ); + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); + const options = { cwd: '/tmp', packages: 'packages', @@ -183,29 +177,23 @@ describe( 'dev-release-tools/utils', () => { skipMainRepository: true }; - getPackageJsonStub.onCall( 0 ).returns( { name: '@ckeditor/ckeditor5-core' } ); - getPackageJsonStub.onCall( 1 ).returns( { name: '@ckeditor/ckeditor5-engine' } ); - getPackageJsonStub.onCall( 2 ).returns( { name: '@ckeditor/ckeditor5-utils' } ); - getPackageJsonStub.onCall( 3 ).returns( { name: '@ckeditor/ckeditor5-build-classic' } ); - getPackageJsonStub.onCall( 4 ).returns( { name: '@ckeditor/ckeditor5-build-inline' } ); - const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 1 ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).to.equal( true ); - - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 5 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).to.equal( true ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 1 ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 5 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); } ); it( 'allows returning the main repository', () => { - getDirectoriesStub.returns( [ + vi.mocked( tools.getDirectories ).mockReturnValue( [ 'ckeditor5-core', 'ckeditor5-engine', 'ckeditor5-utils', @@ -213,6 +201,13 @@ describe( 'dev-release-tools/utils', () => { 'ckeditor5-build-inline' ] ); + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); + const options = { cwd: '/tmp', packages: 'packages', @@ -222,25 +217,19 @@ describe( 'dev-release-tools/utils', () => { skipMainRepository: false }; - getPackageJsonStub.onCall( 0 ).returns( { name: '@ckeditor/ckeditor5-core' } ); - getPackageJsonStub.onCall( 1 ).returns( { name: '@ckeditor/ckeditor5-engine' } ); - getPackageJsonStub.onCall( 2 ).returns( { name: '@ckeditor/ckeditor5-utils' } ); - getPackageJsonStub.onCall( 3 ).returns( { name: '@ckeditor/ckeditor5-build-classic' } ); - getPackageJsonStub.onCall( 4 ).returns( { name: '@ckeditor/ckeditor5-build-inline' } ); - const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 1 ); - expect( pathsCollection.matched.has( '/tmp' ) ).to.equal( true ); - - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 5 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).to.equal( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-classic' ) ).to.equal( true ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 1 ); + expect( pathsCollection.matched.has( '/tmp' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 5 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); } ); it( 'allows returning the main repository only (skipMainRepository=false)', () => { @@ -251,12 +240,12 @@ describe( 'dev-release-tools/utils', () => { const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 1 ); - expect( pathsCollection.matched.has( '/tmp' ) ).to.equal( true ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 1 ); + expect( pathsCollection.matched.has( '/tmp' ) ).toEqual( true ); - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 0 ); + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 0 ); } ); it( 'allows returning the main repository only (skipMainRepository=true)', () => { @@ -268,12 +257,12 @@ describe( 'dev-release-tools/utils', () => { const pathsCollection = getPackagesPaths( options ); - expect( pathsCollection.matched ).to.be.instanceof( Set ); - expect( pathsCollection.matched.size ).to.equal( 0 ); + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 0 ); - expect( pathsCollection.skipped ).to.be.instanceof( Set ); - expect( pathsCollection.skipped.size ).to.equal( 1 ); - expect( pathsCollection.skipped.has( '/tmp' ) ).to.equal( true ); + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 1 ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); } ); } ); } ); From 628c9c4452ebee330aa16ed9e08779a3cb37315c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 12:22:52 +0200 Subject: [PATCH 048/172] Aligned tests in `utils/getnpmtagfromversion.js`. --- .../tests/utils/getnpmtagfromversion.js | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js b/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js index e04a71e10..4f37d59dd 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js @@ -3,38 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, vi } from 'vitest'; +import semver from 'semver'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); +import getNpmTagFromVersion from '../../lib/utils/getnpmtagfromversion.js'; -describe( 'dev-release-tools/getNpmTagFromVersion', () => { - let stub, getNpmTagFromVersion; - - beforeEach( () => { - stub = { - semver: { - prerelease: sinon.stub() - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'semver', stub.semver ); - - getNpmTagFromVersion = require( '../../lib/utils/getnpmtagfromversion' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - } ); +vi.mock( 'semver' ); +describe( 'dev-release-tools/getNpmTagFromVersion', () => { it( 'should return "latest" when processing a X.Y.Z version', () => { expect( getNpmTagFromVersion( '1.0.0' ) ).to.equal( 'latest' ); expect( getNpmTagFromVersion( '2.1.0' ) ).to.equal( 'latest' ); @@ -42,7 +18,7 @@ describe( 'dev-release-tools/getNpmTagFromVersion', () => { } ); it( 'should return "alpha" when processing a X.Y.Z-alpha.X version', () => { - stub.semver.prerelease.returns( [ 'alpha', 0 ] ); + vi.mocked( semver.prerelease ).mockReturnValue( [ 'alpha', 0 ] ); expect( getNpmTagFromVersion( '1.0.0-alpha.0' ) ).to.equal( 'alpha' ); expect( getNpmTagFromVersion( '2.1.0-alpha.0' ) ).to.equal( 'alpha' ); From 2690820a2085a262ce90a7925a6ccc75757c73a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Mon, 9 Sep 2024 12:29:16 +0200 Subject: [PATCH 049/172] Aligned tests in `utils/getnewversiontype.js`. --- .../tests/utils/getnewversiontype.js | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js b/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js index 00c7b8dba..194f1fbdc 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js @@ -3,82 +3,80 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const getNewVersionType = require( '../../lib/utils/getnewversiontype' ); +import { describe, it, expect } from 'vitest'; +import { getNewVersionType } from '../../lib/utils/getnewversiontype.js'; describe( 'dev-release-tools/utils', () => { describe( 'getSubPackagesPaths()', () => { it( 'returns "skip" when passing an empty array of commits', () => { - expect( getNewVersionType( [] ) ).to.equal( 'skip' ); + expect( getNewVersionType( [] ) ).toEqual( 'skip' ); } ); it( 'returns "internal" when passing non-public commits', () => { - expect( getNewVersionType( [ { isPublicCommit: false } ] ) ).to.equal( 'internal' ); + expect( getNewVersionType( [ { isPublicCommit: false } ] ) ).toEqual( 'internal' ); } ); it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Other" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Other' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'major' ); + expect( getNewVersionType( commits ) ).toEqual( 'major' ); } ); it( 'returns "major" if BREAKING CHANGES was introduced in "type:Other" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Other' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'major' ); + expect( getNewVersionType( commits ) ).toEqual( 'major' ); } ); it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Fix" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Fix' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'major' ); + expect( getNewVersionType( commits ) ).toEqual( 'major' ); } ); it( 'returns "major" if BREAKING CHANGES was introduced in "type:Fix" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Fix' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'major' ); + expect( getNewVersionType( commits ) ).toEqual( 'major' ); } ); it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Feature" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Feature' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'major' ); + expect( getNewVersionType( commits ) ).toEqual( 'major' ); } ); it( 'returns "major" if BREAKING CHANGES was introduced in "type:Feature" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Feature' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'major' ); + expect( getNewVersionType( commits ) ).toEqual( 'major' ); } ); it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Other" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Other' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'minor' ); + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); } ); it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Fix" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Fix' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'minor' ); + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); } ); it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Feature" commit', () => { const commits = [ { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Feature' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'minor' ); + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); } ); it( 'returns "minor" if found "type:Feature" commit in the collection', () => { @@ -88,7 +86,7 @@ describe( 'dev-release-tools/utils', () => { { isPublicCommit: false, notes: [], rawType: 'Docs' }, { isPublicCommit: true, notes: [], rawType: 'Feature' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'minor' ); + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); } ); it( 'returns "major" if found "MAJOR BREAKING CHANGES" commit in the collection', () => { @@ -105,7 +103,7 @@ describe( 'dev-release-tools/utils', () => { rawType: 'Feature' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'major' ); + expect( getNewVersionType( commits ) ).toEqual( 'major' ); } ); it( 'returns "patch" if no breaking changes or features commits were made', () => { @@ -113,7 +111,7 @@ describe( 'dev-release-tools/utils', () => { { isPublicCommit: true, notes: [], rawType: 'Fix' }, { isPublicCommit: true, notes: [], rawType: 'Other' } ]; - expect( getNewVersionType( commits ) ).to.equal( 'patch' ); + expect( getNewVersionType( commits ) ).toEqual( 'patch' ); } ); } ); } ); From df9e49c937f5be91f5119eddbed2cf2ebbf205f6 Mon Sep 17 00:00:00 2001 From: Marcin Panek Date: Mon, 9 Sep 2024 12:54:37 +0200 Subject: [PATCH 050/172] Removed "use strict". --- packages/ckeditor5-dev-bump-year/lib/bumpyear.js | 2 -- packages/ckeditor5-dev-bump-year/lib/index.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/ckeditor5-dev-bump-year/lib/bumpyear.js b/packages/ckeditor5-dev-bump-year/lib/bumpyear.js index a37f28e93..362375b5b 100644 --- a/packages/ckeditor5-dev-bump-year/lib/bumpyear.js +++ b/packages/ckeditor5-dev-bump-year/lib/bumpyear.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - import chalk from 'chalk'; import fs from 'fs'; import { globSync } from 'glob'; diff --git a/packages/ckeditor5-dev-bump-year/lib/index.js b/packages/ckeditor5-dev-bump-year/lib/index.js index 77ac60e0b..7cac1b92e 100644 --- a/packages/ckeditor5-dev-bump-year/lib/index.js +++ b/packages/ckeditor5-dev-bump-year/lib/index.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - import bumpYear from './bumpyear.js'; export default { From 830377ebe7f9f0ac330e9f279d73426ca2268261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Tue, 10 Sep 2024 07:20:41 +0200 Subject: [PATCH 051/172] Aligned tests in `utils/getcommits.js`. --- .../lib/utils/getcommits.js | 2 +- .../tests/utils/getcommits.js | 160 ++++++++---------- 2 files changed, 69 insertions(+), 93 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js index 2faa99149..b58d488b8 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js @@ -7,7 +7,7 @@ import conventionalCommitsParser from 'conventional-commits-parser'; import conventionalCommitsFilter from 'conventional-commits-filter'; import gitRawCommits from 'git-raw-commits'; import concat from 'concat-stream'; -import parserOptions from './parseroptions'; +import parserOptions from './parseroptions.js'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 56f8e805d..52bb4495c 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -3,29 +3,26 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const sinon = require( 'sinon' ); -const expect = require( 'chai' ).expect; -const mockery = require( 'mockery' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach } from 'vitest'; +import fs from 'fs'; +import path from 'path'; +import gitRawCommits from 'git-raw-commits'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; describe( 'dev-release-tools/utils', () => { - let tmpCwd, cwd, stubs, sandbox, getCommits; + let tmpCwd, cwd, getCommits, stubs; describe( 'getCommits()', () => { - before( () => { + beforeAll( () => { cwd = process.cwd(); tmpCwd = fs.mkdtempSync( __dirname + path.sep ); } ); - after( () => { + afterAll( () => { fs.rmdirSync( tmpCwd ); } ); - beforeEach( () => { + beforeEach( async () => { process.chdir( tmpCwd ); exec( 'git init' ); @@ -35,49 +32,30 @@ describe( 'dev-release-tools/utils', () => { exec( 'git config user.name "CKEditor5 CI"' ); } - sandbox = sinon.createSandbox(); + vi.doMock( 'git-raw-commits', () => ( { + default: vi.fn( gitRawCommits ) + } ) ); - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); + vi.doMock( '@ckeditor/ckeditor5-dev-utils' ); stubs = { - shExec: sandbox.stub(), - gitRawCommits: sandbox.stub() + gitRawCommits: ( await import( 'git-raw-commits' ) ).default, + devTools: ( await import( '@ckeditor/ckeditor5-dev-utils' ) ).tools }; - // Other kinds of mocking the `git-raw-commits` package end with an error. - // But this way it works. - stubs.gitRawCommits.callsFake( options => { - const modulePath = require.resolve( 'git-raw-commits' ); - - return require( modulePath )( options ); - } ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - tools: { - shExec: stubs.shExec - } - } ); - - mockery.registerMock( 'git-raw-commits', stubs.gitRawCommits ); - - getCommits = require( '../../lib/utils/getcommits' ); + getCommits = ( await import( '../../lib/utils/getcommits.js' ) ).getCommits; } ); afterEach( () => { process.chdir( cwd ); fs.rmSync( path.join( tmpCwd, '.git' ), { recursive: true } ); - sandbox.restore(); - mockery.disable(); + vi.resetModules(); } ); describe( 'branch for releasing is the same as the main branch', () => { - beforeEach( () => { - stubs.shExec.onFirstCall().returns( 'master\n' ); + beforeEach( async () => { + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'master\n' ); } ); it( 'throws an error when the specified release branch is not equal to the current checked out branch', () => { @@ -87,7 +65,7 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Supposed to be rejected.' ); }, err => { - expect( err.message ).to.equal( + expect( err.message ).toEqual( 'Expected to be checked out on the release branch ("release") instead of "master". Aborting.' ); } @@ -95,8 +73,8 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'throws an error when the default release branch is not equal to the current checked out branch', () => { - stubs.shExec.reset(); - stubs.shExec.onFirstCall().returns( 'release\n' ); + vi.mocked( stubs.devTools.shExec ).mockReset(); + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'release\n' ); return getCommits( transformCommit ) .then( @@ -104,7 +82,7 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Supposed to be rejected.' ); }, err => { - expect( err.message ).to.equal( + expect( err.message ).toEqual( 'Expected to be checked out on the release branch ("master") instead of "release". Aborting.' ); } @@ -118,7 +96,7 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Supposed to be rejected.' ); }, err => { - expect( err.message ).to.equal( 'Given repository is empty.' ); + expect( err.message ).toEqual( 'Given repository is empty.' ); } ); } ); @@ -130,7 +108,7 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Supposed to be rejected.' ); }, err => { - expect( err.message ).to.equal( 'Cannot find tag or commit "foobar" in given repository.' ); + expect( err.message ).toEqual( 'Cannot find tag or commit "foobar" in given repository.' ); } ); } ); @@ -141,15 +119,15 @@ describe( 'dev-release-tools/utils', () => { return getCommits( transformCommit ) .then( commits => { - expect( commits.length ).to.equal( 2 ); - expect( commits[ 0 ].header ).to.equal( 'Second.' ); - expect( commits[ 1 ].header ).to.equal( 'First.' ); + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'Second.' ); + expect( commits[ 1 ].header ).toEqual( 'First.' ); } ); } ); it( 'returns an array of commits after "git init" (main branch is not equal to "master")', () => { - stubs.shExec.reset(); - stubs.shExec.onFirstCall().returns( 'main-branch\n' ); + vi.mocked( stubs.devTools.shExec ).mockReset(); + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'main-branch\n' ); exec( 'git checkout -b main-branch' ); exec( 'git commit --allow-empty --message "First."' ); @@ -157,9 +135,9 @@ describe( 'dev-release-tools/utils', () => { return getCommits( transformCommit, { mainBranch: 'main-branch', releaseBranch: 'main-branch' } ) .then( commits => { - expect( commits.length ).to.equal( 2 ); - expect( commits[ 0 ].header ).to.equal( 'Second.' ); - expect( commits[ 1 ].header ).to.equal( 'First.' ); + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'Second.' ); + expect( commits[ 1 ].header ).toEqual( 'First.' ); } ); } ); @@ -172,11 +150,11 @@ describe( 'dev-release-tools/utils', () => { return getCommits( transformCommit ) .then( commits => { - expect( commits.length ).to.equal( 4 ); - expect( commits[ 0 ].header ).to.equal( 'Fourth.' ); - expect( commits[ 1 ].header ).to.equal( 'Third.' ); - expect( commits[ 2 ].header ).to.equal( 'Second.' ); - expect( commits[ 3 ].header ).to.equal( 'First.' ); + expect( commits.length ).toEqual( 4 ); + expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); + expect( commits[ 1 ].header ).toEqual( 'Third.' ); + expect( commits[ 2 ].header ).toEqual( 'Second.' ); + expect( commits[ 3 ].header ).toEqual( 'First.' ); } ); } ); @@ -189,9 +167,9 @@ describe( 'dev-release-tools/utils', () => { return getCommits( transformCommit, { from: 'v1.0.0' } ) .then( commits => { - expect( commits.length ).to.equal( 2 ); - expect( commits[ 0 ].header ).to.equal( 'Fourth.' ); - expect( commits[ 1 ].header ).to.equal( 'Third.' ); + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); + expect( commits[ 1 ].header ).toEqual( 'Third.' ); } ); } ); @@ -207,49 +185,47 @@ describe( 'dev-release-tools/utils', () => { return getCommits( transformCommit, { from: commitId } ) .then( commits => { - expect( commits.length ).to.equal( 1 ); - expect( commits[ 0 ].header ).to.equal( 'Fourth.' ); + expect( commits.length ).toEqual( 1 ); + expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); } ); } ); it( 'ignores false values returned by the "transformCommit" mapper', () => { - const transformCommit = sinon.stub(); - - transformCommit.onFirstCall().callsFake( commit => commit ); - transformCommit.onSecondCall().callsFake( () => null ); + const transformCommit = vi.fn() + .mockImplementationOnce( commit => commit ) + .mockImplementationOnce( () => null ); exec( 'git commit --allow-empty --message "First."' ); exec( 'git commit --allow-empty --message "Second."' ); return getCommits( transformCommit ) .then( commits => { - expect( commits.length ).to.equal( 1 ); - expect( commits[ 0 ].header ).to.equal( 'Second.' ); + expect( commits.length ).toEqual( 1 ); + expect( commits[ 0 ].header ).toEqual( 'Second.' ); } ); } ); it( 'handles arrays returned by the "transformCommit" mapper', () => { - const transformCommit = sinon.stub(); - - transformCommit.onFirstCall().callsFake( commit => { - return [ commit, commit ]; - } ); + const transformCommit = vi.fn() + .mockImplementationOnce( commit => { + return [ commit, commit ]; + } ); exec( 'git commit --allow-empty --message "First."' ); return getCommits( transformCommit ) .then( commits => { - expect( commits.length ).to.equal( 2 ); - expect( commits[ 0 ].header ).to.equal( 'First.' ); - expect( commits[ 1 ].header ).to.equal( 'First.' ); + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'First.' ); + expect( commits[ 1 ].header ).toEqual( 'First.' ); } ); } ); } ); describe( 'branch for releasing is other than the main branch', () => { it( 'collects commits from the main branch and the release branch', () => { - stubs.shExec.onFirstCall().returns( 'release\n' ); - stubs.shExec.onSecondCall().callsFake( exec ); + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'release\n' ); + vi.mocked( stubs.devTools.shExec ).mockImplementationOnce( exec ); exec( 'git commit --allow-empty --message "Type: master: 1."' ); exec( 'git tag v1.0.0' ); @@ -288,9 +264,9 @@ describe( 'dev-release-tools/utils', () => { return getCommits( transformCommit, { from: 'v1.0.0', releaseBranch: 'release' } ) .then( commits => { - expect( commits.length ).to.equal( 8 ); + expect( commits.length ).toEqual( 8 ); - expect( stubs.gitRawCommits.firstCall.args[ 0 ] ).to.deep.equal( { + expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 1, { from: 'v1.0.0', to: baseCommit, format: '%B%n-hash-%n%H', @@ -298,7 +274,7 @@ describe( 'dev-release-tools/utils', () => { firstParent: true } ); - expect( stubs.gitRawCommits.secondCall.args[ 0 ] ).to.deep.equal( { + expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 2, { to: 'HEAD', from: baseCommit, format: '%B%n-hash-%n%H', @@ -309,13 +285,13 @@ describe( 'dev-release-tools/utils', () => { } ); } ); } ); +} ); - function exec( command ) { - return tools.shExec( command, { verbosity: 'error' } ); - } +function exec( command ) { + return tools.shExec( command, { verbosity: 'error' } ); +} - // Do not modify the commit. - function transformCommit( commit ) { - return commit; - } -} ); +// Do not modify the commit. +function transformCommit( commit ) { + return commit; +} From a551fc5cecbae799057a6726ba001e20ad7a5579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Tue, 10 Sep 2024 07:33:35 +0200 Subject: [PATCH 052/172] Aligned tests in `utils/getchangedfilesforcommit.js`. --- .../tests/utils/getchangedfilesforcommit.js | 33 ++++++++----------- .../tests/utils/getcommits.js | 16 ++++----- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js index f9ef54978..6b4d4c21e 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js @@ -3,26 +3,23 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest'; +import fs from 'fs'; +import path from 'path'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { getChangedFilesForCommit } from '../../lib/utils/getchangedfilesforcommit.js'; -const fs = require( 'fs' ); -const path = require( 'path' ); -const expect = require( 'chai' ).expect; -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +describe( 'dev-release-tools/utils', () => { + let tmpCwd, cwd; -describe( 'dev-release-tools/utils/transform-commit', () => { - let tmpCwd, cwd, getChangedFilesForCommit; - - describe( 'getChangedFilesForCommit()', function() { - this.timeout( 15 * 1000 ); - - before( () => { + describe( 'getChangedFilesForCommit()', { timeout: 15000 }, function() { + beforeAll( () => { cwd = process.cwd(); tmpCwd = fs.mkdtempSync( __dirname + path.sep ); process.chdir( tmpCwd ); } ); - after( () => { + afterAll( () => { process.chdir( cwd ); fs.rmdirSync( tmpCwd ); } ); @@ -34,8 +31,6 @@ describe( 'dev-release-tools/utils/transform-commit', () => { exec( 'git config user.email "ckeditor5@ckeditor.com"' ); exec( 'git config user.name "CKEditor5 CI"' ); } - - getChangedFilesForCommit = require( '../../lib/utils/getchangedfilesforcommit' ); } ); afterEach( () => { @@ -54,7 +49,7 @@ describe( 'dev-release-tools/utils/transform-commit', () => { const files = getChangedFilesForCommit( getLastCommit() ); - expect( files ).to.deep.equal( [ + expect( files ).toEqual( [ '1.txt', '2.txt', '3.txt', @@ -80,7 +75,7 @@ describe( 'dev-release-tools/utils/transform-commit', () => { const files = getChangedFilesForCommit( getLastCommit() ); - expect( files ).to.deep.equal( [ + expect( files ).toEqual( [ '2.js', '3.js', '4.js' @@ -110,7 +105,7 @@ describe( 'dev-release-tools/utils/transform-commit', () => { const files = getChangedFilesForCommit( getLastCommit() ); - expect( files ).to.deep.equal( [ + expect( files ).toEqual( [ '5.json', '6.json' ] ); @@ -149,7 +144,7 @@ describe( 'dev-release-tools/utils/transform-commit', () => { const files = getChangedFilesForCommit( getLastCommit() ); - expect( files ).to.deep.equal( [ + expect( files ).toEqual( [ '5.json', '6.json' ] ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 52bb4495c..6ab46e827 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -285,13 +285,13 @@ describe( 'dev-release-tools/utils', () => { } ); } ); } ); -} ); -function exec( command ) { - return tools.shExec( command, { verbosity: 'error' } ); -} + function exec( command ) { + return tools.shExec( command, { verbosity: 'error' } ); + } -// Do not modify the commit. -function transformCommit( commit ) { - return commit; -} + // Do not modify the commit. + function transformCommit( commit ) { + return commit; + } +} ); From 579882732d567f230f732b06cfe8fe9f15bc90d3 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 08:00:16 +0200 Subject: [PATCH 053/172] Extracted utils from `utils/changelog` to separate files. --- .../ckeditor5-dev-release-tools/lib/index.js | 4 +- .../lib/tasks/creategithubrelease.js | 2 +- .../generatechangelogformonorepository.js | 52 ++- .../generatechangelogforsinglepackage.js | 39 +- .../lib/utils/changelog.js | 104 ----- .../lib/utils/constants.js | 14 + .../lib/utils/displaycommits.js | 4 +- .../lib/utils/getchangelog.js | 22 + .../lib/utils/getchangesforversion.js | 28 ++ .../lib/utils/getformatteddate.js | 14 + .../lib/utils/savechangelog.js | 18 + .../lib/utils/transformcommitfactory.js | 2 +- .../lib/utils/truncatechangelog.js | 42 ++ .../lib/utils/versions.js | 2 +- .../tests/index.js | 50 +- .../tests/tasks/creategithubrelease.js | 4 +- .../changelog--extract-to-seperate-files.js | 441 ++++++++++++++++++ .../tests/utils/changelog.js | 437 ----------------- .../tests/utils/versions.js | 4 +- 19 files changed, 662 insertions(+), 621 deletions(-) delete mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/changelog.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/constants.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js delete mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/changelog.js diff --git a/packages/ckeditor5-dev-release-tools/lib/index.js b/packages/ckeditor5-dev-release-tools/lib/index.js index 1a14aea99..1d6ce6481 100644 --- a/packages/ckeditor5-dev-release-tools/lib/index.js +++ b/packages/ckeditor5-dev-release-tools/lib/index.js @@ -23,7 +23,9 @@ export { getCurrent, getLastTagFromGit } from './utils/versions.js'; -export { getChangesForVersion, getChangelog, saveChangelog } from './utils/changelog.js'; +export { default as getChangesForVersion } from './utils/getchangesforversion.js'; +export { default as getChangelog } from './utils/getchangelog.js'; +export { default as saveChangelog } from './utils/savechangelog.js'; export { default as executeInParallel } from './utils/executeinparallel.js'; export { default as validateRepositoryToRelease } from './utils/validaterepositorytorelease.js'; export { default as checkVersionAvailability } from './utils/checkversionavailability.js'; diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js index eaf7b5602..4c8eb5f77 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js @@ -5,7 +5,7 @@ import { Octokit } from '@octokit/rest'; import semver from 'semver'; -import transformCommitUtils from '../utils/transformcommitutils.js'; +import * as transformCommitUtils from '../utils/transformcommitutils.js'; const { getRepositoryUrl } = transformCommitUtils; diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js index ec9be9bc8..708a73438 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js @@ -9,18 +9,22 @@ import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; import compareFunc from 'compare-func'; import chalk from 'chalk'; import semver from 'semver'; -import changelogUtils from '../utils/changelog'; -import cli from '../utils/cli'; -import displayCommits from '../utils/displaycommits'; -import displaySkippedPackages from '../utils/displayskippedpackages'; -import generateChangelog from '../utils/generatechangelog'; -import getPackageJson from '../utils/getpackagejson'; -import getPackagesPaths from '../utils/getpackagespaths'; -import getCommits from '../utils/getcommits'; -import getNewVersionType from '../utils/getnewversiontype'; -import getWriterOptions from '../utils/getwriteroptions'; -import { getRepositoryUrl } from '../utils/transformcommitutils'; -import transformCommitFactory from '../utils/transformcommitfactory'; +import cli from '../utils/cli.js'; +import displayCommits from '../utils/displaycommits.js'; +import displaySkippedPackages from '../utils/displayskippedpackages.js'; +import generateChangelog from '../utils/generatechangelog.js'; +import getPackageJson from '../utils/getpackagejson.js'; +import getPackagesPaths from '../utils/getpackagespaths.js'; +import getCommits from '../utils/getcommits.js'; +import getNewVersionType from '../utils/getnewversiontype.js'; +import getWriterOptions from '../utils/getwriteroptions.js'; +import getFormattedDate from '../utils/getformatteddate.js'; +import getChangelog from '../utils/getchangelog.js'; +import saveChangelog from '../utils/savechangelog.js'; +import truncateChangelog from '../utils/truncatechangelog.js'; +import transformCommitFactory from '../utils/transformcommitfactory.js'; +import { getRepositoryUrl } from '../utils/transformcommitutils.js'; +import { CHANGELOG_FILE, CHANGELOG_HEADER } from '../utils/constants.js'; const VERSIONING_POLICY_URL = 'https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html'; const noteInfo = `[ℹī¸](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-changes)`; @@ -123,11 +127,11 @@ export default async function generateChangelogForMonoRepository( options ) { } if ( !skipFileSave ) { - await saveChangelog(); + await saveChangelogToFile(); // Make a commit from the repository where we started. process.chdir( options.cwd ); - tools.shExec( `git add ${ changelogUtils.changelogFile }`, { verbosity: 'error' } ); + tools.shExec( `git add ${ CHANGELOG_FILE }`, { verbosity: 'error' } ); tools.shExec( 'git commit -m "Docs: Changelog. [skip ci]"', { verbosity: 'error' } ); logInfo( 'Committed.', { indentLevel: 1 } ); } @@ -408,7 +412,7 @@ export default async function generateChangelogForMonoRepository( options ) { isPatch: semver.diff( version, rootPkgJson.version ) === 'patch', skipCommitsLink: Boolean( options.skipLinks ), skipCompareLink: Boolean( options.skipLinks ), - date: options.formatDate ? options.formatDate( new Date() ) : changelogUtils.getFormattedDate() + date: options.formatDate ? options.formatDate( new Date() ) : getFormattedDate() }; const writerOptions = getWriterOptions( { @@ -462,7 +466,7 @@ export default async function generateChangelogForMonoRepository( options ) { const dependenciesSummary = generateSummaryOfChangesInPackages(); return [ - changelogUtils.changelogHeader, + CHANGELOG_HEADER, changesFromCommits.trim(), '\n\n', dependenciesSummary @@ -472,26 +476,24 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Combines the generated changes based on commits and summary of version changes in packages. * Appends those changes at the beginning of the changelog file. - * - * @param {String} changesFromCommits Generated entries based on commits. */ - async function saveChangelog() { + async function saveChangelogToFile() { logProcess( 'Saving changelog...' ); - if ( !fs.existsSync( changelogUtils.changelogFile ) ) { + if ( !fs.existsSync( CHANGELOG_FILE ) ) { logInfo( 'Changelog file does not exist. Creating...', { isWarning: true, indentLevel: 1 } ); - changelogUtils.saveChangelog( changelogUtils.changelogHeader ); + saveChangelog( CHANGELOG_FILE ); } logInfo( 'Preparing a summary of version changes in packages.', { indentLevel: 1 } ); - let currentChangelog = changelogUtils.getChangelog(); + let currentChangelog = getChangelog(); const nextVersionChangelog = await getChangelogForNextVersion(); // Remove header from current changelog. - currentChangelog = currentChangelog.replace( changelogUtils.changelogHeader, '' ).trim(); + currentChangelog = currentChangelog.replace( CHANGELOG_HEADER, '' ).trim(); // Concat header, new entries and old changelog to single string. let newChangelog = nextVersionChangelog + '\n\n\n' + currentChangelog; @@ -499,10 +501,10 @@ export default async function generateChangelogForMonoRepository( options ) { newChangelog = newChangelog.trim() + '\n'; // Save the changelog. - changelogUtils.saveChangelog( newChangelog ); + saveChangelog( newChangelog ); // Truncate the changelog to keep the latest five release entries. - changelogUtils.truncateChangelog( 5 ); + truncateChangelog( 5 ); logInfo( 'Saved.', { indentLevel: 1 } ); } diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js index 884df91b4..42077a391 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js @@ -7,16 +7,19 @@ import fs from 'fs'; import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; import chalk from 'chalk'; import semver from 'semver'; -import cli from '../utils/cli'; -import changelogUtils from '../utils/changelog'; -import displayCommits from '../utils/displaycommits'; -import generateChangelog from '../utils/generatechangelog'; -import getPackageJson from '../utils/getpackagejson'; -import getNewVersionType from '../utils/getnewversiontype'; -import getCommits from '../utils/getcommits'; -import getWriterOptions from '../utils/getwriteroptions'; -import { getRepositoryUrl } from '../utils/transformcommitutils'; -import transformCommitFactory from '../utils/transformcommitfactory'; +import cli from '../utils/cli.js'; +import displayCommits from '../utils/displaycommits.js'; +import generateChangelog from '../utils/generatechangelog.js'; +import getPackageJson from '../utils/getpackagejson.js'; +import getNewVersionType from '../utils/getnewversiontype.js'; +import getCommits from '../utils/getcommits.js'; +import getWriterOptions from '../utils/getwriteroptions.js'; +import { getRepositoryUrl } from '../utils/transformcommitutils.js'; +import transformCommitFactory from '../utils/transformcommitfactory.js'; +import getFormattedDate from '../utils/getformatteddate.js'; +import saveChangelog from '../utils/savechangelog.js'; +import getChangelog from '../utils/getchangelog.js'; +import { CHANGELOG_FILE, CHANGELOG_HEADER } from '../utils/constants.js'; const SKIP_GENERATE_CHANGELOG = 'Typed "skip" as a new version. Aborting.'; @@ -104,7 +107,7 @@ export default async function generateChangelogForSinglePackage( options = {} ) isInternalRelease, skipCommitsLink: Boolean( options.skipLinks ), skipCompareLink: Boolean( options.skipLinks ), - date: options.formatDate ? options.formatDate( new Date() ) : changelogUtils.getFormattedDate() + date: options.formatDate ? options.formatDate( new Date() ) : getFormattedDate() }; const writerOptions = getWriterOptions( { @@ -135,25 +138,25 @@ export default async function generateChangelogForSinglePackage( options = {} ) .then( changesFromCommits => { logProcess( 'Saving changelog...' ); - if ( !fs.existsSync( changelogUtils.changelogFile ) ) { + if ( !fs.existsSync( CHANGELOG_FILE ) ) { logInfo( 'Changelog file does not exist. Creating...', { isWarning: true, indentLevel: 1 } ); - changelogUtils.saveChangelog( changelogUtils.changelogHeader ); + saveChangelog( CHANGELOG_HEADER ); } - let currentChangelog = changelogUtils.getChangelog(); + let currentChangelog = getChangelog(); // Remove header from current changelog. - currentChangelog = currentChangelog.replace( changelogUtils.changelogHeader, '' ); + currentChangelog = currentChangelog.replace( CHANGELOG_HEADER, '' ); // Concat header, new and current changelog. - let newChangelog = changelogUtils.changelogHeader + changesFromCommits + currentChangelog.trim(); + let newChangelog = CHANGELOG_HEADER + changesFromCommits + currentChangelog.trim(); newChangelog = newChangelog.trim() + '\n'; // Save the changelog. - changelogUtils.saveChangelog( newChangelog ); + saveChangelog( newChangelog ); - tools.shExec( `git add ${ changelogUtils.changelogFile }`, { verbosity: 'error' } ); + tools.shExec( `git add ${ CHANGELOG_FILE }`, { verbosity: 'error' } ); tools.shExec( 'git commit -m "Docs: Changelog. [skip ci]"', { verbosity: 'error' } ); logInfo( 'Saved.', { indentLevel: 1 } ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js deleted file mode 100644 index 07c9e1a48..000000000 --- a/packages/ckeditor5-dev-release-tools/lib/utils/changelog.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import fs from 'fs'; -import path from 'path'; -import { format } from 'date-fns'; -import { getRepositoryUrl } from './transformcommitutils'; - -/** - * Changelog file name. - */ -export const CHANGELOG_FILE = 'CHANGELOG.md'; - -/** - * Changelog header. - */ -export const CHANGELOG_HEADER = 'Changelog\n=========\n\n'; - -/** - * Retrieves changes from the changelog for the given version (tag). - * - * @param {String} version - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} - */ -export function getChangesForVersion( version, cwd = process.cwd() ) { - version = version.replace( /^v/, '' ); - - const changelog = getChangelog( cwd ).replace( CHANGELOG_HEADER, '\n' ); - const match = changelog.match( new RegExp( `\\n(## \\[?${ version }\\]?[\\s\\S]+?)(?:\\n## \\[?|$)` ) ); - - if ( !match || !match[ 1 ] ) { - return null; - } - - return match[ 1 ].replace( /##[^\n]+\n/, '' ).trim(); -} - -/** - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} - */ -export function getChangelog( cwd = process.cwd() ) { - const changelogFile = path.join( cwd, CHANGELOG_FILE ); - - if ( !fs.existsSync( changelogFile ) ) { - return null; - } - - return fs.readFileSync( changelogFile, 'utf-8' ); -} - -/** - * @param {String} content - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - */ -export function saveChangelog( content, cwd = process.cwd() ) { - const changelogFile = path.join( cwd, CHANGELOG_FILE ); - - fs.writeFileSync( changelogFile, content, 'utf-8' ); -} - -/** - * @param {Number} length - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - */ -export function truncateChangelog( length, cwd = process.cwd() ) { - const changelog = getChangelog( cwd ); - - if ( !changelog ) { - return; - } - - const entryHeader = '## [\\s\\S]+?'; - const entryHeaderRegexp = new RegExp( `\\n(${ entryHeader })(?=\\n${ entryHeader }|$)`, 'g' ); - - const entries = [ ...changelog.matchAll( entryHeaderRegexp ) ] - .filter( match => match && match[ 1 ] ) - .map( match => match[ 1 ] ); - - if ( !entries.length ) { - return; - } - - const truncatedEntries = entries.slice( 0, length ); - - const changelogFooter = entries.length > truncatedEntries.length ? - `\n\n---\n\nTo see all releases, visit the [release page](${ getRepositoryUrl( cwd ) }/releases).\n` : - '\n'; - - const truncatedChangelog = CHANGELOG_HEADER + truncatedEntries.join( '\n' ).trim() + changelogFooter; - - saveChangelog( truncatedChangelog, cwd ); -} - -/** - * @returns {String} - */ -export function getFormattedDate() { - return format( new Date(), 'yyyy-MM-dd' ); -} - diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/constants.js b/packages/ckeditor5-dev-release-tools/lib/utils/constants.js new file mode 100644 index 000000000..e2dd09a0c --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/constants.js @@ -0,0 +1,14 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * Changelog file name. + */ +export const CHANGELOG_FILE = 'CHANGELOG.md'; + +/** + * Changelog header. + */ +export const CHANGELOG_HEADER = 'Changelog\n=========\n\n'; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js index 1e7c5d269..0145c5749 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js @@ -5,8 +5,8 @@ import chalk from 'chalk'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; -import utils from './transformcommitutils'; -import { INDENT_SIZE, COMMIT_INDENT_SIZE } from './cli'; +import * as utils from './transformcommitutils.js'; +import { INDENT_SIZE, COMMIT_INDENT_SIZE } from './cli.js'; /** * @param {Array.|Set.} commits diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js new file mode 100644 index 000000000..6d0d39381 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js @@ -0,0 +1,22 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; +import path from 'path'; +import { CHANGELOG_FILE } from './constants.js'; + +/** + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {String|null} + */ +export default function getChangelog( cwd = process.cwd() ) { + const changelogFile = path.join( cwd, CHANGELOG_FILE ); + + if ( !fs.existsSync( changelogFile ) ) { + return null; + } + + return fs.readFileSync( changelogFile, 'utf-8' ); +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js b/packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js new file mode 100644 index 000000000..1c61c7981 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js @@ -0,0 +1,28 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { CHANGELOG_HEADER } from './constants.js'; +import getChangelog from './getchangelog.js'; + +/** + * Retrieves changes from the changelog for the given version (tag). + * + * @param {String} version + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {String|null} + */ +export default function getChangesForVersion( version, cwd = process.cwd() ) { + version = version.replace( /^v/, '' ); + + const changelog = getChangelog( cwd ).replace( CHANGELOG_HEADER, '\n' ); + + const match = changelog.match( new RegExp( `\\n(## \\[?${ version }\\]?[\\s\\S]+?)(?:\\n## \\[?|$)` ) ); + + if ( !match || !match[ 1 ] ) { + return null; + } + + return match[ 1 ].replace( /##[^\n]+\n/, '' ).trim(); +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js b/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js new file mode 100644 index 000000000..fcd8fde29 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js @@ -0,0 +1,14 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { format } from 'date-fns'; + +/** + * @returns {String} + */ +export default function getFormattedDate() { + return format( new Date(), 'yyyy-MM-dd' ); +} + diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js new file mode 100644 index 000000000..412c948c0 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js @@ -0,0 +1,18 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; +import path from 'path'; +import { CHANGELOG_FILE } from './constants.js'; + +/** + * @param {String} content + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + */ +export default function saveChangelog( content, cwd = process.cwd() ) { + const changelogFile = path.join( cwd, CHANGELOG_FILE ); + + fs.writeFileSync( changelogFile, content, 'utf-8' ); +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js index b4177ccbe..01e5c8b21 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js @@ -4,7 +4,7 @@ */ import { cloneDeepWith } from 'lodash'; -import utils from './transformcommitutils.js'; +import * as utils from './transformcommitutils.js'; import { getChangedFilesForCommit } from './getchangedfilesforcommit.js'; // Squash commit follows the pattern: "A pull request title (#{number})". diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js new file mode 100644 index 000000000..a1b24298a --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js @@ -0,0 +1,42 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { CHANGELOG_HEADER } from './constants.js'; +import { getRepositoryUrl } from './transformcommitutils.js'; +import saveChangelog from './savechangelog.js'; +import getChangelog from './getchangelog.js'; + +/** + * @param {Number} length + * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + */ +export default function truncateChangelog( length, cwd = process.cwd() ) { + const changelog = getChangelog( cwd ); + + if ( !changelog ) { + return; + } + + const entryHeader = '## [\\s\\S]+?'; + const entryHeaderRegexp = new RegExp( `\\n(${ entryHeader })(?=\\n${ entryHeader }|$)`, 'g' ); + + const entries = [ ...changelog.matchAll( entryHeaderRegexp ) ] + .filter( match => match && match[ 1 ] ) + .map( match => match[ 1 ] ); + + if ( !entries.length ) { + return; + } + + const truncatedEntries = entries.slice( 0, length ); + + const changelogFooter = entries.length > truncatedEntries.length ? + `\n\n---\n\nTo see all releases, visit the [release page](${ getRepositoryUrl( cwd ) }/releases).\n` : + '\n'; + + const truncatedChangelog = CHANGELOG_HEADER + truncatedEntries.join( '\n' ).trim() + changelogFooter; + + saveChangelog( truncatedChangelog, cwd ); +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js index 8437682f5..645ce5792 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js @@ -4,7 +4,7 @@ */ import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import { getChangelog } from './changelog.js'; +import getChangelog from './getchangelog.js'; import { getPackageJson } from './getpackagejson.js'; /** diff --git a/packages/ckeditor5-dev-release-tools/tests/index.js b/packages/ckeditor5-dev-release-tools/tests/index.js index 633374bb6..e3dea1d97 100644 --- a/packages/ckeditor5-dev-release-tools/tests/index.js +++ b/packages/ckeditor5-dev-release-tools/tests/index.js @@ -3,20 +3,21 @@ * For licensing, see LICENSE.md. */ -'use strict'; - import { describe, expect, it, vi } from 'vitest'; -import generateChangelogForSinglePackage from '../lib/tasks/generatechangelogforsinglepackage'; -import generateChangelogForMonoRepository from '../lib/tasks/generatechangelogformonorepository'; -import updateDependencies from '../lib/tasks/updatedependencies'; -import commitAndTag from '../lib/tasks/commitandtag'; -import createGithubRelease from '../lib/tasks/creategithubrelease'; -import reassignNpmTags from '../lib/tasks/reassignnpmtags'; -import prepareRepository from '../lib/tasks/preparerepository'; -import push from '../lib/tasks/push'; -import publishPackages from '../lib/tasks/publishpackages'; -import updateVersions from '../lib/tasks/updateversions'; -import cleanUpPackages from '../lib/tasks/cleanuppackages'; +import generateChangelogForSinglePackage from '../lib/tasks/generatechangelogforsinglepackage.js'; +import generateChangelogForMonoRepository from '../lib/tasks/generatechangelogformonorepository.js'; +import updateDependencies from '../lib/tasks/updatedependencies.js'; +import commitAndTag from '../lib/tasks/commitandtag.js'; +import createGithubRelease from '../lib/tasks/creategithubrelease.js'; +import reassignNpmTags from '../lib/tasks/reassignnpmtags.js'; +import prepareRepository from '../lib/tasks/preparerepository.js'; +import push from '../lib/tasks/push.js'; +import publishPackages from '../lib/tasks/publishpackages.js'; +import updateVersions from '../lib/tasks/updateversions.js'; +import cleanUpPackages from '../lib/tasks/cleanuppackages.js'; +import getChangesForVersion from '../lib/utils/getchangesforversion.js'; +import getChangelog from '../lib/utils/getchangelog.js'; +import saveChangelog from '../lib/utils/savechangelog.js'; import { getLastFromChangelog, getLastPreRelease, @@ -25,20 +26,15 @@ import { getNextNightly, getCurrent, getLastTagFromGit -} from '../lib/utils/versions'; -import { - getChangesForVersion, - getChangelog, - saveChangelog -} from '../lib/utils/changelog'; -import executeInParallel from '../lib/utils/executeinparallel'; -import validateRepositoryToRelease from '../lib/utils/validaterepositorytorelease'; -import checkVersionAvailability from '../lib/utils/checkversionavailability'; -import verifyPackagesPublishedCorrectly from '../lib/tasks/verifypackagespublishedcorrectly'; -import getNpmTagFromVersion from '../lib/utils/getnpmtagfromversion'; -import isVersionPublishableForTag from '../lib/utils/isversionpublishablefortag'; - -import * as index from '../lib/index'; +} from '../lib/utils/versions.js'; +import executeInParallel from '../lib/utils/executeinparallel.js'; +import validateRepositoryToRelease from '../lib/utils/validaterepositorytorelease.js'; +import checkVersionAvailability from '../lib/utils/checkversionavailability.js'; +import verifyPackagesPublishedCorrectly from '../lib/tasks/verifypackagespublishedcorrectly.js'; +import getNpmTagFromVersion from '../lib/utils/getnpmtagfromversion.js'; +import isVersionPublishableForTag from '../lib/utils/isversionpublishablefortag.js'; + +import * as index from '../lib/index.js'; vi.mock( '../lib/tasks/generatechangelogforsinglepackage' ); vi.mock( '../lib/tasks/generatechangelogformonorepository' ); diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js index c175293ea..2f1ac7bec 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/creategithubrelease.js @@ -5,7 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import createGithubRelease from '../../lib/tasks/creategithubrelease.js'; -import transformCommitUtils from '../../lib/utils/transformcommitutils.js'; +import * as transformCommitUtils from '../../lib/utils/transformcommitutils.js'; const stubs = vi.hoisted( () => ( { constructor: vi.fn(), @@ -26,7 +26,7 @@ vi.mock( '@octokit/rest', () => ( { } } ) ); -vi.mock( '../../lib/utils/transformcommitutils' ); +vi.mock( '../../lib/utils/transformcommitutils.js' ); describe( 'createGithubRelease()', () => { let options; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js b/packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js new file mode 100644 index 000000000..461d0bade --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js @@ -0,0 +1,441 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +// import * as changelogUtils from '../../lib/utils/changelog.js'; +import { getRepositoryUrl } from '../../lib/utils/transformcommitutils.js'; + +vi.mock( '../../lib/utils/transformcommitutils.js' ); + +describe( 'changelog', () => { + let changelogUtils; + + beforeEach( () => { + vi.mocked( getRepositoryUrl ).mockReturnValue( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); + + it( 'should define constants', async () => { + changelogUtils = await import( '../../lib/utils/changelog.js' ); + + expect( changelogUtils.CHANGELOG_FILE ).to.be.a( 'string' ); + expect( changelogUtils.CHANGELOG_HEADER ).to.be.a( 'string' ); + } ); + + describe( 'getChangesForVersion()', () => { + beforeEach( async () => { + vi.resetAllMocks(); + + vi.doMock( '../../lib/utils/changelog.js', async importOriginal => { + return { + ...await importOriginal(), + getChangelog: vi.fn() + }; + } ); + + changelogUtils = ( await import( '../../lib/utils/changelog.js' ) ); + + console.log( changelogUtils ); + } ); + + it( 'returns changes for the first tag which is a link to the release', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + const changelog = [ + '## [0.1.0](https://github.com) (2017-01-13)', + '', + expectedChangelog + ].join( '\n' ); + + console.log( vi.mocked( changelogUtils ).getChangelog ); + vi.mocked( changelogUtils ).getChangelog.mockReturnValue( changelogUtils.CHANGELOG_HEADER + changelog ); + + const parsedChangelog = changelogUtils.getChangesForVersion( 'v0.1.0' ); + + // expect( vi.mocked( changelogUtils ).getChangelog ).toHaveBeenCalledOnce(); + expect( parsedChangelog ).to.equal( expectedChangelog ); + } ); + + it( 'returns changes for the first tag which is not a link', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + const changelog = [ + '## 0.1.0 (2017-01-13)', + '', + expectedChangelog + ].join( '\n' ); + + const currentChangelogStub = sandbox.stub( utils, 'getChangelog' ) + .returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + const parsedChangelog = changelogUtils.getChangesForVersion( 'v0.1.0' ); + + expect( currentChangelogStub.calledOnce ).to.equal( true ); + expect( parsedChangelog ).to.equal( expectedChangelog ); + } ); + + it( 'returns changes between tags', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))', + '', + '### BREAKING CHANGE', + '', + '* Bump the major!' + ].join( '\n' ); + + const changelog = [ + '## [1.0.0](https://github.com/) (2017-01-13)', + '', + expectedChangelog, + '', + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + const currentChangelogStub = sandbox.stub( utils, 'getChangelog' ) + .returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + const parsedChangelog = changelogUtils.getChangesForVersion( 'v1.0.0' ); + + expect( currentChangelogStub.calledOnce ).to.equal( true ); + expect( parsedChangelog ).to.equal( expectedChangelog ); + } ); + + it( 'returns null if cannot find changes for the specified version', () => { + const changelog = [ + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + sandbox.stub( utils, 'getChangelog' ) + .returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + expect( changelogUtils.getChangesForVersion( 'v1.0.0' ) ).to.equal( null ); + } ); + + it( 'works when date is not specified', () => { + const changelog = [ + '## 0.3.0', + '', + 'Foo' + ].join( '\n' ); + + sandbox.stub( utils, 'getChangelog' ) + .returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + expect( changelogUtils.getChangesForVersion( 'v0.3.0' ) ) + .to.equal( 'Foo' ); + } ); + + it( 'captures correct range of changes (headers are URLs)', () => { + const changelog = [ + '## [0.3.0](https://github.com) (2017-01-13)', + '', + '3', + '', + 'Some text ## [like a release header]', + '', + '## [0.2.0](https://github.com) (2017-01-13)', + '', + '2', + '', + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '1' + ].join( '\n' ); + + sandbox.stub( utils, 'getChangelog' ) + .returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + expect( changelogUtils.getChangesForVersion( 'v0.3.0' ) ) + .to.equal( '3\n\nSome text ## [like a release header]' ); + + expect( changelogUtils.getChangesForVersion( 'v0.2.0' ) ) + .to.equal( '2' ); + } ); + + it( 'captures correct range of changes (headers are plain text, "the initial" release check)', () => { + const changelog = [ + 'Changelog', + '=========', + '', + '## 1.0.2 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.2`.', + '', + '', + '## 1.0.1 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.1`.', + '', + '', + '## 1.0.0 (2022-02-22)', + '', + 'This is the initial release.' + ].join( '\n' ); + + sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + expect( changelogUtils.getChangesForVersion( '1.0.0' ) ).to.equal( 'This is the initial release.' ); + } ); + + it( 'captures correct range of changes (headers are plain text, "middle" version check)', () => { + const changelog = [ + 'Changelog', + '=========', + '', + '## 1.0.2 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.2`.', + '', + '', + '## 1.0.1 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.1`.', + '', + '', + '## 1.0.0 (2022-02-22)', + '', + 'This is the initial release.' + ].join( '\n' ); + + sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + expect( changelogUtils.getChangesForVersion( '1.0.1' ) ).to.equal( [ + '### Other changes', + '', + '* Other change for `1.0.1`.' + ].join( '\n' ) ); + } ); + + it( 'captures correct range of changes (headers are plain text, "the latest" check)', () => { + const changelog = [ + 'Changelog', + '=========', + '', + '## 1.0.2 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.2`.', + '', + '', + '## 1.0.1 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.1`.', + '', + '', + '## 1.0.0 (2022-02-22)', + '', + 'This is the initial release.' + ].join( '\n' ); + + sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelog ); + + expect( changelogUtils.getChangesForVersion( '1.0.2' ) ).to.equal( [ + '### Other changes', + '', + '* Other change for `1.0.2`.' + ].join( '\n' ) ); + } ); + } ); + + describe( 'getChangelog()', () => { + it( 'resolves the changelog', () => { + const joinStub = sandbox.stub( path, 'join' ).returns( 'path-to-changelog' ); + const existsSyncStub = sandbox.stub( fs, 'existsSync' ).returns( true ); + const readFileStub = sandbox.stub( fs, 'readFileSync' ).returns( 'Content.' ); + const changelog = changelogUtils.getChangelog(); + + expect( joinStub.calledOnce ).to.equal( true ); + expect( existsSyncStub.calledOnce ).to.equal( true ); + expect( readFileStub.calledOnce ).to.equal( true ); + expect( readFileStub.firstCall.args[ 0 ] ).to.equal( 'path-to-changelog' ); + expect( readFileStub.firstCall.args[ 1 ] ).to.equal( 'utf-8' ); + expect( changelog ).to.equal( 'Content.' ); + } ); + + it( 'returns null if the changelog does not exist', () => { + const joinStub = sandbox.stub( path, 'join' ).returns( 'path-to-changelog' ); + const existsSyncStub = sandbox.stub( fs, 'existsSync' ).returns( false ); + const readFileStub = sandbox.stub( fs, 'readFileSync' ); + const changelog = changelogUtils.getChangelog(); + + expect( joinStub.calledOnce ).to.equal( true ); + expect( existsSyncStub.calledOnce ).to.equal( true ); + expect( readFileStub.called ).to.equal( false ); + expect( changelog ).to.equal( null ); + } ); + } ); + + describe( 'saveChangelog()', () => { + it( 'saves the changelog', () => { + const processCwdStub = sandbox.stub( process, 'cwd' ).returns( '/tmp' ); + const joinStub = sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); + const writeFileStub = sandbox.stub( fs, 'writeFileSync' ); + + changelogUtils.saveChangelog( 'New content.' ); + + expect( joinStub.calledOnce ).to.equal( true ); + expect( processCwdStub.calledOnce ).to.equal( true ); + expect( writeFileStub.calledOnce ).to.equal( true ); + expect( writeFileStub.firstCall.args[ 0 ] ).to.equal( '/tmp/CHANGELOG.md' ); + expect( writeFileStub.firstCall.args[ 1 ] ).to.equal( 'New content.' ); + } ); + + it( 'allows changing cwd', () => { + const joinStub = sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); + const writeFileStub = sandbox.stub( fs, 'writeFileSync' ); + + changelogUtils.saveChangelog( 'New content.', '/new-cwd' ); + + expect( joinStub.calledOnce ).to.equal( true ); + expect( writeFileStub.calledOnce ).to.equal( true ); + expect( writeFileStub.firstCall.args[ 0 ] ).to.equal( '/new-cwd/CHANGELOG.md' ); + expect( writeFileStub.firstCall.args[ 1 ] ).to.equal( 'New content.' ); + } ); + } ); + + describe( 'truncateChangelog()', () => { + it( 'does nothing if there is no changelog', () => { + const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); + + sandbox.stub( utils, 'getChangelog' ).returns( null ); + + changelogUtils.truncateChangelog( 5 ); + + expect( saveChangelogStub.called ).to.equal( false ); + } ); + + it( 'does nothing if changelog does not contain entries', () => { + const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); + + sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + '\n\n' ); + + changelogUtils.truncateChangelog( 5 ); + + expect( saveChangelogStub.called ).to.equal( false ); + } ); + + it( 'truncates the changelog and adds the link to the release page', () => { + const expectedChangelogEntries = [ + '## [0.3.0](https://github.com) (2017-01-13)', + '', + '3', + '', + 'Some text ## [like a release header]', + '', + '## [0.2.0](https://github.com) (2017-01-13)', + '', + '2' + ].join( '\n' ); + + const expectedChangelogFooter = [ + '', + '', + '---', + '', + 'To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases).', + '' + ].join( '\n' ); + + const changelogEntries = [ + expectedChangelogEntries, + '', + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '1' + ].join( '\n' ); + + const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); + + sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelogEntries ); + + changelogUtils.truncateChangelog( 2 ); + + expect( saveChangelogStub.calledOnce ).to.equal( true ); + expect( saveChangelogStub.firstCall.args[ 0 ] ).to.equal( + changelogUtils.CHANGELOG_HEADER + + expectedChangelogEntries + + expectedChangelogFooter + ); + } ); + + it( 'does not add the link to the release page if changelog is not truncated', () => { + const expectedChangelogEntries = [ + '## [0.3.0](https://github.com) (2017-01-13)', + '', + '3', + '', + 'Some text ## [like a release header]', + '', + '## [0.2.0](https://github.com) (2017-01-13)', + '', + '2' + ].join( '\n' ); + + const expectedChangelogFooter = '\n'; + + const changelogEntries = expectedChangelogEntries; + + const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); + + sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelogEntries ); + + changelogUtils.truncateChangelog( 2 ); + + expect( saveChangelogStub.calledOnce ).to.equal( true ); + expect( saveChangelogStub.firstCall.args[ 0 ] ).to.equal( + changelogUtils.CHANGELOG_HEADER + + expectedChangelogEntries + + expectedChangelogFooter + ); + } ); + } ); + + describe( 'getFormattedDate()', () => { + let clock; + + beforeEach( () => { + clock = sinon.useFakeTimers( { + now: new Date( '2023-06-15 12:00:00' ) + } ); + } ); + + afterEach( () => { + clock.restore(); + } ); + + it( 'returns a date following the format "year-month-day" with the leading zeros', () => { + expect( changelogUtils.getFormattedDate() ).to.equal( '2023-06-15' ); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/changelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/changelog.js deleted file mode 100644 index 2a5423207..000000000 --- a/packages/ckeditor5-dev-release-tools/tests/utils/changelog.js +++ /dev/null @@ -1,437 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); - -describe( 'dev-release-tools/utils', () => { - let utils, sandbox; - - describe( 'changelog', () => { - beforeEach( () => { - sandbox = sinon.createSandbox(); - - sandbox.stub( - require( '../../lib/utils/transformcommitutils' ), - 'getRepositoryUrl' - ).returns( 'https://github.com/ckeditor/ckeditor5-dev' ); - - utils = require( '../../lib/utils/changelog' ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - - it( 'should define constants', () => { - expect( utils.changelogFile ).to.be.a( 'string' ); - expect( utils.changelogHeader ).to.be.a( 'string' ); - } ); - - describe( 'getChangesForVersion()', () => { - it( 'returns changes for the first tag which is a link to the release', () => { - const expectedChangelog = [ - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - const changelog = [ - '## [0.1.0](https://github.com) (2017-01-13)', - '', - expectedChangelog - ].join( '\n' ); - - const currentChangelogStub = sandbox.stub( utils, 'getChangelog' ) - .returns( utils.changelogHeader + changelog ); - - const parsedChangelog = utils.getChangesForVersion( 'v0.1.0' ); - - expect( currentChangelogStub.calledOnce ).to.equal( true ); - expect( parsedChangelog ).to.equal( expectedChangelog ); - } ); - - it( 'returns changes for the first tag which is not a link', () => { - const expectedChangelog = [ - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - const changelog = [ - '## 0.1.0 (2017-01-13)', - '', - expectedChangelog - ].join( '\n' ); - - const currentChangelogStub = sandbox.stub( utils, 'getChangelog' ) - .returns( utils.changelogHeader + changelog ); - - const parsedChangelog = utils.getChangesForVersion( 'v0.1.0' ); - - expect( currentChangelogStub.calledOnce ).to.equal( true ); - expect( parsedChangelog ).to.equal( expectedChangelog ); - } ); - - it( 'returns changes between tags', () => { - const expectedChangelog = [ - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))', - '', - '### BREAKING CHANGE', - '', - '* Bump the major!' - ].join( '\n' ); - - const changelog = [ - '## [1.0.0](https://github.com/) (2017-01-13)', - '', - expectedChangelog, - '', - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - const currentChangelogStub = sandbox.stub( utils, 'getChangelog' ) - .returns( utils.changelogHeader + changelog ); - - const parsedChangelog = utils.getChangesForVersion( 'v1.0.0' ); - - expect( currentChangelogStub.calledOnce ).to.equal( true ); - expect( parsedChangelog ).to.equal( expectedChangelog ); - } ); - - it( 'returns null if cannot find changes for the specified version', () => { - const changelog = [ - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ) - .returns( utils.changelogHeader + changelog ); - - expect( utils.getChangesForVersion( 'v1.0.0' ) ).to.equal( null ); - } ); - - it( 'works when date is not specified', () => { - const changelog = [ - '## 0.3.0', - '', - 'Foo' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ) - .returns( utils.changelogHeader + changelog ); - - expect( utils.getChangesForVersion( 'v0.3.0' ) ) - .to.equal( 'Foo' ); - } ); - - it( 'captures correct range of changes (headers are URLs)', () => { - const changelog = [ - '## [0.3.0](https://github.com) (2017-01-13)', - '', - '3', - '', - 'Some text ## [like a release header]', - '', - '## [0.2.0](https://github.com) (2017-01-13)', - '', - '2', - '', - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '1' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ) - .returns( utils.changelogHeader + changelog ); - - expect( utils.getChangesForVersion( 'v0.3.0' ) ) - .to.equal( '3\n\nSome text ## [like a release header]' ); - - expect( utils.getChangesForVersion( 'v0.2.0' ) ) - .to.equal( '2' ); - } ); - - it( 'captures correct range of changes (headers are plain text, "the initial" release check)', () => { - const changelog = [ - 'Changelog', - '=========', - '', - '## 1.0.2 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.2`.', - '', - '', - '## 1.0.1 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.1`.', - '', - '', - '## 1.0.0 (2022-02-22)', - '', - 'This is the initial release.' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ).returns( utils.changelogHeader + changelog ); - - expect( utils.getChangesForVersion( '1.0.0' ) ).to.equal( 'This is the initial release.' ); - } ); - - it( 'captures correct range of changes (headers are plain text, "middle" version check)', () => { - const changelog = [ - 'Changelog', - '=========', - '', - '## 1.0.2 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.2`.', - '', - '', - '## 1.0.1 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.1`.', - '', - '', - '## 1.0.0 (2022-02-22)', - '', - 'This is the initial release.' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ).returns( utils.changelogHeader + changelog ); - - expect( utils.getChangesForVersion( '1.0.1' ) ).to.equal( [ - '### Other changes', - '', - '* Other change for `1.0.1`.' - ].join( '\n' ) ); - } ); - - it( 'captures correct range of changes (headers are plain text, "the latest" check)', () => { - const changelog = [ - 'Changelog', - '=========', - '', - '## 1.0.2 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.2`.', - '', - '', - '## 1.0.1 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.1`.', - '', - '', - '## 1.0.0 (2022-02-22)', - '', - 'This is the initial release.' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ).returns( utils.changelogHeader + changelog ); - - expect( utils.getChangesForVersion( '1.0.2' ) ).to.equal( [ - '### Other changes', - '', - '* Other change for `1.0.2`.' - ].join( '\n' ) ); - } ); - } ); - - describe( 'getChangelog()', () => { - it( 'resolves the changelog', () => { - const joinStub = sandbox.stub( path, 'join' ).returns( 'path-to-changelog' ); - const existsSyncStub = sandbox.stub( fs, 'existsSync' ).returns( true ); - const readFileStub = sandbox.stub( fs, 'readFileSync' ).returns( 'Content.' ); - const changelog = utils.getChangelog(); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( existsSyncStub.calledOnce ).to.equal( true ); - expect( readFileStub.calledOnce ).to.equal( true ); - expect( readFileStub.firstCall.args[ 0 ] ).to.equal( 'path-to-changelog' ); - expect( readFileStub.firstCall.args[ 1 ] ).to.equal( 'utf-8' ); - expect( changelog ).to.equal( 'Content.' ); - } ); - - it( 'returns null if the changelog does not exist', () => { - const joinStub = sandbox.stub( path, 'join' ).returns( 'path-to-changelog' ); - const existsSyncStub = sandbox.stub( fs, 'existsSync' ).returns( false ); - const readFileStub = sandbox.stub( fs, 'readFileSync' ); - const changelog = utils.getChangelog(); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( existsSyncStub.calledOnce ).to.equal( true ); - expect( readFileStub.called ).to.equal( false ); - expect( changelog ).to.equal( null ); - } ); - } ); - - describe( 'saveChangelog()', () => { - it( 'saves the changelog', () => { - const processCwdStub = sandbox.stub( process, 'cwd' ).returns( '/tmp' ); - const joinStub = sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); - const writeFileStub = sandbox.stub( fs, 'writeFileSync' ); - - utils.saveChangelog( 'New content.' ); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( processCwdStub.calledOnce ).to.equal( true ); - expect( writeFileStub.calledOnce ).to.equal( true ); - expect( writeFileStub.firstCall.args[ 0 ] ).to.equal( '/tmp/CHANGELOG.md' ); - expect( writeFileStub.firstCall.args[ 1 ] ).to.equal( 'New content.' ); - } ); - - it( 'allows changing cwd', () => { - const joinStub = sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); - const writeFileStub = sandbox.stub( fs, 'writeFileSync' ); - - utils.saveChangelog( 'New content.', '/new-cwd' ); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( writeFileStub.calledOnce ).to.equal( true ); - expect( writeFileStub.firstCall.args[ 0 ] ).to.equal( '/new-cwd/CHANGELOG.md' ); - expect( writeFileStub.firstCall.args[ 1 ] ).to.equal( 'New content.' ); - } ); - } ); - - describe( 'truncateChangelog()', () => { - it( 'does nothing if there is no changelog', () => { - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( null ); - - utils.truncateChangelog( 5 ); - - expect( saveChangelogStub.called ).to.equal( false ); - } ); - - it( 'does nothing if changelog does not contain entries', () => { - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( utils.changelogHeader + '\n\n' ); - - utils.truncateChangelog( 5 ); - - expect( saveChangelogStub.called ).to.equal( false ); - } ); - - it( 'truncates the changelog and adds the link to the release page', () => { - const expectedChangelogEntries = [ - '## [0.3.0](https://github.com) (2017-01-13)', - '', - '3', - '', - 'Some text ## [like a release header]', - '', - '## [0.2.0](https://github.com) (2017-01-13)', - '', - '2' - ].join( '\n' ); - - const expectedChangelogFooter = [ - '', - '', - '---', - '', - 'To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases).', - '' - ].join( '\n' ); - - const changelogEntries = [ - expectedChangelogEntries, - '', - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '1' - ].join( '\n' ); - - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( utils.changelogHeader + changelogEntries ); - - utils.truncateChangelog( 2 ); - - expect( saveChangelogStub.calledOnce ).to.equal( true ); - expect( saveChangelogStub.firstCall.args[ 0 ] ).to.equal( - utils.changelogHeader + - expectedChangelogEntries + - expectedChangelogFooter - ); - } ); - - it( 'does not add the link to the release page if changelog is not truncated', () => { - const expectedChangelogEntries = [ - '## [0.3.0](https://github.com) (2017-01-13)', - '', - '3', - '', - 'Some text ## [like a release header]', - '', - '## [0.2.0](https://github.com) (2017-01-13)', - '', - '2' - ].join( '\n' ); - - const expectedChangelogFooter = '\n'; - - const changelogEntries = expectedChangelogEntries; - - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( utils.changelogHeader + changelogEntries ); - - utils.truncateChangelog( 2 ); - - expect( saveChangelogStub.calledOnce ).to.equal( true ); - expect( saveChangelogStub.firstCall.args[ 0 ] ).to.equal( - utils.changelogHeader + - expectedChangelogEntries + - expectedChangelogFooter - ); - } ); - } ); - - describe( 'getFormattedDate()', () => { - let clock; - - beforeEach( () => { - clock = sinon.useFakeTimers( { - now: new Date( '2023-06-15 12:00:00' ) - } ); - } ); - - afterEach( () => { - clock.restore(); - } ); - - it( 'returns a date following the format "year-month-day" with the leading zeros', () => { - expect( utils.getFormattedDate() ).to.equal( '2023-06-15' ); - } ); - } ); - } ); -} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js index 69ec1d53a..eb15f71c5 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js @@ -5,7 +5,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import { getChangelog } from '../../lib/utils/changelog.js'; +import getChangelog from '../../lib/utils/getchangelog.js'; import { getPackageJson } from '../../lib/utils/getpackagejson.js'; import { @@ -19,7 +19,7 @@ import { } from '../../lib/utils/versions.js'; vi.mock( '@ckeditor/ckeditor5-dev-utils' ); -vi.mock( '../../lib/utils/changelog.js' ); +vi.mock( '../../lib/utils/getchangelog.js' ); vi.mock( '../../lib/utils/getpackagejson.js' ); describe( 'dev-release-tools/utils', () => { From faf553e0e7f14642feac26b287f627c3fff69d7b Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 08:00:45 +0200 Subject: [PATCH 054/172] Tests for checkVersionAvailability. --- .../tests/utils/checkversionavailability.js | 150 +++++++----------- 1 file changed, 56 insertions(+), 94 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js b/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js index c0ea6d222..4473d4684 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js @@ -3,99 +3,61 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); - -describe( 'dev-release-tools/utils', () => { - let checkVersionAvailability, sandbox, stubs; - - describe( 'checkVersionAvailability()', () => { - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - shExec: sandbox.stub(), - shellEscape: sinon.stub().callsFake( v => v[ 0 ] ) - }; - - checkVersionAvailability = proxyquire( '../../lib/utils/checkversionavailability.js', { - '@ckeditor/ckeditor5-dev-utils': { - tools: { - shExec: stubs.shExec - } - }, - 'shell-escape': stubs.shellEscape - } ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - - it( 'should resolve to true if version does not exist (npm >= 8.13.0 && npm < 10.0.0)', () => { - stubs.shExec.rejects( new Error( 'npm ERR! code E404' ) ); - - return checkVersionAvailability( '1.0.1', 'stub-package' ) - .then( result => { - expect( stubs.shExec.callCount ).to.equal( 1 ); - expect( stubs.shExec.firstCall.args[ 0 ] ).to.equal( 'npm show stub-package@1.0.1 version' ); - expect( result ).to.be.true; - } ); - } ); - - it( 'should resolve to true if version does not exist (npm >= 10.0.0)', () => { - stubs.shExec.rejects( new Error( 'npm error code E404' ) ); - - return checkVersionAvailability( '1.0.1', 'stub-package' ) - .then( result => { - expect( stubs.shExec.callCount ).to.equal( 1 ); - expect( stubs.shExec.firstCall.args[ 0 ] ).to.equal( 'npm show stub-package@1.0.1 version' ); - expect( result ).to.be.true; - } ); - } ); - - it( 'should resolve to true if version does not exist (npm < 8.13.0)', () => { - stubs.shExec.resolves(); - - return checkVersionAvailability( '1.0.1', 'stub-package' ) - .then( result => { - expect( result ).to.be.true; - } ); - } ); - - it( 'should resolve to false if version exists', () => { - stubs.shExec.resolves( '1.0.1' ); - - return checkVersionAvailability( '1.0.1', 'stub-package' ) - .then( result => { - expect( result ).to.be.false; - } ); - } ); - - it( 'should re-throw an error if unknown error occured', () => { - stubs.shExec.rejects( new Error( 'Unknown error.' ) ); - - return checkVersionAvailability( '1.0.1', 'stub-package' ) - .then( () => { - throw new Error( 'Expected to be rejected.' ); - } ) - .catch( error => { - expect( error.message ).to.equal( 'Unknown error.' ); - } ); - } ); - - it( 'should escape arguments passed to a shell command', async () => { - stubs.shExec.rejects( new Error( 'npm ERR! code E404' ) ); - - return checkVersionAvailability( '1.0.1', 'stub-package' ) - .then( () => { - expect( stubs.shellEscape.callCount ).to.equal( 2 ); - expect( stubs.shellEscape.firstCall.firstArg ).to.deep.equal( [ 'stub-package' ] ); - expect( stubs.shellEscape.secondCall.firstArg ).to.deep.equal( [ '1.0.1' ] ); - } ); - } ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import shellEscape from 'shell-escape'; +import checkVersionAvailability from '../../lib/utils/checkversionavailability.js'; + +vi.mock( 'shell-escape' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); + +describe( 'checkVersionAvailability()', () => { + beforeEach( () => { + vi.mocked( shellEscape ).mockImplementation( v => v[ 0 ] ); + } ); + + it( 'should resolve to true if version does not exist (npm >= 8.13.0 && npm < 10.0.0)', async () => { + vi.mocked( tools ).shExec.mockRejectedValue( new Error( 'npm ERR! code E404' ) ); + + const result = await checkVersionAvailability( '1.0.1', 'stub-package' ); + + expect( vi.mocked( tools ).shExec ).toHaveBeenCalledExactlyOnceWith( 'npm show stub-package@1.0.1 version', expect.any( Object ) ); + expect( result ).toBe( true ); + } ); + + it( 'should resolve to true if version does not exist (npm >= 10.0.0)', async () => { + vi.mocked( tools ).shExec.mockRejectedValue( new Error( 'npm error code E404' ) ); + + const result = await checkVersionAvailability( '1.0.1', 'stub-package' ); + expect( vi.mocked( tools ).shExec ).toHaveBeenCalledExactlyOnceWith( 'npm show stub-package@1.0.1 version', expect.any( Object ) ); + expect( result ).toBe( true ); + } ); + + it( 'should resolve to true if version does not exist (npm < 8.13.0)', async () => { + vi.mocked( tools ).shExec.mockResolvedValue( '' ); + + await expect( checkVersionAvailability( '1.0.1', 'stub-package' ) ).resolves.toBe( true ); + } ); + + it( 'should resolve to false if version exists', async () => { + vi.mocked( tools ).shExec.mockResolvedValue( '1.0.1' ); + + await expect( checkVersionAvailability( '1.0.1', 'stub-package' ) ).resolves.toBe( false ); + } ); + + it( 'should re-throw an error if unknown error occured', async () => { + vi.mocked( tools ).shExec.mockRejectedValue( new Error( 'Unknown error.' ) ); + + await expect( checkVersionAvailability( '1.0.1', 'stub-package' ) ) + .rejects.toThrow( 'Unknown error.' ); + } ); + + it( 'should escape arguments passed to a shell command', async () => { + vi.mocked( tools ).shExec.mockRejectedValue( new Error( 'npm ERR! code E404' ) ); + + await checkVersionAvailability( '1.0.1', 'stub-package' ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledWith( [ 'stub-package' ] ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledWith( [ '1.0.1' ] ); } ); } ); From d07eeb21b3071dd876e70f064ab4d67d89b9d29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Tue, 10 Sep 2024 08:09:48 +0200 Subject: [PATCH 055/172] Aligned tests in `utils/generatechangelog.js`. --- .../lib/utils/getwriteroptions.js | 4 +- .../tests/utils/generatechangelog.js | 138 +++++++++--------- 2 files changed, 70 insertions(+), 72 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js index e2c02ba9e..d94a5b744 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js @@ -5,7 +5,7 @@ import fs from 'fs'; import path from 'path'; -import utils from './transformcommitutils.js'; +import { getTypeOrder } from './transformcommitutils.js'; const templatePath = path.join( __dirname, '..', 'templates' ); @@ -28,5 +28,5 @@ export function getWriterOptions( transform ) { } function sortFunction( a, b ) { - return utils.getTypeOrder( a.title ) - utils.getTypeOrder( b.title ); + return getTypeOrder( a.title ) - getTypeOrder( b.title ); } diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js index 722b40ccd..34c4928b9 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const compareFunc = require( 'compare-func' ); -const getWriterOptions = require( '../../lib/utils/getwriteroptions' ); -const generateChangelog = require( '../../lib/utils/generatechangelog' ); +import { describe, it, expect } from 'vitest'; +import compareFunc from 'compare-func'; +import { getWriterOptions } from '../../lib/utils/getwriteroptions.js'; +import { generateChangelog } from '../../lib/utils/generatechangelog.js'; describe( 'dev-release-tools/utils', () => { const url = 'https://github.com/ckeditor/ckeditor5-package'; @@ -56,16 +54,16 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Features' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* The first an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' ); - expect( changesAsArray[ 3 ] ).to.equal( + expect( changesAsArray[ 3 ] ).toEqual( '* The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' ); } ); @@ -108,16 +106,16 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Bug fixes' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* The first an amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' ); - expect( changesAsArray[ 3 ] ).to.equal( + expect( changesAsArray[ 3 ] ).toEqual( '* The second an amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' ); } ); @@ -160,16 +158,16 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Other changes' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* The first an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' ); - expect( changesAsArray[ 3 ] ).to.equal( + expect( changesAsArray[ 3 ] ).toEqual( '* The second an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' ); } ); @@ -219,25 +217,25 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Features' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* An amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' ); - expect( changesAsArray[ 3 ] ).to.equal( + expect( changesAsArray[ 3 ] ).toEqual( '### Bug fixes' ); - expect( changesAsArray[ 4 ] ).to.equal( + expect( changesAsArray[ 4 ] ).toEqual( '* An amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' ); - expect( changesAsArray[ 5 ] ).to.equal( + expect( changesAsArray[ 5 ] ).toEqual( '### Other changes' ); - expect( changesAsArray[ 6 ] ).to.equal( + expect( changesAsArray[ 6 ] ).toEqual( '* An amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/yyyyyyy))' ); } ); @@ -280,16 +278,16 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Features' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* The first an amazing feature.' ); - expect( changesAsArray[ 3 ] ).to.equal( + expect( changesAsArray[ 3 ] ).toEqual( '* The second an amazing feature.' ); } ); @@ -316,7 +314,7 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## 1.0.0 (0000-00-00)' ); } ); @@ -388,7 +386,7 @@ describe( 'dev-release-tools/utils', () => { '* The third an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/yyyyyyy))' ].join( '\n' ); - expect( changes.trim() ).to.equal( changelog ); + expect( changes.trim() ).toEqual( changelog ); } ); } ); @@ -443,14 +441,14 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Other changes' ); /* eslint-disable max-len */ - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* Updated translations. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa), [commit](https://github.com/ckeditor/ckeditor5-package/c/bb), [commit](https://github.com/ckeditor/ckeditor5-package/c/cc), [commit](https://github.com/ckeditor/ckeditor5-package/c/dd))' ); /* eslint-enable max-len */ @@ -508,13 +506,13 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Other changes' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* Updated translations.' ); } ); @@ -591,14 +589,14 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Other changes' ); /* eslint-disable max-len */ - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* Updated translations. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa), [commit](https://github.com/ckeditor/ckeditor5-package/c/bb), [commit](https://github.com/ckeditor/ckeditor5-package/c/cc), [commit](https://github.com/ckeditor/ckeditor5-package/c/dd))' ); /* eslint-enable max-len */ @@ -676,14 +674,14 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Other changes' ); /* eslint-disable max-len */ - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* Updated translations. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa), [commit](https://github.com/ckeditor/ckeditor5-package/c/bb), [commit](https://github.com/ckeditor/ckeditor5-package/c/cc), [commit](https://github.com/ckeditor/ckeditor5-package/c/dd))' ); /* eslint-enable max-len */ @@ -749,14 +747,14 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Other changes' ); /* eslint-disable max-len */ - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* Updated translations.' ); /* eslint-enable max-len */ @@ -809,19 +807,19 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### Features' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* (a) The first an amazing feature.' ); - expect( changesAsArray[ 3 ] ).to.equal( + expect( changesAsArray[ 3 ] ).toEqual( '* (b) The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' ); - expect( changesAsArray[ 4 ] ).to.equal( + expect( changesAsArray[ 4 ] ).toEqual( '* (c) The last one an amazing feature.' ); } ); @@ -861,10 +859,10 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( 'Internal changes only (updated dependencies, documentation, etc.).' ); } ); @@ -891,10 +889,10 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( 'Internal changes only (updated dependencies, documentation, etc.).' ); } ); @@ -996,46 +994,46 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' ); - expect( changesAsArray[ 1 ] ).to.equal( + expect( changesAsArray[ 1 ] ).toEqual( '### MAJOR BREAKING CHANGES' ); - expect( changesAsArray[ 2 ] ).to.equal( + expect( changesAsArray[ 2 ] ).toEqual( '* This change should be scoped too but the script should work if the scope is being missed.' ); - expect( changesAsArray[ 3 ] ).to.equal( + expect( changesAsArray[ 3 ] ).toEqual( '### MINOR BREAKING CHANGES' ); - expect( changesAsArray[ 4 ] ).to.equal( + expect( changesAsArray[ 4 ] ).toEqual( '* **engine**: Nothing but I would like to use the note - engine.' ); - expect( changesAsArray[ 5 ] ).to.equal( + expect( changesAsArray[ 5 ] ).toEqual( '* **ui**: Nothing but I would like to use the note - ui.' ); - expect( changesAsArray[ 6 ] ).to.equal( + expect( changesAsArray[ 6 ] ).toEqual( '### Features' ); - expect( changesAsArray[ 7 ] ).to.equal( + expect( changesAsArray[ 7 ] ).toEqual( '* **autoformat**: It just works. ([commit](https://github.com/ckeditor/ckeditor5-package/c/bb))' ); - expect( changesAsArray[ 8 ] ).to.equal( + expect( changesAsArray[ 8 ] ).toEqual( '* **engine**: The first an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/c/xx))' ); - expect( changesAsArray[ 9 ] ).to.equal( + expect( changesAsArray[ 9 ] ).toEqual( '* The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/c/zz))' ); - expect( changesAsArray[ 10 ] ).to.equal( + expect( changesAsArray[ 10 ] ).toEqual( '### Bug fixes' ); - expect( changesAsArray[ 11 ] ).to.equal( + expect( changesAsArray[ 11 ] ).toEqual( '* **ui**: The first amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/c/yy))' ); - expect( changesAsArray[ 12 ] ).to.equal( + expect( changesAsArray[ 12 ] ).toEqual( '### Other changes' ); - expect( changesAsArray[ 13 ] ).to.equal( + expect( changesAsArray[ 13 ] ).toEqual( '* Use the newest version of Node.js on CI. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa))' ); } ); @@ -1062,7 +1060,7 @@ describe( 'dev-release-tools/utils', () => { .map( line => line.trim() ) .filter( line => line.length ); - expect( changesAsArray[ 0 ] ).to.equal( + expect( changesAsArray[ 0 ] ).toEqual( '## 1.1.0 (0000-00-00)' ); } ); From 74880b09b4749d9eeaaf2e25d04af4a0de312918 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 08:53:28 +0200 Subject: [PATCH 056/172] More tests. --- .circleci/config.yml | 2 + .../changelog--extract-to-seperate-files.js | 441 ------------------ .../tests/utils/constants.js | 22 + .../tests/utils/getchangelog.js | 49 ++ .../tests/utils/getchangesforversion.js | 263 +++++++++++ .../tests/utils/getformatteddate.js | 22 + 6 files changed, 358 insertions(+), 441 deletions(-) delete mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/constants.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/getchangelog.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/getchangesforversion.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/getformatteddate.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 217678d71..502462aa1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,6 +104,8 @@ jobs: name: Execute ESLint command: yarn run lint - run: + environment: + TZ: Europe/Warsaw name: Run unit tests command: yarn run coverage - unless: diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js b/packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js deleted file mode 100644 index 461d0bade..000000000 --- a/packages/ckeditor5-dev-release-tools/tests/utils/changelog--extract-to-seperate-files.js +++ /dev/null @@ -1,441 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; -import fs from 'fs-extra'; -// import * as changelogUtils from '../../lib/utils/changelog.js'; -import { getRepositoryUrl } from '../../lib/utils/transformcommitutils.js'; - -vi.mock( '../../lib/utils/transformcommitutils.js' ); - -describe( 'changelog', () => { - let changelogUtils; - - beforeEach( () => { - vi.mocked( getRepositoryUrl ).mockReturnValue( 'https://github.com/ckeditor/ckeditor5-dev' ); - } ); - - it( 'should define constants', async () => { - changelogUtils = await import( '../../lib/utils/changelog.js' ); - - expect( changelogUtils.CHANGELOG_FILE ).to.be.a( 'string' ); - expect( changelogUtils.CHANGELOG_HEADER ).to.be.a( 'string' ); - } ); - - describe( 'getChangesForVersion()', () => { - beforeEach( async () => { - vi.resetAllMocks(); - - vi.doMock( '../../lib/utils/changelog.js', async importOriginal => { - return { - ...await importOriginal(), - getChangelog: vi.fn() - }; - } ); - - changelogUtils = ( await import( '../../lib/utils/changelog.js' ) ); - - console.log( changelogUtils ); - } ); - - it( 'returns changes for the first tag which is a link to the release', () => { - const expectedChangelog = [ - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - const changelog = [ - '## [0.1.0](https://github.com) (2017-01-13)', - '', - expectedChangelog - ].join( '\n' ); - - console.log( vi.mocked( changelogUtils ).getChangelog ); - vi.mocked( changelogUtils ).getChangelog.mockReturnValue( changelogUtils.CHANGELOG_HEADER + changelog ); - - const parsedChangelog = changelogUtils.getChangesForVersion( 'v0.1.0' ); - - // expect( vi.mocked( changelogUtils ).getChangelog ).toHaveBeenCalledOnce(); - expect( parsedChangelog ).to.equal( expectedChangelog ); - } ); - - it( 'returns changes for the first tag which is not a link', () => { - const expectedChangelog = [ - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - const changelog = [ - '## 0.1.0 (2017-01-13)', - '', - expectedChangelog - ].join( '\n' ); - - const currentChangelogStub = sandbox.stub( utils, 'getChangelog' ) - .returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - const parsedChangelog = changelogUtils.getChangesForVersion( 'v0.1.0' ); - - expect( currentChangelogStub.calledOnce ).to.equal( true ); - expect( parsedChangelog ).to.equal( expectedChangelog ); - } ); - - it( 'returns changes between tags', () => { - const expectedChangelog = [ - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))', - '', - '### BREAKING CHANGE', - '', - '* Bump the major!' - ].join( '\n' ); - - const changelog = [ - '## [1.0.0](https://github.com/) (2017-01-13)', - '', - expectedChangelog, - '', - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - const currentChangelogStub = sandbox.stub( utils, 'getChangelog' ) - .returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - const parsedChangelog = changelogUtils.getChangesForVersion( 'v1.0.0' ); - - expect( currentChangelogStub.calledOnce ).to.equal( true ); - expect( parsedChangelog ).to.equal( expectedChangelog ); - } ); - - it( 'returns null if cannot find changes for the specified version', () => { - const changelog = [ - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '### Features', - '', - '* Cloned the main module. ([abcd123](https://github.com))' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ) - .returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - expect( changelogUtils.getChangesForVersion( 'v1.0.0' ) ).to.equal( null ); - } ); - - it( 'works when date is not specified', () => { - const changelog = [ - '## 0.3.0', - '', - 'Foo' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ) - .returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - expect( changelogUtils.getChangesForVersion( 'v0.3.0' ) ) - .to.equal( 'Foo' ); - } ); - - it( 'captures correct range of changes (headers are URLs)', () => { - const changelog = [ - '## [0.3.0](https://github.com) (2017-01-13)', - '', - '3', - '', - 'Some text ## [like a release header]', - '', - '## [0.2.0](https://github.com) (2017-01-13)', - '', - '2', - '', - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '1' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ) - .returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - expect( changelogUtils.getChangesForVersion( 'v0.3.0' ) ) - .to.equal( '3\n\nSome text ## [like a release header]' ); - - expect( changelogUtils.getChangesForVersion( 'v0.2.0' ) ) - .to.equal( '2' ); - } ); - - it( 'captures correct range of changes (headers are plain text, "the initial" release check)', () => { - const changelog = [ - 'Changelog', - '=========', - '', - '## 1.0.2 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.2`.', - '', - '', - '## 1.0.1 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.1`.', - '', - '', - '## 1.0.0 (2022-02-22)', - '', - 'This is the initial release.' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - expect( changelogUtils.getChangesForVersion( '1.0.0' ) ).to.equal( 'This is the initial release.' ); - } ); - - it( 'captures correct range of changes (headers are plain text, "middle" version check)', () => { - const changelog = [ - 'Changelog', - '=========', - '', - '## 1.0.2 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.2`.', - '', - '', - '## 1.0.1 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.1`.', - '', - '', - '## 1.0.0 (2022-02-22)', - '', - 'This is the initial release.' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - expect( changelogUtils.getChangesForVersion( '1.0.1' ) ).to.equal( [ - '### Other changes', - '', - '* Other change for `1.0.1`.' - ].join( '\n' ) ); - } ); - - it( 'captures correct range of changes (headers are plain text, "the latest" check)', () => { - const changelog = [ - 'Changelog', - '=========', - '', - '## 1.0.2 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.2`.', - '', - '', - '## 1.0.1 (2022-02-22)', - '', - '### Other changes', - '', - '* Other change for `1.0.1`.', - '', - '', - '## 1.0.0 (2022-02-22)', - '', - 'This is the initial release.' - ].join( '\n' ); - - sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelog ); - - expect( changelogUtils.getChangesForVersion( '1.0.2' ) ).to.equal( [ - '### Other changes', - '', - '* Other change for `1.0.2`.' - ].join( '\n' ) ); - } ); - } ); - - describe( 'getChangelog()', () => { - it( 'resolves the changelog', () => { - const joinStub = sandbox.stub( path, 'join' ).returns( 'path-to-changelog' ); - const existsSyncStub = sandbox.stub( fs, 'existsSync' ).returns( true ); - const readFileStub = sandbox.stub( fs, 'readFileSync' ).returns( 'Content.' ); - const changelog = changelogUtils.getChangelog(); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( existsSyncStub.calledOnce ).to.equal( true ); - expect( readFileStub.calledOnce ).to.equal( true ); - expect( readFileStub.firstCall.args[ 0 ] ).to.equal( 'path-to-changelog' ); - expect( readFileStub.firstCall.args[ 1 ] ).to.equal( 'utf-8' ); - expect( changelog ).to.equal( 'Content.' ); - } ); - - it( 'returns null if the changelog does not exist', () => { - const joinStub = sandbox.stub( path, 'join' ).returns( 'path-to-changelog' ); - const existsSyncStub = sandbox.stub( fs, 'existsSync' ).returns( false ); - const readFileStub = sandbox.stub( fs, 'readFileSync' ); - const changelog = changelogUtils.getChangelog(); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( existsSyncStub.calledOnce ).to.equal( true ); - expect( readFileStub.called ).to.equal( false ); - expect( changelog ).to.equal( null ); - } ); - } ); - - describe( 'saveChangelog()', () => { - it( 'saves the changelog', () => { - const processCwdStub = sandbox.stub( process, 'cwd' ).returns( '/tmp' ); - const joinStub = sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); - const writeFileStub = sandbox.stub( fs, 'writeFileSync' ); - - changelogUtils.saveChangelog( 'New content.' ); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( processCwdStub.calledOnce ).to.equal( true ); - expect( writeFileStub.calledOnce ).to.equal( true ); - expect( writeFileStub.firstCall.args[ 0 ] ).to.equal( '/tmp/CHANGELOG.md' ); - expect( writeFileStub.firstCall.args[ 1 ] ).to.equal( 'New content.' ); - } ); - - it( 'allows changing cwd', () => { - const joinStub = sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); - const writeFileStub = sandbox.stub( fs, 'writeFileSync' ); - - changelogUtils.saveChangelog( 'New content.', '/new-cwd' ); - - expect( joinStub.calledOnce ).to.equal( true ); - expect( writeFileStub.calledOnce ).to.equal( true ); - expect( writeFileStub.firstCall.args[ 0 ] ).to.equal( '/new-cwd/CHANGELOG.md' ); - expect( writeFileStub.firstCall.args[ 1 ] ).to.equal( 'New content.' ); - } ); - } ); - - describe( 'truncateChangelog()', () => { - it( 'does nothing if there is no changelog', () => { - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( null ); - - changelogUtils.truncateChangelog( 5 ); - - expect( saveChangelogStub.called ).to.equal( false ); - } ); - - it( 'does nothing if changelog does not contain entries', () => { - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + '\n\n' ); - - changelogUtils.truncateChangelog( 5 ); - - expect( saveChangelogStub.called ).to.equal( false ); - } ); - - it( 'truncates the changelog and adds the link to the release page', () => { - const expectedChangelogEntries = [ - '## [0.3.0](https://github.com) (2017-01-13)', - '', - '3', - '', - 'Some text ## [like a release header]', - '', - '## [0.2.0](https://github.com) (2017-01-13)', - '', - '2' - ].join( '\n' ); - - const expectedChangelogFooter = [ - '', - '', - '---', - '', - 'To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases).', - '' - ].join( '\n' ); - - const changelogEntries = [ - expectedChangelogEntries, - '', - '## [0.1.0](https://github.com) (2017-01-13)', - '', - '1' - ].join( '\n' ); - - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelogEntries ); - - changelogUtils.truncateChangelog( 2 ); - - expect( saveChangelogStub.calledOnce ).to.equal( true ); - expect( saveChangelogStub.firstCall.args[ 0 ] ).to.equal( - changelogUtils.CHANGELOG_HEADER + - expectedChangelogEntries + - expectedChangelogFooter - ); - } ); - - it( 'does not add the link to the release page if changelog is not truncated', () => { - const expectedChangelogEntries = [ - '## [0.3.0](https://github.com) (2017-01-13)', - '', - '3', - '', - 'Some text ## [like a release header]', - '', - '## [0.2.0](https://github.com) (2017-01-13)', - '', - '2' - ].join( '\n' ); - - const expectedChangelogFooter = '\n'; - - const changelogEntries = expectedChangelogEntries; - - const saveChangelogStub = sandbox.stub( utils, 'saveChangelog' ); - - sandbox.stub( utils, 'getChangelog' ).returns( changelogUtils.CHANGELOG_HEADER + changelogEntries ); - - changelogUtils.truncateChangelog( 2 ); - - expect( saveChangelogStub.calledOnce ).to.equal( true ); - expect( saveChangelogStub.firstCall.args[ 0 ] ).to.equal( - changelogUtils.CHANGELOG_HEADER + - expectedChangelogEntries + - expectedChangelogFooter - ); - } ); - } ); - - describe( 'getFormattedDate()', () => { - let clock; - - beforeEach( () => { - clock = sinon.useFakeTimers( { - now: new Date( '2023-06-15 12:00:00' ) - } ); - } ); - - afterEach( () => { - clock.restore(); - } ); - - it( 'returns a date following the format "year-month-day" with the leading zeros', () => { - expect( changelogUtils.getFormattedDate() ).to.equal( '2023-06-15' ); - } ); - } ); -} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/constants.js b/packages/ckeditor5-dev-release-tools/tests/utils/constants.js new file mode 100644 index 000000000..872713e83 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/constants.js @@ -0,0 +1,22 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it } from 'vitest'; +import * as constants from '../../lib/utils/constants.js'; + +describe( 'constants', () => { + it( '#CHANGELOG_FILE', async () => { + expect( constants.CHANGELOG_FILE ).to.be.a( 'string' ); + } ); + + it( '#CHANGELOG_HEADER', async () => { + expect( constants.CHANGELOG_HEADER ).to.be.a( 'string' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getchangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/getchangelog.js new file mode 100644 index 000000000..9d3d70944 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getchangelog.js @@ -0,0 +1,49 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; +import path from 'path'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import getChangelog from '../../lib/utils/getchangelog.js'; + +vi.mock( 'fs' ); +vi.mock( 'path', () => ( { + default: { + join: vi.fn() + } +} ) ); +vi.mock( '../../lib/utils/constants.js', () => ( { + CHANGELOG_FILE: 'changelog.md' +} ) ); + +describe( 'getChangelog()', () => { + beforeEach( () => { + vi.mocked( path ).join.mockReturnValue( 'path-to-changelog' ); + vi.mocked( fs ).readFileSync.mockReturnValue( 'Content.' ); + } ); + + it( 'resolves the changelog content when a file exists (using default cwd)', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/home/ckeditor' ); + vi.mocked( fs ).existsSync.mockReturnValue( true ); + + expect( getChangelog() ).to.equal( 'Content.' ); + expect( vi.mocked( path ).join ).toHaveBeenCalledExactlyOnceWith( '/home/ckeditor', 'changelog.md' ); + expect( vi.mocked( fs ).readFileSync ).toHaveBeenCalledExactlyOnceWith( 'path-to-changelog', 'utf-8' ); + } ); + + it( 'resolves the changelog content when a file exists (using the specified cwd)', () => { + vi.mocked( fs ).existsSync.mockReturnValue( true ); + + expect( getChangelog( 'custom-cwd' ) ).to.equal( 'Content.' ); + expect( vi.mocked( path ).join ).toHaveBeenCalledExactlyOnceWith( 'custom-cwd', 'changelog.md' ); + expect( vi.mocked( fs ).readFileSync ).toHaveBeenCalledExactlyOnceWith( 'path-to-changelog', 'utf-8' ); + } ); + + it( 'returns null if the changelog does not exist', () => { + vi.mocked( fs ).existsSync.mockReturnValue( false ); + + expect( getChangelog() ).to.equal( null ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getchangesforversion.js b/packages/ckeditor5-dev-release-tools/tests/utils/getchangesforversion.js new file mode 100644 index 000000000..c0c4d58ce --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getchangesforversion.js @@ -0,0 +1,263 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import getChangelog from '../../lib/utils/getchangelog.js'; +import getChangesForVersion from '../../lib/utils/getchangesforversion.js'; +import { CHANGELOG_HEADER } from '../../lib/utils/constants.js'; + +vi.mock( '../../lib/utils/getchangelog.js' ); + +describe( 'getChangesForVersion()', () => { + beforeEach( () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/home/ckeditor' ); + } ); + + it( 'returns changes for the first tag which is a link to the release (default cwd)', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + const changelog = [ + '## [0.1.0](https://github.com) (2017-01-13)', + '', + expectedChangelog + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '0.1.0' ) ).to.equal( expectedChangelog ); + expect( getChangelog ).toHaveBeenCalledExactlyOnceWith( '/home/ckeditor' ); + } ); + + it( 'returns changes for the first tag which is a link to the release (a custom cwd)', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + const changelog = [ + '## [0.1.0](https://github.com) (2017-01-13)', + '', + expectedChangelog + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '0.1.0', '/custom/cwd' ) ).to.equal( expectedChangelog ); + expect( getChangelog ).toHaveBeenCalledExactlyOnceWith( '/custom/cwd' ); + } ); + + it( 'returns changes if a specified version starts with the "v" letter', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + const changelog = [ + '## 0.1.0 (2017-01-13)', + '', + expectedChangelog + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( 'v0.1.0' ) ).to.equal( expectedChangelog ); + } ); + + it( 'returns changes for the first tag which is not a link', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + const changelog = [ + '## 0.1.0 (2017-01-13)', + '', + expectedChangelog + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '0.1.0' ) ).to.equal( expectedChangelog ); + } ); + + it( 'returns changes between tags', () => { + const expectedChangelog = [ + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))', + '', + '### BREAKING CHANGE', + '', + '* Bump the major!' + ].join( '\n' ); + + const changelog = [ + '## [1.0.0](https://github.com/) (2017-01-13)', + '', + expectedChangelog, + '', + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '1.0.0' ) ).to.equal( expectedChangelog ); + } ); + + it( 'returns null if cannot find changes for the specified version', () => { + const changelog = [ + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '### Features', + '', + '* Cloned the main module. ([abcd123](https://github.com))' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + expect( getChangesForVersion( '1.0.0' ) ).to.equal( null ); + } ); + + it( 'works when date is not specified', () => { + const changelog = [ + '## 0.3.0', + '', + 'Foo' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '0.3.0' ) ).to.equal( 'Foo' ); + } ); + + it( 'captures correct range of changes (headers are URLs)', () => { + const changelog = [ + '## [0.3.0](https://github.com) (2017-01-13)', + '', + '3', + '', + 'Some text ## [like a release header]', + '', + '## [0.2.0](https://github.com) (2017-01-13)', + '', + '2', + '', + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '1' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '0.3.0' ) ).to.equal( '3\n\nSome text ## [like a release header]' ); + expect( getChangesForVersion( '0.2.0' ) ).to.equal( '2' ); + } ); + + it( 'captures correct range of changes (headers are plain text, "the initial" release check)', () => { + const changelog = [ + 'Changelog', + '=========', + '', + '## 1.0.2 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.2`.', + '', + '', + '## 1.0.1 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.1`.', + '', + '', + '## 1.0.0 (2022-02-22)', + '', + 'This is the initial release.' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '1.0.0' ) ).to.equal( 'This is the initial release.' ); + } ); + + it( 'captures correct range of changes (headers are plain text, "middle" version check)', () => { + const changelog = [ + 'Changelog', + '=========', + '', + '## 1.0.2 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.2`.', + '', + '', + '## 1.0.1 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.1`.', + '', + '', + '## 1.0.0 (2022-02-22)', + '', + 'This is the initial release.' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '1.0.1' ) ).to.equal( [ + '### Other changes', + '', + '* Other change for `1.0.1`.' + ].join( '\n' ) ); + } ); + + it( 'captures correct range of changes (headers are plain text, "the latest" check)', () => { + const changelog = [ + 'Changelog', + '=========', + '', + '## 1.0.2 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.2`.', + '', + '', + '## 1.0.1 (2022-02-22)', + '', + '### Other changes', + '', + '* Other change for `1.0.1`.', + '', + '', + '## 1.0.0 (2022-02-22)', + '', + 'This is the initial release.' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelog ); + + expect( getChangesForVersion( '1.0.2' ) ).to.equal( [ + '### Other changes', + '', + '* Other change for `1.0.2`.' + ].join( '\n' ) ); + } ); +} ); + diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getformatteddate.js b/packages/ckeditor5-dev-release-tools/tests/utils/getformatteddate.js new file mode 100644 index 000000000..e69387684 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getformatteddate.js @@ -0,0 +1,22 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; +import getFormattedDate from '../../lib/utils/getformatteddate.js'; + +describe( 'getFormattedDate()', () => { + beforeEach( () => { + vi.useFakeTimers(); + vi.setSystemTime( new Date( '2023-06-15 12:00:00' ) ); + } ); + + afterEach( () => { + vi.useRealTimers(); + } ); + + it( 'returns a date following the format "year-month-day" with the leading zeros', () => { + expect( getFormattedDate() ).to.equal( '2023-06-15' ); + } ); +} ); From f1d2e780de404d7637ef91460d2aecedf779929a Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 10:04:25 +0200 Subject: [PATCH 057/172] Tests for saveChangelog. --- .../tests/utils/savechangelog.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/savechangelog.js diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/savechangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/savechangelog.js new file mode 100644 index 000000000..a28d59f9c --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/savechangelog.js @@ -0,0 +1,45 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; +import path from 'path'; +import { describe, expect, it, vi } from 'vitest'; +import saveChangelog from '../../lib/utils/savechangelog.js'; + +vi.mock( 'fs' ); +vi.mock( 'path', () => ( { + default: { + join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ) + } +} ) ); +vi.mock( '../../lib/utils/constants.js', () => ( { + CHANGELOG_FILE: 'changelog.md' +} ) ); + +describe( 'saveChangelog()', () => { + it( 'saves the changelog (default cwd)', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/tmp' ); + + saveChangelog( 'New content.' ); + + expect( vi.mocked( path ).join ).toHaveBeenCalledExactlyOnceWith( '/tmp', 'changelog.md' ); + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + '/tmp/changelog.md', + 'New content.', + 'utf-8' + ); + } ); + + it( 'saves the changelog (allows passing a custom cwd)', () => { + saveChangelog( 'New content.', '/custom/cwd' ); + + expect( vi.mocked( path ).join ).toHaveBeenCalledExactlyOnceWith( '/custom/cwd', 'changelog.md' ); + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + '/custom/cwd/changelog.md', + 'New content.', + 'utf-8' + ); + } ); +} ); From a7281c088218e8135e6a56041b4a54f93a140900 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 11:57:50 +0200 Subject: [PATCH 058/172] Tests for truncateChangelog. --- .../tests/utils/truncatechangelog.js | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/truncatechangelog.js diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/truncatechangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/truncatechangelog.js new file mode 100644 index 000000000..ae26167fb --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/truncatechangelog.js @@ -0,0 +1,139 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { getRepositoryUrl } from '../../lib/utils/transformcommitutils.js'; +import saveChangelog from '../../lib/utils/savechangelog.js'; +import getChangelog from '../../lib/utils/getchangelog.js'; +import truncateChangelog from '../../lib/utils/truncatechangelog.js'; +import { CHANGELOG_HEADER } from '../../lib/utils/constants.js'; + +vi.mock( '../../lib/utils/transformcommitutils.js' ); +vi.mock( '../../lib/utils/savechangelog.js' ); +vi.mock( '../../lib/utils/getchangelog.js' ); +vi.mock( '../../lib/utils/constants.js', () => ( { + CHANGELOG_HEADER: '# Changelog\n\n' +} ) ); + +describe( 'truncateChangelog()', () => { + beforeEach( () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/home/ckeditor' ); + vi.mocked( getRepositoryUrl ).mockReturnValue( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); + + it( 'does nothing if there is no changelog', () => { + vi.mocked( getChangelog ).mockReturnValue( null ); + truncateChangelog( 5 ); + expect( vi.mocked( saveChangelog ) ).not.toHaveBeenCalled(); + } ); + + it( 'truncates the changelog and adds the link to the release page (using default cwd)', () => { + const expectedChangelogEntries = [ + '## [0.3.0](https://github.com) (2017-01-13)', + '', + '3', + '', + 'Some text ## [like a release header]', + '', + '## [0.2.0](https://github.com) (2017-01-13)', + '', + '2' + ].join( '\n' ); + + const expectedChangelogFooter = [ + '', + '', + '---', + '', + 'To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases).', + '' + ].join( '\n' ); + + const changelogEntries = [ + expectedChangelogEntries, + '', + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '1' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelogEntries ); + + truncateChangelog( 2 ); + + expect( vi.mocked( getChangelog ) ).toHaveBeenCalledExactlyOnceWith( '/home/ckeditor' ); + expect( vi.mocked( saveChangelog ) ).toHaveBeenCalledExactlyOnceWith( + CHANGELOG_HEADER + expectedChangelogEntries + expectedChangelogFooter, + '/home/ckeditor' + ); + } ); + + it( 'truncates the changelog and adds the link to the release page (using a custom cwd)', () => { + const expectedChangelogEntries = [ + '## [0.3.0](https://github.com) (2017-01-13)', + '', + '3', + '', + 'Some text ## [like a release header]', + '', + '## [0.2.0](https://github.com) (2017-01-13)', + '', + '2' + ].join( '\n' ); + + const expectedChangelogFooter = [ + '', + '', + '---', + '', + 'To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases).', + '' + ].join( '\n' ); + + const changelogEntries = [ + expectedChangelogEntries, + '', + '## [0.1.0](https://github.com) (2017-01-13)', + '', + '1' + ].join( '\n' ); + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelogEntries ); + + truncateChangelog( 2, '/custom/cwd' ); + + expect( vi.mocked( getChangelog ) ).toHaveBeenCalledExactlyOnceWith( '/custom/cwd' ); + expect( vi.mocked( saveChangelog ) ).toHaveBeenCalledExactlyOnceWith( + CHANGELOG_HEADER + expectedChangelogEntries + expectedChangelogFooter, + '/custom/cwd' + ); + } ); + + it( 'does not add the link to the release page if changelog is not truncated', () => { + const expectedChangelogEntries = [ + '## [0.3.0](https://github.com) (2017-01-13)', + '', + '3', + '', + 'Some text ## [like a release header]', + '', + '## [0.2.0](https://github.com) (2017-01-13)', + '', + '2' + ].join( '\n' ); + + const expectedChangelogFooter = '\n'; + const changelogEntries = expectedChangelogEntries; + + vi.mocked( getChangelog ).mockReturnValue( CHANGELOG_HEADER + changelogEntries ); + + truncateChangelog( 2 ); + + expect( vi.mocked( saveChangelog ) ).toHaveBeenCalledExactlyOnceWith( + CHANGELOG_HEADER + expectedChangelogEntries + expectedChangelogFooter, + expect.any( String ) + ); + } ); +} ); From 660229beba337cd5288ef5f83cc8d2ebee3c2d25 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 13:48:10 +0200 Subject: [PATCH 059/172] The CLI utils are now exported as a separate modules. --- .../ckeditor5-dev-release-tools/lib/index.js | 1 + .../generatechangelogformonorepository.js | 8 +- .../generatechangelogforsinglepackage.js | 8 +- .../lib/utils/cli.js | 343 ------------- .../lib/utils/configurereleaseoptions.js | 43 ++ .../lib/utils/confirmincludingpackage.js | 24 + .../lib/utils/confirmnpmtag.js | 32 ++ .../lib/utils/constants.js | 12 + .../lib/utils/displaycommits.js | 6 +- .../lib/utils/displayskippedpackages.js | 6 +- .../lib/utils/getpackagejson.js | 2 +- .../providenewversionformonorepository.js | 51 ++ .../lib/utils/providetoken.js | 26 + .../lib/utils/provideversion.js | 98 ++++ .../lib/utils/versions.js | 2 +- .../tests/index.js | 9 + .../tests/utils/checkversionavailability.js | 2 +- .../tests/utils/cli.js | 476 ------------------ .../tests/utils/configurereleaseoptions.js | 52 ++ .../tests/utils/confirmincludingpackage.js | 31 ++ .../tests/utils/confirmnpmtag.js | 61 +++ .../tests/utils/constants.js | 7 + .../providenewversionformonorepository.js | 149 ++++++ .../tests/utils/providetoken.js | 50 ++ .../tests/utils/provideversion.js | 203 ++++++++ .../tests/utils/versions.js | 2 +- 26 files changed, 867 insertions(+), 837 deletions(-) delete mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/cli.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js create mode 100644 packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js delete mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/cli.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/configurereleaseoptions.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/confirmincludingpackage.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/confirmnpmtag.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/providenewversionformonorepository.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/providetoken.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/provideversion.js diff --git a/packages/ckeditor5-dev-release-tools/lib/index.js b/packages/ckeditor5-dev-release-tools/lib/index.js index 1d6ce6481..43fc56853 100644 --- a/packages/ckeditor5-dev-release-tools/lib/index.js +++ b/packages/ckeditor5-dev-release-tools/lib/index.js @@ -32,3 +32,4 @@ export { default as checkVersionAvailability } from './utils/checkversionavailab export { default as verifyPackagesPublishedCorrectly } from './tasks/verifypackagespublishedcorrectly.js'; export { default as getNpmTagFromVersion } from './utils/getnpmtagfromversion.js'; export { default as isVersionPublishableForTag } from './utils/isversionpublishablefortag.js'; +export { default as provideToken } from './utils/providetoken.js'; diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js index 708a73438..34c34777c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js @@ -9,7 +9,6 @@ import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; import compareFunc from 'compare-func'; import chalk from 'chalk'; import semver from 'semver'; -import cli from '../utils/cli.js'; import displayCommits from '../utils/displaycommits.js'; import displaySkippedPackages from '../utils/displayskippedpackages.js'; import generateChangelog from '../utils/generatechangelog.js'; @@ -24,7 +23,8 @@ import saveChangelog from '../utils/savechangelog.js'; import truncateChangelog from '../utils/truncatechangelog.js'; import transformCommitFactory from '../utils/transformcommitfactory.js'; import { getRepositoryUrl } from '../utils/transformcommitutils.js'; -import { CHANGELOG_FILE, CHANGELOG_HEADER } from '../utils/constants.js'; +import provideNewVersionForMonoRepository from '../utils/providenewversionformonorepository.js'; +import { CHANGELOG_FILE, CHANGELOG_HEADER, CLI_INDENT_SIZE } from '../utils/constants.js'; const VERSIONING_POLICY_URL = 'https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html'; const noteInfo = `[ℹī¸](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-changes)`; @@ -297,7 +297,7 @@ export default async function generateChangelogForMonoRepository( options ) { bumpType = 'patch'; } - return cli.provideNewVersionForMonoRepository( highestVersion, packageHighestVersion, bumpType, { indentLevel: 1 } ) + return provideNewVersionForMonoRepository( highestVersion, packageHighestVersion, bumpType, { indentLevel: 1 } ) .then( version => { nextVersion = version; @@ -721,7 +721,7 @@ export default async function generateChangelogForMonoRepository( options ) { const startWithNewLine = options.startWithNewLine || false; const method = options.isWarning ? 'warning' : 'info'; - log[ method ]( `${ startWithNewLine ? '\n' : '' }${ ' '.repeat( indentLevel * cli.INDENT_SIZE ) }` + message ); + log[ method ]( `${ startWithNewLine ? '\n' : '' }${ ' '.repeat( indentLevel * CLI_INDENT_SIZE ) }` + message ); } } diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js index 42077a391..3a2604fae 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js @@ -7,7 +7,6 @@ import fs from 'fs'; import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; import chalk from 'chalk'; import semver from 'semver'; -import cli from '../utils/cli.js'; import displayCommits from '../utils/displaycommits.js'; import generateChangelog from '../utils/generatechangelog.js'; import getPackageJson from '../utils/getpackagejson.js'; @@ -19,7 +18,8 @@ import transformCommitFactory from '../utils/transformcommitfactory.js'; import getFormattedDate from '../utils/getformatteddate.js'; import saveChangelog from '../utils/savechangelog.js'; import getChangelog from '../utils/getchangelog.js'; -import { CHANGELOG_FILE, CHANGELOG_HEADER } from '../utils/constants.js'; +import provideVersion from '../utils/provideversion.js'; +import { CHANGELOG_FILE, CHANGELOG_HEADER, CLI_INDENT_SIZE } from '../utils/constants.js'; const SKIP_GENERATE_CHANGELOG = 'Typed "skip" as a new version. Aborting.'; @@ -80,7 +80,7 @@ export default async function generateChangelogForSinglePackage( options = {} ) displayCommits( allCommits, { indentLevel: 1 } ); - return cli.provideVersion( pkgJson.version, releaseType, { indentLevel: 1 } ); + return provideVersion( pkgJson.version, releaseType, { indentLevel: 1 } ); } ) .then( version => { if ( version === 'skip' ) { @@ -194,7 +194,7 @@ export default async function generateChangelogForSinglePackage( options = {} ) const startWithNewLine = options.startWithNewLine || false; const method = options.isWarning ? 'warning' : 'info'; - log[ method ]( `${ startWithNewLine ? '\n' : '' }${ ' '.repeat( indentLevel * cli.INDENT_SIZE ) }` + message ); + log[ method ]( `${ startWithNewLine ? '\n' : '' }${ ' '.repeat( indentLevel * CLI_INDENT_SIZE ) }` + message ); } } diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/cli.js b/packages/ckeditor5-dev-release-tools/lib/utils/cli.js deleted file mode 100644 index 4c333da62..000000000 --- a/packages/ckeditor5-dev-release-tools/lib/utils/cli.js +++ /dev/null @@ -1,343 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import inquirer from 'inquirer'; -import semver from 'semver'; -import chalk from 'chalk'; - -const QUESTION_MARK = chalk.cyan( '?' ); - -/** - * A size of default indent for a log. - */ -export const INDENT_SIZE = 3; - -/** - * A size of indent for a second and next lines in a log. The number is equal to length of the log string: - * '* 1234567 ', where '1234567' is a short commit id. - * It does not include a value from `cli.INDENT_SIZE`. - */ -export const COMMIT_INDENT_SIZE = 10; - -/** - * Asks a user for a confirmation for updating and tagging versions of the packages. - * - * @param {Map} packages Packages to release. - * @returns {Promise.} - */ -export function confirmUpdatingVersions( packages ) { - let message = 'Packages and their old and new versions:\n'; - - for ( const packageName of Array.from( packages.keys() ).sort() ) { - const packageDetails = packages.get( packageName ); - - message += ` * "${ packageName }": v${ packageDetails.previousVersion } => v${ packageDetails.version }\n`; - } - - message += 'Continue?'; - - const confirmQuestion = { - message, - type: 'confirm', - name: 'confirm', - default: true - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); -} - -/** - * Asks a user for a confirmation for publishing changes. - * - * @param {Map} packages Packages to release. - * @returns {Promise.} - */ -export function confirmPublishing( packages ) { - let message = 'Services where the release will be created:\n'; - - for ( const packageName of Array.from( packages.keys() ).sort() ) { - const packageDetails = packages.get( packageName ); - - let packageMessage = ` * "${ packageName }" - version: ${ packageDetails.version }`; - - const services = []; - - if ( packageDetails.shouldReleaseOnNpm ) { - services.push( 'NPM' ); - } - - if ( packageDetails.shouldReleaseOnGithub ) { - services.push( 'GitHub' ); - } - - let color; - - if ( services.length ) { - color = chalk.magenta; - packageMessage += ` - services: ${ services.join( ', ' ) } `; - } else { - color = chalk.gray; - packageMessage += ' - nothing to release'; - } - - message += color( packageMessage ) + '\n'; - } - - message += 'Continue?'; - - const confirmQuestion = { - message, - type: 'confirm', - name: 'confirm', - default: true - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); -} - -/** - * Asks a user for a confirmation for removing archives created by `npm pack` command. - * - * @returns {Promise.} - */ -export function confirmRemovingFiles() { - const confirmQuestion = { - message: 'Remove created archives?', - type: 'confirm', - name: 'confirm', - default: true - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); -} - -/** - * Asks a user for a confirmation for including a package that does not contain all required files. - * - * @returns {Promise.} - */ -export function confirmIncludingPackage() { - const confirmQuestion = { - message: 'Package does not contain all required files to publish. Include this package in the release and continue?', - type: 'confirm', - name: 'confirm', - default: true - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); -} - -/** - * Asks a user for providing the new version. - * - * @param {String} packageVersion - * @param {String|null} releaseTypeOrNewVersion - * @param {Object} [options] - * @param {Boolean} [options.disableInternalVersion=false] Whether to "internal" version is enabled. - * @param {Boolean} [options.disableSkipVersion=false] Whether to "skip" version is enabled. - * @param {Number} [options.indentLevel=0] The indent level. - * @returns {Promise.} - */ -export function provideVersion( packageVersion, releaseTypeOrNewVersion, options = {} ) { - const indentLevel = options.indentLevel || 0; - const suggestedVersion = getSuggestedVersion(); - - let message = 'Type the new version, "skip" or "internal"'; - - if ( options.disableInternalVersion ) { - message = 'Type the new version or "skip"'; - } - - message += ` (suggested: "${ suggestedVersion }", current: "${ packageVersion }"):`; - - const versionQuestion = { - type: 'input', - name: 'version', - default: suggestedVersion, - message, - - filter( input ) { - return input.trim(); - }, - - validate( input ) { - if ( !options.disableSkipVersion && input === 'skip' ) { - return true; - } - - if ( !options.disableInternalVersion && input === 'internal' ) { - return true; - } - - // TODO: Check whether provided version is available. - return semver.valid( input ) ? true : 'Please provide a valid version.'; - }, - - prefix: getPrefix( indentLevel ) - }; - - return inquirer.prompt( [ versionQuestion ] ) - .then( answers => answers.version ); - - function getSuggestedVersion() { - if ( !releaseTypeOrNewVersion || releaseTypeOrNewVersion === 'skip' ) { - return 'skip'; - } - - if ( semver.valid( releaseTypeOrNewVersion ) ) { - return releaseTypeOrNewVersion; - } - - if ( releaseTypeOrNewVersion === 'internal' ) { - return options.disableInternalVersion ? 'skip' : 'internal'; - } - - if ( semver.prerelease( packageVersion ) ) { - releaseTypeOrNewVersion = 'prerelease'; - } - - // If package's version is below the '1.0.0', bump the 'minor' instead of 'major' - if ( releaseTypeOrNewVersion === 'major' && semver.gt( '1.0.0', packageVersion ) ) { - return semver.inc( packageVersion, 'minor' ); - } - - return semver.inc( packageVersion, releaseTypeOrNewVersion ); - } -} - -/** - * Asks a user for providing the new version for a major release. - * - * @param {String} version - * @param {String} foundPackage - * @param {String} bumpType - * @param {Object} [options={}] - * @param {Number} [options.indentLevel=0] The indent level. - * @returns {Promise.} - */ -export function provideNewVersionForMonoRepository( version, foundPackage, bumpType, options = {} ) { - const indentLevel = options.indentLevel || 0; - const suggestedVersion = semver.inc( version, bumpType ); - - const message = 'Type the new version ' + - `(current highest: "${ version }" found in "${ chalk.underline( foundPackage ) }", suggested: "${ suggestedVersion }"):`; - - const versionQuestion = { - type: 'input', - name: 'version', - default: suggestedVersion, - message, - - filter( input ) { - return input.trim(); - }, - - validate( input ) { - if ( !semver.valid( input ) ) { - return 'Please provide a valid version.'; - } - - return semver.gt( input, version ) ? true : `Provided version must be higher than "${ version }".`; - }, - prefix: getPrefix( indentLevel ) - }; - - return inquirer.prompt( [ versionQuestion ] ) - .then( answers => answers.version ); -} - -/** - * Asks a user for providing the GitHub token. - * - * @returns {Promise.} - */ -export function provideToken() { - const tokenQuestion = { - type: 'password', - name: 'token', - message: 'Provide the GitHub token:', - validate( input ) { - return input.length === 40 ? true : 'Please provide a valid token.'; - } - }; - - return inquirer.prompt( [ tokenQuestion ] ) - .then( answers => answers.token ); -} - -/** - * Asks a user for selecting services where packages will be released. - * - * If the user choices a GitHub, required token also has to be provided. - * - * @returns {Promise.} - */ -export function configureReleaseOptions() { - const options = {}; - - const servicesQuestion = { - type: 'checkbox', - name: 'services', - message: 'Select services where packages will be released:', - choices: [ - 'npm', - 'GitHub' - ], - default: [ - 'npm', - 'GitHub' - ] - }; - - return inquirer.prompt( [ servicesQuestion ] ) - .then( answers => { - options.npm = answers.services.includes( 'npm' ); - options.github = answers.services.includes( 'GitHub' ); - - if ( !options.github ) { - return options; - } - - return provideToken() - .then( token => { - options.token = token; - - return options; - } ); - } ); -} - -/** - * Asks a user for a confirmation for updating and tagging versions of the packages. - * - * @param {String} versionTag A version tag based on a package version specified in `package.json`. - * @param {String} npmTag A tag typed by the user when using the release tools. - * @returns {Promise.} - */ -export function confirmNpmTag( versionTag, npmTag ) { - const areVersionsEqual = versionTag === npmTag; - const color = areVersionsEqual ? chalk.magenta : chalk.red; - - // eslint-disable-next-line max-len - const message = `The next release bumps the "${ color( versionTag ) }" version. Should it be published to npm as "${ color( npmTag ) }"?`; - - const confirmQuestion = { - message, - type: 'confirm', - name: 'confirm', - default: areVersionsEqual - }; - - return inquirer.prompt( [ confirmQuestion ] ) - .then( answers => answers.confirm ); -} - -function getPrefix( indent ) { - return ' '.repeat( indent * INDENT_SIZE ) + QUESTION_MARK; -} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js new file mode 100644 index 000000000..26b9d89bc --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js @@ -0,0 +1,43 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import inquirer from 'inquirer'; +import provideToken from './providetoken.js'; + +/** + * Asks a user for selecting services where packages will be released. + * + * If the user choices a GitHub, required token also has to be provided. + * + * @returns {Promise.} + */ +export default async function configureReleaseOptions() { + const options = {}; + + const servicesQuestion = { + type: 'checkbox', + name: 'services', + message: 'Select services where packages will be released:', + choices: [ + 'npm', + 'GitHub' + ], + default: [ + 'npm', + 'GitHub' + ] + }; + + const answers = await inquirer.prompt( [ servicesQuestion ] ); + + options.npm = answers.services.includes( 'npm' ); + options.github = answers.services.includes( 'GitHub' ); + + if ( options.github ) { + options.token = await provideToken(); + } + + return options; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js b/packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js new file mode 100644 index 000000000..160e8eab4 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js @@ -0,0 +1,24 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import inquirer from 'inquirer'; + +/** + * Asks a user for a confirmation for including a package that does not contain all required files. + * + * @returns {Promise.} + */ +export default async function confirmIncludingPackage() { + const confirmQuestion = { + message: 'Package does not contain all required files to publish. Include this package in the release and continue?', + type: 'confirm', + name: 'confirm', + default: true + }; + + const { confirm } = await inquirer.prompt( [ confirmQuestion ] ); + + return confirm; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js b/packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js new file mode 100644 index 000000000..94efd99a4 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js @@ -0,0 +1,32 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import inquirer from 'inquirer'; +import chalk from 'chalk'; + +/** + * Asks a user for a confirmation for updating and tagging versions of the packages. + * + * @param {String} versionTag A version tag based on a package version specified in `package.json`. + * @param {String} npmTag A tag typed by the user when using the release tools. + * @returns {Promise.} + */ +export default function confirmNpmTag( versionTag, npmTag ) { + const areVersionsEqual = versionTag === npmTag; + const color = areVersionsEqual ? chalk.magenta : chalk.red; + + // eslint-disable-next-line max-len + const message = `The next release bumps the "${ color( versionTag ) }" version. Should it be published to npm as "${ color( npmTag ) }"?`; + + const confirmQuestion = { + message, + type: 'confirm', + name: 'confirm', + default: areVersionsEqual + }; + + return inquirer.prompt( [ confirmQuestion ] ) + .then( answers => answers.confirm ); +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/constants.js b/packages/ckeditor5-dev-release-tools/lib/utils/constants.js index e2dd09a0c..ef26cb2b4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/constants.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/constants.js @@ -12,3 +12,15 @@ export const CHANGELOG_FILE = 'CHANGELOG.md'; * Changelog header. */ export const CHANGELOG_HEADER = 'Changelog\n=========\n\n'; + +/** + * A size of default indent for a log. + */ +export const CLI_INDENT_SIZE = 3; + +/** + * A size of indent for a second and next lines in a log. The number is equal to length of the log string: + * '* 1234567 ', where '1234567' is a short commit id. + * It does not include a value from `cli.INDENT_SIZE`. + */ +export const CLI_COMMIT_INDENT_SIZE = 10; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js index 0145c5749..9c24557a1 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js @@ -6,7 +6,7 @@ import chalk from 'chalk'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; import * as utils from './transformcommitutils.js'; -import { INDENT_SIZE, COMMIT_INDENT_SIZE } from './cli.js'; +import { CLI_COMMIT_INDENT_SIZE, CLI_INDENT_SIZE } from './constants.js'; /** * @param {Array.|Set.} commits @@ -19,7 +19,7 @@ export function displayCommits( commits, options = {} ) { const attachLinkToCommit = options.attachLinkToCommit || false; const indentLevel = options.indentLevel || 1; - const listIndent = ' '.repeat( INDENT_SIZE * indentLevel ); + const listIndent = ' '.repeat( CLI_INDENT_SIZE * indentLevel ); if ( !( commits.length || commits.size ) ) { log.info( listIndent + chalk.italic( 'No commits to display.' ) ); @@ -49,7 +49,7 @@ export function displayCommits( commits, options = {} ) { const isCommitIncluded = utils.availableCommitTypes.get( singleCommit.rawType ); const indent = commits.size > 1 ? listIndent.slice( 0, listIndent.length - 1 ) + chalk.gray( '|' ) : listIndent; - const noteIndent = indent + ' '.repeat( COMMIT_INDENT_SIZE ); + const noteIndent = indent + ' '.repeat( CLI_COMMIT_INDENT_SIZE ); let logMessage = `${ indent }* ${ chalk.yellow( hash.slice( 0, 7 ) ) } "${ utils.truncate( singleCommit.header, 100 ) }" `; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js b/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js index f492da277..d493bf586 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js @@ -5,8 +5,8 @@ import chalk from 'chalk'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; -import getPackageJson from './getpackagejson'; -import { INDENT_SIZE } from './cli'; +import getPackageJson from './getpackagejson.js'; +import { CLI_INDENT_SIZE } from './constants.js'; /** * Displays skipped packages. @@ -18,7 +18,7 @@ export function displaySkippedPackages( skippedPackagesPaths ) { return; } - const indent = ' '.repeat( INDENT_SIZE ); + const indent = ' '.repeat( CLI_INDENT_SIZE ); const packageNames = Array.from( skippedPackagesPaths ) .map( packagePath => getPackageJson( packagePath ).name ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js index 1be20a5ca..10d0bfcdd 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js @@ -15,7 +15,7 @@ import upath from 'upath'; * @param {String} [cwd=process.cwd()] Where to look for package.json. * @returns {Object} */ -export function getPackageJson( cwd = process.cwd() ) { +export default function getPackageJson( cwd = process.cwd() ) { let pkgJsonPath = cwd; if ( !pkgJsonPath.endsWith( 'package.json' ) ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js new file mode 100644 index 000000000..80c171f21 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js @@ -0,0 +1,51 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import inquirer from 'inquirer'; +import semver from 'semver'; +import chalk from 'chalk'; +import { CLI_INDENT_SIZE } from './constants.js'; + +/** + * Asks a user for providing the new version for a major release. + * + * @param {String} version + * @param {String} foundPackage + * @param {String} bumpType + * @param {Object} [options={}] + * @param {Number} [options.indentLevel=0] The indent level. + * @returns {Promise.} + */ +export default async function provideNewVersionForMonoRepository( version, foundPackage, bumpType, options = {} ) { + const indentLevel = options.indentLevel || 0; + const suggestedVersion = semver.inc( version, bumpType ); + + const message = 'Type the new version ' + + `(current highest: "${ version }" found in "${ chalk.underline( foundPackage ) }", suggested: "${ suggestedVersion }"):`; + + const versionQuestion = { + type: 'input', + name: 'version', + default: suggestedVersion, + message, + + filter( input ) { + return input.trim(); + }, + + validate( input ) { + if ( !semver.valid( input ) ) { + return 'Please provide a valid version.'; + } + + return semver.gt( input, version ) ? true : `Provided version must be higher than "${ version }".`; + }, + prefix: ' '.repeat( indentLevel * CLI_INDENT_SIZE ) + chalk.cyan( '?' ) + }; + + const answers = await inquirer.prompt( [ versionQuestion ] ); + + return answers.version; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js b/packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js new file mode 100644 index 000000000..a552df67c --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js @@ -0,0 +1,26 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import inquirer from 'inquirer'; + +/** + * Asks a user for providing the GitHub token. + * + * @returns {Promise.} + */ +export default async function provideToken() { + const tokenQuestion = { + type: 'password', + name: 'token', + message: 'Provide the GitHub token:', + validate( input ) { + return input.length === 40 ? true : 'Please provide a valid token.'; + } + }; + + const { token } = await inquirer.prompt( [ tokenQuestion ] ); + + return token; +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js b/packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js new file mode 100644 index 000000000..a3e14ef34 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js @@ -0,0 +1,98 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import inquirer from 'inquirer'; +import semver from 'semver'; +import chalk from 'chalk'; +import { CLI_INDENT_SIZE } from './constants.js'; + +/** + * Asks a user for providing the new version. + * + * @param {String} packageVersion + * @param {String|null} releaseTypeOrNewVersion + * @param {Object} [options] + * @param {Boolean} [options.disableInternalVersion=false] Whether to "internal" version is enabled. + * @param {Boolean} [options.disableSkipVersion=false] Whether to "skip" version is enabled. + * @param {Number} [options.indentLevel=0] The indent level. + * @returns {Promise.} + */ +export default function provideVersion( packageVersion, releaseTypeOrNewVersion, options = {} ) { + const indentLevel = options.indentLevel || 0; + const suggestedVersion = getSuggestedVersion( { + packageVersion, + releaseTypeOrNewVersion, + disableInternalVersion: options.disableInternalVersion + } ); + + let message = 'Type the new version, "skip" or "internal"'; + + if ( options.disableInternalVersion ) { + message = 'Type the new version or "skip"'; + } + + message += ` (suggested: "${ suggestedVersion }", current: "${ packageVersion }"):`; + + const versionQuestion = { + type: 'input', + name: 'version', + default: suggestedVersion, + message, + + filter( input ) { + return input.trim(); + }, + + validate( input ) { + if ( !options.disableSkipVersion && input === 'skip' ) { + return true; + } + + if ( !options.disableInternalVersion && input === 'internal' ) { + return true; + } + + // TODO: Check whether provided version is available. + return semver.valid( input ) ? true : 'Please provide a valid version.'; + }, + + prefix: ' '.repeat( indentLevel * CLI_INDENT_SIZE ) + chalk.cyan( '?' ) + }; + + return inquirer.prompt( [ versionQuestion ] ) + .then( answers => answers.version ); +} + +/** + * @param {Object} options + * @param {String} options.packageVersion + * @param {String|null} options.releaseTypeOrNewVersion + * @param {Boolean} options.disableInternalVersion + * @return {String} + */ +function getSuggestedVersion( { packageVersion, releaseTypeOrNewVersion, disableInternalVersion } ) { + if ( !releaseTypeOrNewVersion || releaseTypeOrNewVersion === 'skip' ) { + return 'skip'; + } + + if ( semver.valid( releaseTypeOrNewVersion ) ) { + return releaseTypeOrNewVersion; + } + + if ( releaseTypeOrNewVersion === 'internal' ) { + return disableInternalVersion ? 'skip' : 'internal'; + } + + if ( semver.prerelease( packageVersion ) ) { + releaseTypeOrNewVersion = 'prerelease'; + } + + // If package's version is below the '1.0.0', bump the 'minor' instead of 'major' + if ( releaseTypeOrNewVersion === 'major' && semver.gt( '1.0.0', packageVersion ) ) { + return semver.inc( packageVersion, 'minor' ); + } + + return semver.inc( packageVersion, releaseTypeOrNewVersion ); +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js index 645ce5792..0499955ac 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js @@ -5,7 +5,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; import getChangelog from './getchangelog.js'; -import { getPackageJson } from './getpackagejson.js'; +import getPackageJson from './getpackagejson.js'; /** * Returns a last created version in changelog file. diff --git a/packages/ckeditor5-dev-release-tools/tests/index.js b/packages/ckeditor5-dev-release-tools/tests/index.js index e3dea1d97..b483a9b17 100644 --- a/packages/ckeditor5-dev-release-tools/tests/index.js +++ b/packages/ckeditor5-dev-release-tools/tests/index.js @@ -33,6 +33,7 @@ import checkVersionAvailability from '../lib/utils/checkversionavailability.js'; import verifyPackagesPublishedCorrectly from '../lib/tasks/verifypackagespublishedcorrectly.js'; import getNpmTagFromVersion from '../lib/utils/getnpmtagfromversion.js'; import isVersionPublishableForTag from '../lib/utils/isversionpublishablefortag.js'; +import provideToken from '../lib/utils/providetoken.js'; import * as index from '../lib/index.js'; @@ -53,6 +54,7 @@ vi.mock( '../lib/utils/changelog' ); vi.mock( '../lib/utils/executeinparallel' ); vi.mock( '../lib/utils/validaterepositorytorelease' ); vi.mock( '../lib/utils/isversionpublishablefortag' ); +vi.mock( '../lib/utils/provideToken' ); describe( 'dev-release-tools/index', () => { describe( 'generateChangelogForSinglePackage()', () => { @@ -243,4 +245,11 @@ describe( 'dev-release-tools/index', () => { expect( index.verifyPackagesPublishedCorrectly ).to.equal( verifyPackagesPublishedCorrectly ); } ); } ); + + describe( 'provideToken()', () => { + it( 'should be a function', () => { + expect( provideToken ).to.be.a( 'function' ); + expect( index.provideToken ).to.equal( provideToken ); + } ); + } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js b/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js index 4473d4684..d0fa6c092 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/checkversionavailability.js @@ -45,7 +45,7 @@ describe( 'checkVersionAvailability()', () => { await expect( checkVersionAvailability( '1.0.1', 'stub-package' ) ).resolves.toBe( false ); } ); - it( 'should re-throw an error if unknown error occured', async () => { + it( 'should re-throw an error if unknown error occurred', async () => { vi.mocked( tools ).shExec.mockRejectedValue( new Error( 'Unknown error.' ) ); await expect( checkVersionAvailability( '1.0.1', 'stub-package' ) ) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/cli.js b/packages/ckeditor5-dev-release-tools/tests/utils/cli.js deleted file mode 100644 index 9641fba91..000000000 --- a/packages/ckeditor5-dev-release-tools/tests/utils/cli.js +++ /dev/null @@ -1,476 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); - -describe( 'dev-release-tools/utils', () => { - let cli, sandbox, questionItems, userAnswer, stub; - - describe( 'cli', () => { - beforeEach( () => { - userAnswer = undefined; - sandbox = sinon.createSandbox(); - questionItems = []; - - stub = { - chalk: { - red: sandbox.stub().callsFake( str => str ), - magenta: sandbox.stub().callsFake( str => str ), - cyan: sandbox.stub().callsFake( str => str ), - underline: sandbox.stub().callsFake( str => str ), - gray: sandbox.stub().callsFake( str => str ) - } - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'inquirer', { - prompt( questions ) { - questionItems.push( ...questions ); - const questionItem = questions[ 0 ]; - - // If `userAnswer` is undefined, return a suggested value as a user input. - return Promise.resolve( { - [ questionItem.name ]: typeof userAnswer != 'undefined' ? userAnswer : questionItem.default - } ); - } - } ); - - mockery.registerMock( 'chalk', stub.chalk ); - - cli = require( '../../lib/utils/cli' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); - } ); - - describe( 'INDENT_SIZE', () => { - it( 'is defined', () => { - expect( cli.INDENT_SIZE ).to.be.a( 'Number' ); - } ); - } ); - - describe( 'COMMIT_INDENT_SIZE', () => { - it( 'is defined', () => { - expect( cli.COMMIT_INDENT_SIZE ).to.be.a( 'Number' ); - } ); - } ); - - describe( 'confirmUpdatingVersions()', () => { - it( 'displays packages and their versions (current and proposed) to release', () => { - const packagesMap = new Map(); - - packagesMap.set( '@ckeditor/ckeditor5-engine', { - previousVersion: '1.0.0', - version: '1.1.0' - } ); - packagesMap.set( '@ckeditor/ckeditor5-core', { - previousVersion: '0.7.0', - version: '0.7.1' - } ); - - return cli.confirmUpdatingVersions( packagesMap ) - .then( () => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.match( /^Packages and their old and new versions:/ ); - expect( question.message ).to.match( /"@ckeditor\/ckeditor5-engine": v1\.0\.0 => v1\.1\.0/ ); - expect( question.message ).to.match( /"@ckeditor\/ckeditor5-core": v0\.7\.0 => v0\.7\.1/ ); - expect( question.message ).to.match( /Continue\?$/ ); - } ); - } ); - - it( 'sorts the packages alphabetically', () => { - const packagesMap = new Map(); - - packagesMap.set( '@ckeditor/ckeditor5-list', {} ); - packagesMap.set( '@ckeditor/ckeditor5-autoformat', {} ); - packagesMap.set( '@ckeditor/ckeditor5-basic-styles', {} ); - packagesMap.set( '@ckeditor/ckeditor5-core', {} ); - packagesMap.set( '@ckeditor/ckeditor5-link', {} ); - packagesMap.set( '@ckeditor/ckeditor5-build-classic', {} ); - - return cli.confirmUpdatingVersions( packagesMap ) - .then( () => { - const packagesAsArray = questionItems[ 0 ].message - .split( '\n' ) - // Remove header and footer from the message. - .slice( 1, -1 ) - // Extract package name from the whole line. - .map( line => line.replace( /.*"([^"]+)".*/, '$1' ) ); - - expect( packagesAsArray.length ).to.equal( 6 ); - expect( packagesAsArray[ 0 ] ).to.equal( '@ckeditor/ckeditor5-autoformat' ); - expect( packagesAsArray[ 1 ] ).to.equal( '@ckeditor/ckeditor5-basic-styles' ); - expect( packagesAsArray[ 2 ] ).to.equal( '@ckeditor/ckeditor5-build-classic' ); - expect( packagesAsArray[ 3 ] ).to.equal( '@ckeditor/ckeditor5-core' ); - expect( packagesAsArray[ 4 ] ).to.equal( '@ckeditor/ckeditor5-link' ); - expect( packagesAsArray[ 5 ] ).to.equal( '@ckeditor/ckeditor5-list' ); - } ); - } ); - } ); - - describe( 'confirmPublishing()', () => { - it( 'displays packages and services where they should be released', () => { - const packagesMap = new Map(); - - packagesMap.set( '@ckeditor/ckeditor5-engine', { - version: '1.1.0', - shouldReleaseOnNpm: true, - shouldReleaseOnGithub: true - } ); - packagesMap.set( '@ckeditor/ckeditor5-core', { - version: '0.7.0', - shouldReleaseOnNpm: false, - shouldReleaseOnGithub: true - } ); - packagesMap.set( '@ckeditor/ckeditor5-utils', { - version: '1.7.0', - shouldReleaseOnNpm: true, - shouldReleaseOnGithub: false - } ); - packagesMap.set( '@ckeditor/ckeditor5-widget', { - version: '2.0.0', - shouldReleaseOnNpm: false, - shouldReleaseOnGithub: false - } ); - - return cli.confirmPublishing( packagesMap ) - .then( () => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.match( /^Services where the release will be created:/ ); - expect( question.message ).to.match( /"@ckeditor\/ckeditor5-core" - version: 0\.7\.0 - services: GitHub/ ); - expect( question.message ).to.match( /"@ckeditor\/ckeditor5-engine" - version: 1\.1\.0 - services: NPM, GitHub/ ); - expect( question.message ).to.match( /"@ckeditor\/ckeditor5-utils" - version: 1\.7\.0 - services: NPM/ ); - expect( question.message ).to.match( /"@ckeditor\/ckeditor5-widget" - version: 2\.0\.0 - nothing to release/ ); - expect( question.message ).to.match( /Continue\?$/ ); - } ); - } ); - } ); - - describe( 'confirmRemovingFiles()', () => { - it( 'user can disagree with the proposed value', () => { - return cli.confirmRemovingFiles() - .then( () => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.match( /^Remove created archives\?/ ); - expect( question.type ).to.equal( 'confirm' ); - } ); - } ); - } ); - - describe( 'confirmIncludingPackage()', () => { - it( 'user can disagree with the proposed value', () => { - return cli.confirmIncludingPackage() - .then( () => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.match( - /^Package does not contain all required files to publish. Include this package in the release and continue\?/ - ); - expect( question.type ).to.equal( 'confirm' ); - } ); - } ); - } ); - - describe( 'provideVersion()', () => { - it( 'suggests specified version', () => { - return cli.provideVersion( '1.0.0', '1.1.0' ) - .then( newVersion => { - expect( newVersion ).to.equal( '1.1.0' ); - } ); - } ); - - it( 'should suggest proper "major" version for public package', () => { - return cli.provideVersion( '1.0.0', 'major' ) - .then( newVersion => { - expect( newVersion ).to.equal( '2.0.0' ); - } ); - } ); - - it( 'should suggest proper "minor" version for public package', () => { - return cli.provideVersion( '1.0.0', 'minor' ) - .then( newVersion => { - expect( questionItems[ 0 ].message ).to.equal( - 'Type the new version, "skip" or "internal" (suggested: "1.1.0", current: "1.0.0"):' - ); - - expect( newVersion ).to.equal( '1.1.0' ); - } ); - } ); - - it( 'should suggest proper "patch" version for public package', () => { - return cli.provideVersion( '1.0.0', 'patch' ) - .then( newVersion => { - expect( newVersion ).to.equal( '1.0.1' ); - } ); - } ); - - it( 'should suggest "skip" version for package which does not contain changes (proposed null)', () => { - return cli.provideVersion( '1.0.0', null ) - .then( newVersion => { - expect( newVersion ).to.equal( 'skip' ); - } ); - } ); - - it( 'should suggest "skip" version for package which does not contain changes (proposed "skip")', () => { - return cli.provideVersion( '1.0.0', 'skip' ) - .then( newVersion => { - expect( newVersion ).to.equal( 'skip' ); - } ); - } ); - - it( 'should suggest "minor" instead of "major" version for non-public package', () => { - return cli.provideVersion( '0.7.0', 'major' ) - .then( newVersion => { - expect( newVersion ).to.equal( '0.8.0' ); - } ); - } ); - - it( 'should suggest proper "patch" version for non-public package', () => { - return cli.provideVersion( '0.7.0', 'patch' ) - .then( newVersion => { - expect( newVersion ).to.equal( '0.7.1' ); - } ); - } ); - - it( 'returns "internal" if suggested version was "internal"', () => { - return cli.provideVersion( '0.1.0', 'internal' ) - .then( newVersion => { - expect( newVersion ).to.equal( 'internal' ); - } ); - } ); - - it( 'allows disabling "internal" version', () => { - return cli.provideVersion( '0.1.0', 'major', { disableInternalVersion: true } ) - .then( () => { - expect( questionItems[ 0 ].message ).to.equal( - 'Type the new version or "skip" (suggested: "0.2.0", current: "0.1.0"):' - ); - } ); - } ); - - it( 'returns "skip" if suggested version was "internal" but it is disabled', () => { - return cli.provideVersion( '0.1.0', 'internal', { disableInternalVersion: true } ) - .then( newVersion => { - expect( newVersion ).to.equal( 'skip' ); - } ); - } ); - - it( 'should suggest proper pre-release version for pre-release package (major bump)', () => { - return cli.provideVersion( '1.0.0-alpha.1', 'major' ) - .then( newVersion => { - expect( newVersion ).to.equal( '1.0.0-alpha.2' ); - } ); - } ); - - it( 'should suggest proper pre-release version for pre-release package (minor bump)', () => { - return cli.provideVersion( '1.0.0-alpha.1', 'minor' ) - .then( newVersion => { - expect( newVersion ).to.equal( '1.0.0-alpha.2' ); - } ); - } ); - - it( 'should suggest proper pre-release version for pre-release package (patch bump)', () => { - return cli.provideVersion( '1.0.0-alpha.1', 'patch' ) - .then( newVersion => { - expect( newVersion ).to.equal( '1.0.0-alpha.2' ); - } ); - } ); - - it( 'removes spaces from provided version', () => { - return cli.provideVersion( '1.0.0', 'major' ) - .then( () => { - const { filter } = questionItems[ 0 ]; - - expect( filter( ' 0.0.1' ) ).to.equal( '0.0.1' ); - expect( filter( '0.0.1 ' ) ).to.equal( '0.0.1' ); - expect( filter( ' 0.0.1 ' ) ).to.equal( '0.0.1' ); - } ); - } ); - - it( 'validates the provided version (disableInternalVersion=false)', () => { - return cli.provideVersion( '1.0.0', 'major' ) - .then( () => { - const { validate } = questionItems[ 0 ]; - - expect( validate( 'skip' ) ).to.equal( true ); - expect( validate( 'internal' ) ).to.equal( true ); - expect( validate( '2.0.0' ) ).to.equal( true ); - expect( validate( '0.1' ) ).to.equal( 'Please provide a valid version.' ); - } ); - } ); - - it( 'validates the provided version (disableInternalVersion=true)', () => { - return cli.provideVersion( '1.0.0', 'major', { disableInternalVersion: true } ) - .then( () => { - const { validate } = questionItems[ 0 ]; - - expect( validate( 'skip' ) ).to.equal( true ); - expect( validate( 'internal' ) ).to.equal( 'Please provide a valid version.' ); - expect( validate( '2.0.0' ) ).to.equal( true ); - expect( validate( '0.1' ) ).to.equal( 'Please provide a valid version.' ); - } ); - } ); - } ); - - describe( 'provideNewVersionForMonoRepository()', () => { - it( 'bumps major version', () => { - return cli.provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'major' ) - .then( newVersion => { - expect( newVersion ).to.equal( '2.0.0' ); - } ); - } ); - - it( 'bumps minor version', () => { - return cli.provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'minor' ) - .then( newVersion => { - expect( newVersion ).to.equal( '1.1.0' ); - } ); - } ); - - it( 'bumps patch version', () => { - return cli.provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'patch' ) - .then( newVersion => { - expect( newVersion ).to.equal( '1.0.1' ); - } ); - } ); - - it( 'suggest new version', () => { - return cli.provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'minor' ) - .then( () => { - expect( questionItems[ 0 ].default ).to.equal( '1.1.0' ); - } ); - } ); - - it( 'removes spaces from provided version', () => { - return cli.provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo' ) - .then( () => { - const { filter } = questionItems[ 0 ]; - - expect( filter( ' 0.0.1' ) ).to.equal( '0.0.1' ); - expect( filter( '0.0.1 ' ) ).to.equal( '0.0.1' ); - expect( filter( ' 0.0.1 ' ) ).to.equal( '0.0.1' ); - } ); - } ); - - it( 'validates the provided version', () => { - return cli.provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo' ) - .then( () => { - const { validate } = questionItems[ 0 ]; - - expect( validate( '2.0.0' ) ).to.equal( true ); - expect( validate( '1.1.0' ) ).to.equal( true ); - expect( validate( '1.0.0' ) ).to.equal( 'Provided version must be higher than "1.0.0".' ); - expect( validate( 'skip' ) ).to.equal( 'Please provide a valid version.' ); - expect( validate( 'internal' ) ).to.equal( 'Please provide a valid version.' ); - expect( validate( '0.1' ) ).to.equal( 'Please provide a valid version.' ); - } ); - } ); - } ); - - describe( 'provideToken()', () => { - it( 'user is able to provide the token', () => { - return cli.provideToken() - .then( () => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.match( /^Provide the GitHub token/ ); - expect( question.type ).to.equal( 'password' ); - } ); - } ); - - it( 'token must contain 40 characters', () => { - return cli.provideToken() - .then( () => { - const { validate } = questionItems[ 0 ]; - - expect( validate( 'abc' ) ).to.equal( 'Please provide a valid token.' ); - expect( validate( 'a'.repeat( 40 ) ) ).to.equal( true ); - } ); - } ); - } ); - - describe( 'configureReleaseOptions()', () => { - it( 'by default returns both services and requires Github token', () => { - sandbox.stub( cli, 'provideToken' ).resolves( 'a'.repeat( 40 ) ); - - return cli.configureReleaseOptions() - .then( options => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.match( /^Select services where packages will be released:/ ); - expect( question.type ).to.equal( 'checkbox' ); - - expect( cli.provideToken.calledOnce ).to.equal( true ); - - expect( options ).to.deep.equal( { - npm: true, - github: true, - token: 'a'.repeat( 40 ) - } ); - } ); - } ); - - it( 'does not ask about the GitHub token if ignores GitHub release', () => { - sandbox.stub( cli, 'provideToken' ); - userAnswer = [ 'npm' ]; - - return cli.configureReleaseOptions() - .then( options => { - expect( cli.provideToken.called ).to.equal( false ); - - expect( options ).to.deep.equal( { - npm: true, - github: false - } ); - } ); - } ); - } ); - - describe( 'confirmNpmTag()', () => { - it( 'should ask user if continue the release process when passing the same versions', () => { - return cli.confirmNpmTag( 'latest', 'latest' ) - .then( () => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.equal( - 'The next release bumps the "latest" version. Should it be published to npm as "latest"?' - ); - expect( question.type ).to.equal( 'confirm' ); - expect( question.default ).to.equal( true ); - expect( stub.chalk.magenta.callCount ).to.equal( 2 ); - } ); - } ); - - it( 'should ask user if continue the release process when passing different versions', () => { - return cli.confirmNpmTag( 'latest', 'alpha' ) - .then( () => { - const question = questionItems[ 0 ]; - - expect( question.message ).to.equal( - 'The next release bumps the "latest" version. Should it be published to npm as "alpha"?' - ); - expect( question.type ).to.equal( 'confirm' ); - expect( question.default ).to.equal( false ); - expect( stub.chalk.red.callCount ).to.equal( 2 ); - } ); - } ); - } ); - } ); -} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/configurereleaseoptions.js b/packages/ckeditor5-dev-release-tools/tests/utils/configurereleaseoptions.js new file mode 100644 index 000000000..467e222dc --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/configurereleaseoptions.js @@ -0,0 +1,52 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import inquirer from 'inquirer'; +import provideToken from '../../lib/utils/providetoken.js'; +import configureReleaseOptions from '../../lib/utils/configurereleaseoptions.js'; + +vi.mock( 'inquirer' ); +vi.mock( '../../lib/utils/providetoken.js' ); + +describe( 'configureReleaseOptions()', () => { + it( 'returns npm and Github services and asks for a GitHub token', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { + services: [ 'npm', 'GitHub' ] + } ); + vi.mocked( provideToken ).mockReturnValue( 'a'.repeat( 40 ) ); + + const options = await configureReleaseOptions(); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + name: 'services', + type: 'checkbox', + message: 'Select services where packages will be released:', + choices: expect.any( Array ), + default: expect.any( Array ) + } ) + ] ) + ); + + expect( vi.mocked( provideToken ) ).toHaveBeenCalledOnce(); + + expect( options ).toStrictEqual( { + npm: true, + github: true, + token: 'a'.repeat( 40 ) + } ); + } ); + + it( 'should not ask about a GitHub token if processing an npm release only', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { + services: [ 'npm' ] + } ); + + await configureReleaseOptions(); + expect( vi.mocked( provideToken ) ).not.toHaveBeenCalledOnce(); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/confirmincludingpackage.js b/packages/ckeditor5-dev-release-tools/tests/utils/confirmincludingpackage.js new file mode 100644 index 000000000..d63bae99b --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/confirmincludingpackage.js @@ -0,0 +1,31 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import inquirer from 'inquirer'; +import confirmIncludingPackage from '../../lib/utils/confirmincludingpackage.js'; + +vi.mock( 'inquirer' ); + +describe( 'confirmIncludingPackage()', () => { + it( 'user can disagree with the proposed value', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { + confirm: true + } ); + + await expect( confirmIncludingPackage() ).resolves.toBe( true ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + name: 'confirm', + type: 'confirm', + message: expect.stringContaining( 'Package does not contain all required files to publish.' ), + default: expect.any( Boolean ) + } ) + ] ) + ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/confirmnpmtag.js b/packages/ckeditor5-dev-release-tools/tests/utils/confirmnpmtag.js new file mode 100644 index 000000000..5b1fd9272 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/confirmnpmtag.js @@ -0,0 +1,61 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import chalk from 'chalk'; +import inquirer from 'inquirer'; +import confirmNpmTag from '../../lib/utils/confirmnpmtag.js'; + +vi.mock( 'inquirer' ); +vi.mock( 'chalk', () => ( { + default: { + magenta: vi.fn( input => input ), + red: vi.fn( input => input ) + } +} ) ); + +describe( 'confirmNpmTag()', () => { + it( 'should ask user if continue the release process when passing the same versions', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { + confirm: true + } ); + + await expect( confirmNpmTag( 'latest', 'latest' ) ).resolves.toBe( true ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + name: 'confirm', + type: 'confirm', + message: 'The next release bumps the "latest" version. Should it be published to npm as "latest"?', + default: true + } ) + ] ) + ); + + expect( vi.mocked( chalk ).magenta ).toHaveBeenCalledTimes( 2 ); + } ); + + it( 'should ask user if continue the release process when passing different versions', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { + confirm: false + } ); + + await expect( confirmNpmTag( 'latest', 'alpha' ) ).resolves.toBe( false ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + name: 'confirm', + type: 'confirm', + message: 'The next release bumps the "latest" version. Should it be published to npm as "alpha"?', + default: false + } ) + ] ) + ); + + expect( vi.mocked( chalk ).red ).toHaveBeenCalledTimes( 2 ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/constants.js b/packages/ckeditor5-dev-release-tools/tests/utils/constants.js index 872713e83..3e0d6fd67 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/constants.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/constants.js @@ -19,4 +19,11 @@ describe( 'constants', () => { it( '#CHANGELOG_HEADER', async () => { expect( constants.CHANGELOG_HEADER ).to.be.a( 'string' ); } ); + it( '#CLI_INDENT_SIZE', async () => { + expect( constants.CLI_INDENT_SIZE ).to.be.a( 'number' ); + } ); + + it( '#CLI_COMMIT_INDENT_SIZE', async () => { + expect( constants.CLI_COMMIT_INDENT_SIZE ).to.be.a( 'number' ); + } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/providenewversionformonorepository.js b/packages/ckeditor5-dev-release-tools/tests/utils/providenewversionformonorepository.js new file mode 100644 index 000000000..1de7c6fc5 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/providenewversionformonorepository.js @@ -0,0 +1,149 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import inquirer from 'inquirer'; +import chalk from 'chalk'; +import provideNewVersionForMonoRepository from '../../lib/utils/providenewversionformonorepository.js'; + +vi.mock( 'inquirer' ); +vi.mock( 'chalk', () => ( { + default: { + cyan: vi.fn( input => input ), + underline: vi.fn( input => input ) + } +} ) ); +vi.mock( '../../lib/utils/constants.js', () => ( { + CLI_INDENT_SIZE: 1 +} ) ); + +describe( 'provideNewVersionForMonoRepository()', () => { + beforeEach( () => { + vi.mocked( inquirer ).prompt.mockImplementation( input => { + const { default: version } = input[ 0 ]; + + return Promise.resolve( { version } ); + } ); + } ); + + it( 'bumps major version', async () => { + await expect( provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'major' ) ) + .resolves.toEqual( '2.0.0' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version (current highest: "1.0.0" found in "@ckeditor/foo", suggested: "2.0.0"):', + default: '2.0.0' + } ) + ] ) + ); + expect( vi.mocked( chalk ).underline ).toHaveBeenCalledExactlyOnceWith( '@ckeditor/foo' ); + } ); + + it( 'bumps minor version', async () => { + await expect( provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'minor' ) ) + .resolves.toEqual( '1.1.0' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version (current highest: "1.0.0" found in "@ckeditor/foo", suggested: "1.1.0"):', + default: '1.1.0' + } ) + ] ) + ); + expect( vi.mocked( chalk ).underline ).toHaveBeenCalledExactlyOnceWith( '@ckeditor/foo' ); + } ); + + it( 'bumps patch version', async () => { + await expect( provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'patch' ) ) + .resolves.toEqual( '1.0.1' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version (current highest: "1.0.0" found in "@ckeditor/foo", suggested: "1.0.1"):', + default: '1.0.1' + } ) + ] ) + ); + expect( vi.mocked( chalk ).underline ).toHaveBeenCalledExactlyOnceWith( '@ckeditor/foo' ); + } ); + + it( 'allows attaching the helper as a part of another process (indent=0)', async () => { + await expect( provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'major', { indentLevel: 0 } ) ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + prefix: '?' + } ) + ] ) + ); + + expect( vi.mocked( chalk ).cyan ).toHaveBeenCalledExactlyOnceWith( '?' ); + } ); + + it( 'allows attaching the helper as a part of another process (indent=3)', async () => { + await expect( provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo', 'major', { indentLevel: 3 } ) ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + // CLI indent is mocked as _a space per indent size_. + prefix: ' ?' + } ) + ] ) + ); + + expect( vi.mocked( chalk ).cyan ).toHaveBeenCalledExactlyOnceWith( '?' ); + } ); + + it( 'removes spaces from provided version', async () => { + await provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + filter: expect.any( Function ) + } ) + ] ) + ); + + const [ firstCall ] = vi.mocked( inquirer ).prompt.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstQuestion ] = firstArgument; + const { filter } = firstQuestion; + + expect( filter( ' 0.0.1' ) ).to.equal( '0.0.1' ); + expect( filter( '0.0.1 ' ) ).to.equal( '0.0.1' ); + expect( filter( ' 0.0.1 ' ) ).to.equal( '0.0.1' ); + } ); + + it( 'validates the provided version', async () => { + await provideNewVersionForMonoRepository( '1.0.0', '@ckeditor/foo' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + validate: expect.any( Function ) + } ) + ] ) + ); + + const [ firstCall ] = vi.mocked( inquirer ).prompt.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstQuestion ] = firstArgument; + const { validate } = firstQuestion; + + expect( validate( '2.0.0' ) ).to.equal( true ); + expect( validate( '1.1.0' ) ).to.equal( true ); + expect( validate( '1.0.0' ) ).to.equal( 'Provided version must be higher than "1.0.0".' ); + expect( validate( 'skip' ) ).to.equal( 'Please provide a valid version.' ); + expect( validate( 'internal' ) ).to.equal( 'Please provide a valid version.' ); + expect( validate( '0.1' ) ).to.equal( 'Please provide a valid version.' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/providetoken.js b/packages/ckeditor5-dev-release-tools/tests/utils/providetoken.js new file mode 100644 index 000000000..745a6ced5 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/providetoken.js @@ -0,0 +1,50 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import inquirer from 'inquirer'; +import provideToken from '../../lib/utils/providetoken.js'; + +vi.mock( 'inquirer' ); + +describe( 'provideToken()', () => { + beforeEach( () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { token: 'MyPassword' } ); + } ); + + it( 'user is able to provide the token', async () => { + await expect( provideToken() ).resolves.toEqual( 'MyPassword' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + name: 'token', + type: 'password', + message: 'Provide the GitHub token:' + } ) + ] ) + ); + } ); + + it( 'token must contain 40 characters', async () => { + await provideToken(); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + validate: expect.any( Function ) + } ) + ] ) + ); + + const [ firstCall ] = vi.mocked( inquirer ).prompt.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstQuestion ] = firstArgument; + const { validate } = firstQuestion; + + expect( validate( 'abc' ) ).to.equal( 'Please provide a valid token.' ); + expect( validate( 'a'.repeat( 40 ) ) ).to.equal( true ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/provideversion.js b/packages/ckeditor5-dev-release-tools/tests/utils/provideversion.js new file mode 100644 index 000000000..1ba6c2704 --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/provideversion.js @@ -0,0 +1,203 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import inquirer from 'inquirer'; +import provideVersion from '../../lib/utils/provideversion.js'; + +vi.mock( 'inquirer' ); + +describe( 'provideVersion()', () => { + beforeEach( () => { + vi.mocked( inquirer ).prompt.mockImplementation( input => { + const { default: version } = input[ 0 ]; + + return Promise.resolve( { version } ); + } ); + } ); + + it( 'should use a specified version if is matches the semver standard', async () => { + await expect( provideVersion( '1.0.0', '1.1.0' ) ).resolves.toEqual( '1.1.0' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version, "skip" or "internal" (suggested: "1.1.0", current: "1.0.0"):', + default: '1.1.0' + } ) + ] ) + ); + } ); + + it( 'should suggest proper "major" version for public package', async () => { + await expect( provideVersion( '1.0.0', 'major' ) ).resolves.toEqual( '2.0.0' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version, "skip" or "internal" (suggested: "2.0.0", current: "1.0.0"):', + default: '2.0.0' + } ) + ] ) + ); + } ); + + it( 'should suggest proper "minor" version for public package', async () => { + await expect( provideVersion( '1.0.0', 'minor' ) ).resolves.toEqual( '1.1.0' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version, "skip" or "internal" (suggested: "1.1.0", current: "1.0.0"):', + default: '1.1.0' + } ) + ] ) + ); + } ); + + it( 'should suggest proper "patch" version for public package', async () => { + await expect( provideVersion( '1.0.0', 'patch' ) ).resolves.toEqual( '1.0.1' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version, "skip" or "internal" (suggested: "1.0.1", current: "1.0.0"):', + default: '1.0.1' + } ) + ] ) + ); + } ); + + it( 'should suggest "skip" version for package which does not contain changes (proposed null)', async () => { + await expect( provideVersion( '1.0.0', null ) ).resolves.toEqual( 'skip' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version, "skip" or "internal" (suggested: "skip", current: "1.0.0"):', + default: 'skip' + } ) + ] ) + ); + } ); + + it( 'should suggest "skip" version for package which does not contain changes (proposed "skip")', async () => { + await expect( provideVersion( '1.0.0', 'skip' ) ).resolves.toEqual( 'skip' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version, "skip" or "internal" (suggested: "skip", current: "1.0.0"):', + default: 'skip' + } ) + ] ) + ); + } ); + + it( 'should suggest "minor" instead of "major" version for non-public package', async () => { + await expect( provideVersion( '0.7.0', 'minor' ) ).resolves.toEqual( '0.8.0' ); + } ); + + it( 'should suggest proper "patch" version for non-public package', async () => { + await expect( provideVersion( '0.7.0', 'patch' ) ).resolves.toEqual( '0.7.1' ); + } ); + + it( 'returns "internal" if suggested version was "internal"', async () => { + await expect( provideVersion( '0.1.0', 'internal' ) ).resolves.toEqual( 'internal' ); + } ); + + it( 'allows disabling "internal" version', async () => { + await expect( provideVersion( '0.1.0', 'major', { disableInternalVersion: true } ) ) + .resolves.toEqual( expect.any( String ) ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + message: 'Type the new version or "skip" (suggested: "0.2.0", current: "0.1.0"):', + default: expect.any( String ) + } ) + ] ) + ); + } ); + + it( 'returns "skip" if suggested version was "internal" but it is disabled', async () => { + await expect( provideVersion( '0.1.0', 'internal', { disableInternalVersion: true } ) ) + .resolves.toEqual( 'skip' ); + } ); + + it( 'should suggest proper pre-release version for pre-release package (major bump)', async () => { + await expect( provideVersion( '1.0.0-alpha.1', 'major' ) ).resolves.toEqual( '1.0.0-alpha.2' ); + } ); + + it( 'should suggest proper pre-release version for pre-release package (minor bump)', async () => { + await expect( provideVersion( '1.0.0-alpha.1', 'minor' ) ).resolves.toEqual( '1.0.0-alpha.2' ); + } ); + + it( 'should suggest proper pre-release version for pre-release package (patch bump)', async () => { + await expect( provideVersion( '1.0.0-alpha.1', 'patch' ) ).resolves.toEqual( '1.0.0-alpha.2' ); + } ); + + it( 'removes spaces from provided version', async () => { + await provideVersion( '1.0.0', 'major' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + filter: expect.any( Function ) + } ) + ] ) + ); + const [ firstCall ] = vi.mocked( inquirer ).prompt.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstQuestion ] = firstArgument; + const { filter } = firstQuestion; + + expect( filter( ' 0.0.1' ) ).to.equal( '0.0.1' ); + expect( filter( '0.0.1 ' ) ).to.equal( '0.0.1' ); + expect( filter( ' 0.0.1 ' ) ).to.equal( '0.0.1' ); + } ); + + it( 'validates the provided version (disableInternalVersion=false)', async () => { + await provideVersion( '1.0.0', 'major' ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + validate: expect.any( Function ) + } ) + ] ) + ); + const [ firstCall ] = vi.mocked( inquirer ).prompt.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstQuestion ] = firstArgument; + const { validate } = firstQuestion; + + expect( validate( 'skip' ) ).to.equal( true ); + expect( validate( 'internal' ) ).to.equal( true ); + expect( validate( '2.0.0' ) ).to.equal( true ); + expect( validate( '0.1' ) ).to.equal( 'Please provide a valid version.' ); + } ); + + it( 'validates the provided version (disableInternalVersion=true)', async () => { + await provideVersion( '1.0.0', 'major', { disableInternalVersion: true } ); + + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( + expect.arrayContaining( [ + expect.objectContaining( { + validate: expect.any( Function ) + } ) + ] ) + ); + const [ firstCall ] = vi.mocked( inquirer ).prompt.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstQuestion ] = firstArgument; + const { validate } = firstQuestion; + + expect( validate( 'skip' ) ).to.equal( true ); + expect( validate( 'internal' ) ).to.equal( 'Please provide a valid version.' ); + expect( validate( '2.0.0' ) ).to.equal( true ); + expect( validate( '0.1' ) ).to.equal( 'Please provide a valid version.' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js index eb15f71c5..655c91788 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js @@ -6,7 +6,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; import getChangelog from '../../lib/utils/getchangelog.js'; -import { getPackageJson } from '../../lib/utils/getpackagejson.js'; +import getPackageJson from '../../lib/utils/getpackagejson.js'; import { getLastFromChangelog, From 6c9f3dc9ec21121852bc7e33621e586f2d7b3be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Tue, 10 Sep 2024 14:09:52 +0200 Subject: [PATCH 060/172] Aligned tests in `utils/abortcontroller.js` and `utils/abortcontroller-integration.js`. --- .../lib/utils/executeinparallel.js | 8 +- .../{parallelworker.cjs => parallelworker.js} | 6 +- .../utils/executeinparallel-integration.js | 78 +--- .../tests/utils/executeinparallel.js | 416 +++++++++--------- 4 files changed, 232 insertions(+), 276 deletions(-) rename packages/ckeditor5-dev-release-tools/lib/utils/{parallelworker.cjs => parallelworker.js} (78%) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js index 5216e1958..d7084080d 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js @@ -11,9 +11,9 @@ import os from 'os'; import fs from 'fs/promises'; import { Worker } from 'worker_threads'; import { glob } from 'glob'; -import { registerAbortController, deregisterAbortController } from './abortcontroller'; +import { registerAbortController, deregisterAbortController } from './abortcontroller.js'; -const WORKER_SCRIPT = upath.join( __dirname, 'parallelworker.cjs' ); +const WORKER_SCRIPT = new URL( './abortcontroller.js', import.meta.url ); /** * This util allows executing a specified task in parallel using Workers. It can be helpful when executing a not resource-consuming @@ -60,8 +60,8 @@ export default async function executeInParallel( options ) { const packagesInThreads = getPackagesGroupedByThreads( packagesToProcess, concurrency ); - const callbackModule = upath.join( cwd, crypto.randomUUID() + '.cjs' ); - await fs.writeFile( callbackModule, `'use strict';\nexport ${ taskToExecute };`, 'utf-8' ); + const callbackModule = upath.join( cwd, crypto.randomUUID() + '.js' ); + await fs.writeFile( callbackModule, `export default ${ taskToExecute };`, 'utf-8' ); const onPackageDone = progressFactory( listrTask, packagesToProcess.length ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.cjs b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js similarity index 78% rename from packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.cjs rename to packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js index 41231633a..3e0771763 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.cjs +++ b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js @@ -11,10 +11,8 @@ * @param {String} callbackModule * @param {Array.} packages */ - import { parentPort, workerData } from 'worker_threads'; - - // TODO: To decide what to do. - const callback = require( workerData.callbackModule ); + const { parentPort, workerData } = await import( 'worker_threads' ); + const callback = await import( workerData.callbackModule ); for ( const packagePath of workerData.packages ) { await callback( packagePath, workerData.taskOptions ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js index 64c1c5f16..3acb9d60a 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js @@ -3,32 +3,25 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const expect = require( 'chai' ).expect; -const fs = require( 'fs' ); -const path = require( 'path' ); -const sinon = require( 'sinon' ); -const glob = require( 'glob' ); +import { describe, it, expect, beforeEach } from 'vitest'; +import fs from 'fs'; +import path from 'path'; +import { glob } from 'glob'; +import executeInParallel from '../../lib/utils/executeinparallel.js'; const REPOSITORY_ROOT = path.join( __dirname, '..', '..', '..', '..' ); -// This file covers the "parallelworker.cjs" file. +// This file covers the "parallelworker.js" file. describe( 'dev-release-tools/utils', () => { - let executeInParallel, abortController; + let abortController; beforeEach( () => { abortController = new AbortController(); - executeInParallel = require( '../../lib/utils/executeinparallel' ); - } ); - - afterEach( () => { - sinon.restore(); } ); describe( 'executeInParallel() - integration', () => { - it( 'should store current time in all found packages (callback returns a promise)', async () => { + it( 'should store current time in all found packages', async () => { const timeBefore = new Date().getTime(); await executeInParallel( { @@ -37,50 +30,11 @@ describe( 'dev-release-tools/utils', () => { packagesDirectory: 'packages', signal: abortController.signal, taskToExecute: async packagePath => { - const fs = require( 'fs/promises' ); - const path = require( 'path' ); - const filePath = path.join( packagePath, 'executeinparallel-integration.log' ); - - await fs.writeFile( filePath, new Date().getTime().toString() ); - }, - listrTask: { - output: '' - } - } ); - - const timeAfter = new Date().getTime(); - - const data = glob.sync( 'packages/*/executeinparallel-integration.log', { cwd: REPOSITORY_ROOT, absolute: true } ) - .map( logFile => { - return { - source: logFile, - value: parseInt( fs.readFileSync( logFile, 'utf-8' ) ), - packageName: logFile.split( '/' ).reverse().slice( 1, 2 ).pop() - }; - } ); - - for ( const { value, packageName, source } of data ) { - expect( value > timeBefore, `comparing timeBefore (${ packageName })` ).to.equal( true ); - expect( value < timeAfter, `comparing timeAfter (${ packageName })` ).to.equal( true ); - - fs.unlinkSync( source ); - } - } ); - - it( 'should store current time in all found packages (callback is a synchronous function)', async () => { - const timeBefore = new Date().getTime(); - - await executeInParallel( { - cwd: REPOSITORY_ROOT, - concurrency: 2, - packagesDirectory: 'packages', - signal: abortController.signal, - taskToExecute: packagePath => { - const fs = require( 'fs' ); - const path = require( 'path' ); + const fs = await import( 'fs/promises' ); + const path = await import( 'path' ); const filePath = path.join( packagePath, 'executeinparallel-integration.log' ); - fs.writeFileSync( filePath, new Date().getTime().toString() ); + fs.writeFile( filePath, new Date().getTime().toString() ); }, listrTask: { output: '' @@ -99,8 +53,8 @@ describe( 'dev-release-tools/utils', () => { } ); for ( const { value, packageName, source } of data ) { - expect( value > timeBefore, `comparing timeBefore (${ packageName })` ).to.equal( true ); - expect( value < timeAfter, `comparing timeAfter (${ packageName })` ).to.equal( true ); + expect( value > timeBefore, `comparing timeBefore (${ packageName })` ).toEqual( true ); + expect( value < timeAfter, `comparing timeAfter (${ packageName })` ).toEqual( true ); fs.unlinkSync( source ); } @@ -113,8 +67,8 @@ describe( 'dev-release-tools/utils', () => { packagesDirectory: 'packages', signal: abortController.signal, taskToExecute: async ( packagePath, taskOptions ) => { - const fs = require( 'fs/promises' ); - const path = require( 'path' ); + const fs = await import( 'fs/promises' ); + const path = await import( 'path' ); const filePath = path.join( packagePath, 'executeinparallel-integration.log' ); await fs.writeFile( filePath, JSON.stringify( taskOptions ) ); @@ -144,7 +98,7 @@ describe( 'dev-release-tools/utils', () => { } ); for ( const { value, packageName, source } of data ) { - expect( value, `comparing taskOptions (${ packageName })` ).to.deep.equal( { + expect( value, `comparing taskOptions (${ packageName })` ).toEqual( { property: 'Example of the property.', some: { deeply: { diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js index 0cdefc38b..067e7bddd 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js @@ -3,70 +3,65 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { glob } from 'glob'; +import fs from 'fs/promises'; +import { registerAbortController, deregisterAbortController } from '../../lib/utils/abortcontroller.js'; +import executeInParallel from '../../lib/utils/executeinparallel.js'; + +const stubs = vi.hoisted( () => ( { + WorkerMock: class { + constructor( script, options ) { + // Define a static property that keeps all instances for a particular test scenario. + if ( !this.constructor.instances ) { + this.constructor.instances = []; + } -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); + this.constructor.instances.push( this ); -describe( 'dev-release-tools/utils', () => { - let executeInParallel, stubs, abortController, WorkerMock, defaultOptions, outputHistory; + this.workerData = options.workerData; + this.on = vi.fn(); + this.terminate = vi.fn(); - beforeEach( () => { - WorkerMock = class { - constructor( script, options ) { - // Define a static property that keeps all instances for a particular test scenario. - if ( !this.constructor.instances ) { - this.constructor.instances = []; - } + expect( script.endsWith( 'parallelworker.js' ) ).toEqual( true ); + } + } +} ) ); - this.constructor.instances.push( this ); +vi.mock( 'worker_threads', () => ( { + Worker: stubs.WorkerMock +} ) ); - this.workerData = options.workerData; - this.on = sinon.stub(); - this.terminate = sinon.stub(); +vi.mock( 'os', () => ( { + default: { + cpus: vi.fn( () => new Array( 4 ) ) + } +} ) ); - expect( script.endsWith( 'parallelworker.cjs' ) ).to.equal( true ); - } - }; +vi.mock( 'crypto', () => ( { + default: { + randomUUID: vi.fn( () => 'uuid-4' ) + } +} ) ); - outputHistory = []; +vi.mock( 'glob' ); +vi.mock( 'fs/promises' ); +vi.mock( '../../lib/utils/abortcontroller.js' ); - stubs = { - process: { - cwd: sinon.stub( process, 'cwd' ).returns( '/home/ckeditor' ) - }, - os: { - cpus: sinon.stub().returns( new Array( 4 ) ) - }, - crypto: { - randomUUID: sinon.stub().returns( 'uuid-4' ) - }, - fs: { - writeFile: sinon.stub().resolves(), - unlink: sinon.stub().resolves() - }, - worker_threads: { - Worker: WorkerMock - }, - glob: { - glob: sinon.stub().resolves( [ - '/home/ckeditor/my-packages/package-01', - '/home/ckeditor/my-packages/package-02', - '/home/ckeditor/my-packages/package-03', - '/home/ckeditor/my-packages/package-04' - ] ) - }, - spinnerStub: { - start: sinon.stub(), - finish: sinon.stub(), - increase: sinon.stub() - }, - abortController: { - registerAbortController: sinon.stub(), - deregisterAbortController: sinon.stub() - } - }; +describe( 'dev-release-tools/utils', () => { + let abortController, defaultOptions, outputHistory; + + beforeEach( () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/home/ckeditor' ); + + vi.mocked( glob ).mockResolvedValue( [ + '/home/ckeditor/my-packages/package-01', + '/home/ckeditor/my-packages/package-02', + '/home/ckeditor/my-packages/package-03', + '/home/ckeditor/my-packages/package-04' + ] ); + + outputHistory = []; abortController = new AbortController(); @@ -80,19 +75,11 @@ describe( 'dev-release-tools/utils', () => { } } }; - - executeInParallel = proxyquire( '../../lib/utils/executeinparallel', { - os: stubs.os, - crypto: stubs.crypto, - 'fs/promises': stubs.fs, - worker_threads: stubs.worker_threads, - glob: stubs.glob, - './abortcontroller': stubs.abortController - } ); } ); afterEach( () => { - sinon.restore(); + // Since the mock is shared across all tests, reset static property that keeps all created instances. + stubs.WorkerMock.instances = []; } ); describe( 'executeInParallel()', () => { @@ -101,28 +88,29 @@ describe( 'dev-release-tools/utils', () => { await delay( 0 ); // By default the helper uses a half of available CPUs. - expect( WorkerMock.instances ).to.lengthOf( 2 ); + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.equal( 'my-packages/*/' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.be.an( 'object' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'cwd', '/home/ckeditor' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'absolute', true ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: '/home/ckeditor', + absolute: true + } ) ); - expect( stubs.fs.writeFile.callCount ).to.equal( 1 ); - expect( stubs.fs.writeFile.firstCall.args[ 0 ] ).to.equal( '/home/ckeditor/uuid-4.cjs' ); - expect( stubs.fs.writeFile.firstCall.args[ 1 ] ).to.equal( - '\'use strict\';\nmodule.exports = packagePath => console.log( \'pwd\', packagePath );' + expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); + expect( fs.writeFile ).toHaveBeenCalledWith( + '/home/ckeditor/uuid-4.js', + 'export default packagePath => console.log( \'pwd\', packagePath );', + 'utf-8' ); - expect( firstWorker.workerData ).to.be.an( 'object' ); - expect( firstWorker.workerData ).to.have.property( 'callbackModule', '/home/ckeditor/uuid-4.cjs' ); - expect( firstWorker.workerData ).to.have.property( 'packages' ); + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.js' ); + expect( firstWorker.workerData ).toHaveProperty( 'packages' ); - expect( secondWorker.workerData ).to.be.an( 'object' ); - expect( secondWorker.workerData ).to.have.property( 'callbackModule', '/home/ckeditor/uuid-4.cjs' ); - expect( secondWorker.workerData ).to.have.property( 'packages' ); + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.js' ); + expect( secondWorker.workerData ).toHaveProperty( 'packages' ); // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -141,16 +129,16 @@ describe( 'dev-release-tools/utils', () => { await delay( 0 ); // By default the helper uses a half of available CPUs. - expect( WorkerMock.instances ).to.lengthOf( 2 ); + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( firstWorker.workerData.packages ).to.deep.equal( [ + expect( firstWorker.workerData.packages ).toEqual( [ '/home/ckeditor/my-packages/package-01', '/home/ckeditor/my-packages/package-04' ] ); - expect( secondWorker.workerData.packages ).to.deep.equal( [ + expect( secondWorker.workerData.packages ).toEqual( [ '/home/ckeditor/my-packages/package-03' ] ); @@ -169,12 +157,13 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( options ); await delay( 0 ); - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.equal( 'my-packages/*/' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.be.an( 'object' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'cwd', '/custom/cwd' ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: '/custom/cwd', + absolute: true + } ) ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -184,17 +173,18 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'should normalize the current working directory to unix-style (default value, Windows path)', async () => { - stubs.process.cwd.returns( 'C:\\Users\\ckeditor' ); + process.cwd.mockReturnValue( 'C:\\Users\\ckeditor' ); const promise = executeInParallel( defaultOptions ); await delay( 0 ); - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.equal( 'my-packages/*/' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.be.an( 'object' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'cwd', 'C:/Users/ckeditor' ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: 'C:/Users/ckeditor', + absolute: true + } ) ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -211,12 +201,13 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( options ); await delay( 0 ); - expect( stubs.glob.glob.callCount ).to.equal( 1 ); - expect( stubs.glob.glob.firstCall.args[ 0 ] ).to.equal( 'my-packages/*/' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.be.an( 'object' ); - expect( stubs.glob.glob.firstCall.args[ 1 ] ).to.have.property( 'cwd', 'C:/Users/ckeditor' ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: 'C:/Users/ckeditor', + absolute: true + } ) ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -226,7 +217,7 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'should work on normalized paths to packages', async () => { - stubs.glob.glob.resolves( [ + vi.mocked( glob ).mockResolvedValue( [ 'C:/Users/workspace/ckeditor/my-packages/package-01', 'C:/Users/workspace/ckeditor/my-packages/package-02', 'C:/Users/workspace/ckeditor/my-packages/package-03', @@ -237,16 +228,16 @@ describe( 'dev-release-tools/utils', () => { await delay( 0 ); // By default the helper uses a half of available CPUs. - expect( WorkerMock.instances ).to.lengthOf( 2 ); + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( firstWorker.workerData.packages ).to.deep.equal( [ + expect( firstWorker.workerData.packages ).toEqual( [ 'C:/Users/workspace/ckeditor/my-packages/package-01', 'C:/Users/workspace/ckeditor/my-packages/package-03' ] ); - expect( secondWorker.workerData.packages ).to.deep.equal( [ + expect( secondWorker.workerData.packages ).toEqual( [ 'C:/Users/workspace/ckeditor/my-packages/package-02', 'C:/Users/workspace/ckeditor/my-packages/package-04' ] ); @@ -276,15 +267,15 @@ describe( 'dev-release-tools/utils', () => { await delay( 0 ); // By default the helper uses a half of available CPUs. - expect( WorkerMock.instances ).to.lengthOf( 2 ); + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( firstWorker.workerData ).to.be.an( 'object' ); - expect( firstWorker.workerData ).to.have.deep.property( 'taskOptions', taskOptions ); + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'taskOptions', taskOptions ); - expect( secondWorker.workerData ).to.be.an( 'object' ); - expect( secondWorker.workerData ).to.have.property( 'taskOptions', taskOptions ); + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'taskOptions', taskOptions ); // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -294,29 +285,30 @@ describe( 'dev-release-tools/utils', () => { } ); it( 'should create the temporary module properly when using Windows-style paths', async () => { - stubs.process.cwd.returns( 'C:\\Users\\ckeditor' ); + process.cwd.mockReturnValue( 'C:\\Users\\ckeditor' ); const promise = executeInParallel( defaultOptions ); await delay( 0 ); - expect( stubs.fs.writeFile.callCount ).to.equal( 1 ); - expect( stubs.fs.writeFile.firstCall.args[ 0 ] ).to.equal( 'C:/Users/ckeditor/uuid-4.cjs' ); - expect( stubs.fs.writeFile.firstCall.args[ 1 ] ).to.equal( - '\'use strict\';\nmodule.exports = packagePath => console.log( \'pwd\', packagePath );' + expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); + expect( fs.writeFile ).toHaveBeenCalledWith( + 'C:/Users/ckeditor/uuid-4.js', + 'export default packagePath => console.log( \'pwd\', packagePath );', + 'utf-8' ); // By default the helper uses a half of available CPUs. - expect( WorkerMock.instances ).to.lengthOf( 2 ); + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( firstWorker.workerData ).to.be.an( 'object' ); - expect( firstWorker.workerData ).to.have.property( 'callbackModule', 'C:/Users/ckeditor/uuid-4.cjs' ); - expect( firstWorker.workerData ).to.have.property( 'packages' ); + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.js' ); + expect( firstWorker.workerData ).toHaveProperty( 'packages' ); - expect( secondWorker.workerData ).to.be.an( 'object' ); - expect( secondWorker.workerData ).to.have.property( 'callbackModule', 'C:/Users/ckeditor/uuid-4.cjs' ); - expect( secondWorker.workerData ).to.have.property( 'packages' ); + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.js' ); + expect( secondWorker.workerData ).toHaveProperty( 'packages' ); // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -333,10 +325,10 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( options ); await delay( 0 ); - expect( WorkerMock.instances ).to.lengthOf( 4 ); + expect( stubs.WorkerMock.instances ).toHaveLength( 4 ); // Workers did not emit an error. - for ( const worker of WorkerMock.instances ) { + for ( const worker of stubs.WorkerMock.instances ) { getExitCallback( worker )( 0 ); } @@ -347,7 +339,7 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; getExitCallback( firstWorker )( 1 ); getExitCallback( secondWorker )( 0 ); @@ -359,7 +351,7 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; getExitCallback( firstWorker )( 0 ); getExitCallback( secondWorker )( 1 ); @@ -371,7 +363,7 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; const error = new Error( 'Example error from a worker.' ); getErrorCallback( firstWorker )( error ); @@ -383,7 +375,7 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( error ); + expect( err ).toEqual( error ); } ); } ); @@ -392,7 +384,7 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; const error = new Error( 'Example error from a worker.' ); getExitCallback( firstWorker )( 0 ); @@ -404,7 +396,7 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( error ); + expect( err ).toEqual( error ); } ); } ); @@ -413,20 +405,20 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( firstWorker.workerData ).to.be.an( 'object' ); - expect( firstWorker.workerData ).to.have.property( 'packages' ); - expect( firstWorker.workerData.packages ).to.be.an( 'array' ); - expect( firstWorker.workerData.packages ).to.deep.equal( [ + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'packages' ); + expect( firstWorker.workerData.packages ).toBeInstanceOf( Array ); + expect( firstWorker.workerData.packages ).toEqual( [ '/home/ckeditor/my-packages/package-01', '/home/ckeditor/my-packages/package-03' ] ); - expect( secondWorker.workerData ).to.be.an( 'object' ); - expect( secondWorker.workerData ).to.have.property( 'packages' ); - expect( secondWorker.workerData.packages ).to.be.an( 'array' ); - expect( secondWorker.workerData.packages ).to.deep.equal( [ + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'packages' ); + expect( secondWorker.workerData.packages ).toBeInstanceOf( Array ); + expect( secondWorker.workerData.packages ).toEqual( [ '/home/ckeditor/my-packages/package-02', '/home/ckeditor/my-packages/package-04' ] ); @@ -442,7 +434,7 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -450,15 +442,15 @@ describe( 'dev-release-tools/utils', () => { await promise; - expect( stubs.fs.unlink.callCount ).to.equal( 1 ); - expect( stubs.fs.unlink.firstCall.args[ 0 ] ).to.equal( '/home/ckeditor/uuid-4.cjs' ); + expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.js' ); } ); it( 'should remove the temporary module if the process is aborted', async () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; abortController.abort( 'SIGINT' ); @@ -468,15 +460,15 @@ describe( 'dev-release-tools/utils', () => { await promise; - expect( stubs.fs.unlink.callCount ).to.equal( 1 ); - expect( stubs.fs.unlink.firstCall.args[ 0 ] ).to.equal( '/home/ckeditor/uuid-4.cjs' ); + expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.js' ); } ); it( 'should remove the temporary module if the promise rejected', async () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker ] = WorkerMock.instances; + const [ firstWorker ] = stubs.WorkerMock.instances; const error = new Error( 'Example error from a worker.' ); getErrorCallback( firstWorker )( error ); @@ -487,8 +479,8 @@ describe( 'dev-release-tools/utils', () => { throw new Error( 'Expected to be rejected.' ); }, () => { - expect( stubs.fs.unlink.callCount ).to.equal( 1 ); - expect( stubs.fs.unlink.firstCall.args[ 0 ] ).to.equal( '/home/ckeditor/uuid-4.cjs' ); + expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.js' ); } ); } ); @@ -497,7 +489,7 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; abortController.abort( 'SIGINT' ); @@ -507,18 +499,18 @@ describe( 'dev-release-tools/utils', () => { await promise; - expect( firstWorker.terminate.callCount ).to.equal( 1 ); - expect( secondWorker.terminate.callCount ).to.equal( 1 ); + expect( firstWorker.terminate ).toHaveBeenCalledTimes( 1 ); + expect( secondWorker.terminate ).toHaveBeenCalledTimes( 1 ); } ); it( 'should attach listener to a worker that executes a callback once per worker', async () => { - const signalEvent = sinon.stub( abortController.signal, 'addEventListener' ); + const signalEvent = vi.spyOn( abortController.signal, 'addEventListener' ); const promise = executeInParallel( defaultOptions ); await delay( 0 ); - expect( stubs.abortController.registerAbortController.callCount ).to.equal( 0 ); + expect( registerAbortController ).toHaveBeenCalledTimes( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; abortController.abort( 'SIGINT' ); @@ -528,26 +520,32 @@ describe( 'dev-release-tools/utils', () => { await promise; - expect( signalEvent.callCount ).to.equal( 2 ); - - expect( signalEvent.firstCall.args[ 0 ] ).to.equal( 'abort' ); - expect( signalEvent.firstCall.args[ 1 ] ).to.be.a( 'function' ); - expect( signalEvent.firstCall.args[ 2 ] ).to.be.an( 'object' ); - expect( signalEvent.firstCall.args[ 2 ] ).to.have.property( 'once', true ); - - expect( signalEvent.secondCall.args[ 0 ] ).to.equal( 'abort' ); - expect( signalEvent.secondCall.args[ 1 ] ).to.be.a( 'function' ); - expect( signalEvent.secondCall.args[ 2 ] ).to.be.an( 'object' ); - expect( signalEvent.secondCall.args[ 2 ] ).to.have.property( 'once', true ); + expect( signalEvent ).toHaveBeenCalledTimes( 2 ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 1, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 2, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); - expect( stubs.abortController.deregisterAbortController.callCount ).to.equal( 0 ); + expect( deregisterAbortController ).toHaveBeenCalledTimes( 0 ); } ); it( 'should register and deregister default abort controller if signal is not provided', async () => { const abortController = new AbortController(); - const signalEvent = sinon.stub( abortController.signal, 'addEventListener' ); + const signalEvent = vi.spyOn( abortController.signal, 'addEventListener' ); - stubs.abortController.registerAbortController.returns( abortController ); + registerAbortController.mockReturnValue( abortController ); const options = Object.assign( {}, defaultOptions ); delete options.signal; @@ -555,9 +553,9 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( options ); await delay( 0 ); - expect( stubs.abortController.registerAbortController.callCount ).to.equal( 1 ); + expect( registerAbortController ).toHaveBeenCalledTimes( 1 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; abortController.abort( 'SIGINT' ); @@ -567,43 +565,49 @@ describe( 'dev-release-tools/utils', () => { await promise; - expect( signalEvent.callCount ).to.equal( 2 ); - - expect( signalEvent.firstCall.args[ 0 ] ).to.equal( 'abort' ); - expect( signalEvent.firstCall.args[ 1 ] ).to.be.a( 'function' ); - expect( signalEvent.firstCall.args[ 2 ] ).to.be.an( 'object' ); - expect( signalEvent.firstCall.args[ 2 ] ).to.have.property( 'once', true ); - - expect( signalEvent.secondCall.args[ 0 ] ).to.equal( 'abort' ); - expect( signalEvent.secondCall.args[ 1 ] ).to.be.a( 'function' ); - expect( signalEvent.secondCall.args[ 2 ] ).to.be.an( 'object' ); - expect( signalEvent.secondCall.args[ 2 ] ).to.have.property( 'once', true ); + expect( signalEvent ).toHaveBeenCalledTimes( 2 ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 1, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 2, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); - expect( stubs.abortController.deregisterAbortController.callCount ).to.equal( 1 ); + expect( deregisterAbortController ).toHaveBeenCalledTimes( 1 ); } ); it( 'should update the progress when a package finished executing the callback', async () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; const firstWorkerPackageDone = getMessageCallback( firstWorker ); const secondWorkerPackageDone = getMessageCallback( secondWorker ); - expect( outputHistory ).to.lengthOf( 0 ); + expect( outputHistory ).toHaveLength( 0 ); firstWorkerPackageDone( 'done:package' ); - expect( outputHistory ).to.include( 'Status: 1/4.' ); - expect( outputHistory ).to.lengthOf( 1 ); + expect( outputHistory ).toContain( 'Status: 1/4.' ); + expect( outputHistory ).toHaveLength( 1 ); secondWorkerPackageDone( 'done:package' ); - expect( outputHistory ).to.include( 'Status: 2/4.' ); - expect( outputHistory ).to.lengthOf( 2 ); + expect( outputHistory ).toContain( 'Status: 2/4.' ); + expect( outputHistory ).toHaveLength( 2 ); secondWorkerPackageDone( 'done:package' ); - expect( outputHistory ).to.lengthOf( 3 ); - expect( outputHistory ).to.include( 'Status: 3/4.' ); + expect( outputHistory ).toHaveLength( 3 ); + expect( outputHistory ).toContain( 'Status: 3/4.' ); firstWorkerPackageDone( 'done:package' ); - expect( outputHistory ).to.lengthOf( 4 ); - expect( outputHistory ).to.include( 'Status: 4/4.' ); + expect( outputHistory ).toHaveLength( 4 ); + expect( outputHistory ).toContain( 'Status: 4/4.' ); // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -616,12 +620,12 @@ describe( 'dev-release-tools/utils', () => { const promise = executeInParallel( defaultOptions ); await delay( 0 ); - const [ firstWorker, secondWorker ] = WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; const firstWorkerPackageDone = getMessageCallback( firstWorker ); - expect( outputHistory ).to.lengthOf( 0 ); + expect( outputHistory ).toHaveLength( 0 ); firstWorkerPackageDone( 'foo' ); - expect( outputHistory ).to.lengthOf( 0 ); + expect( outputHistory ).toHaveLength( 0 ); // Workers did not emit an error. getExitCallback( firstWorker )( 0 ); @@ -637,8 +641,8 @@ function delay( time ) { } function getExitCallback( fakeWorker ) { - for ( const call of fakeWorker.on.getCalls() ) { - const [ eventName, callback ] = call.args; + for ( const call of fakeWorker.on.mock.calls ) { + const [ eventName, callback ] = call; if ( eventName === 'exit' ) { return callback; @@ -647,8 +651,8 @@ function getExitCallback( fakeWorker ) { } function getMessageCallback( fakeWorker ) { - for ( const call of fakeWorker.on.getCalls() ) { - const [ eventName, callback ] = call.args; + for ( const call of fakeWorker.on.mock.calls ) { + const [ eventName, callback ] = call; if ( eventName === 'message' ) { return callback; @@ -657,8 +661,8 @@ function getMessageCallback( fakeWorker ) { } function getErrorCallback( fakeWorker ) { - for ( const call of fakeWorker.on.getCalls() ) { - const [ eventName, callback ] = call.args; + for ( const call of fakeWorker.on.mock.calls ) { + const [ eventName, callback ] = call; if ( eventName === 'error' ) { return callback; From 626fdf8259cc20d61ae3868e0c3b7c0134da5c68 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 14:29:42 +0200 Subject: [PATCH 061/172] Tests for displaySkippedPackages. --- .../lib/utils/displayskippedpackages.js | 2 +- .../tests/utils/displayskippedpackages.js | 102 ++++++++++-------- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js b/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js index d493bf586..490262323 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displayskippedpackages.js @@ -13,7 +13,7 @@ import { CLI_INDENT_SIZE } from './constants.js'; * * @param {Set} skippedPackagesPaths */ -export function displaySkippedPackages( skippedPackagesPaths ) { +export default function displaySkippedPackages( skippedPackagesPaths ) { if ( !skippedPackagesPaths.size ) { return; } diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/displayskippedpackages.js b/packages/ckeditor5-dev-release-tools/tests/utils/displayskippedpackages.js index 523c4ddf0..b15ca9d06 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/displayskippedpackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/displayskippedpackages.js @@ -3,63 +3,71 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import chalk from 'chalk'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import getPackageJson from '../../lib/utils/getpackagejson.js'; +import displaySkippedPackages from '../../lib/utils/displayskippedpackages.js'; -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +const stubs = vi.hoisted( () => { + const values = { + logger: { + info: vi.fn() + }, + chalk: { + bold: vi.fn( input => input ), + underline: vi.fn( input => input ) + } + }; -describe( 'dev-release-tools/utils', () => { - let displaySkippedPackages, sandbox, stubs; + // To make `chalk.bold.yellow.red()` working. + for ( const rootKey of Object.keys( values.chalk ) ) { + for ( const nestedKey of Object.keys( values.chalk ) ) { + values.chalk[ rootKey ][ nestedKey ] = values.chalk[ nestedKey ]; + } + } - beforeEach( () => { - sandbox = sinon.createSandbox(); + return values; +} ); - stubs = { - logger: { - info: sandbox.stub(), - warning: sandbox.stub(), - error: sandbox.stub() - }, - getPackageJson: sandbox.stub() - }; +vi.mock( 'chalk', () => ( { + default: stubs.chalk +} ) ); +vi.mock( '@ckeditor/ckeditor5-dev-utils', () => ( { + logger: vi.fn( () => stubs.logger ) +} ) ); +vi.mock( '../../lib/utils/constants.js', () => ( { + CLI_INDENT_SIZE: 1 +} ) ); +vi.mock( '../../lib/utils/getpackagejson.js' ); - displaySkippedPackages = proxyquire( '../../lib/utils/displayskippedpackages', { - '@ckeditor/ckeditor5-dev-utils': { - logger() { - return stubs.logger; - } - }, - './getpackagejson': stubs.getPackageJson - } ); - } ); +describe( 'displaySkippedPackages()', () => { + it( 'displays name of packages that have been skipped', () => { + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-foo' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-bar' } ); - afterEach( () => { - sandbox.restore(); - } ); + displaySkippedPackages( new Set( [ + '/packages/ckeditor5-foo', + '/packages/ckeditor5-bar' + ] ) ); - describe( 'displaySkippedPackages()', () => { - it( 'displays name of packages that have been skipped', () => { - stubs.getPackageJson.onFirstCall().returns( { name: '@ckeditor/ckeditor5-foo' } ); - stubs.getPackageJson.onSecondCall().returns( { name: '@ckeditor/ckeditor5-bar' } ); + expect( vi.mocked( logger ) ).toHaveBeenCalledOnce(); + expect( stubs.logger.info ).toHaveBeenCalledOnce(); - displaySkippedPackages( new Set( [ - '/packages/ckeditor5-foo', - '/packages/ckeditor5-bar' - ] ) ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const logMessage = firstArgument.split( '\n' ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); + expect( logMessage[ 0 ].includes( 'Packages listed below have been skipped:' ) ).to.equal( true ); + expect( logMessage[ 1 ].includes( ' * @ckeditor/ckeditor5-foo' ) ).to.equal( true ); + expect( logMessage[ 2 ].includes( ' * @ckeditor/ckeditor5-bar' ) ).to.equal( true ); - const logMessage = stubs.logger.info.firstCall.args[ 0 ].split( '\n' ); - - expect( logMessage[ 0 ].includes( 'Packages listed below have been skipped:' ) ).to.equal( true ); - expect( logMessage[ 1 ].includes( ' * @ckeditor/ckeditor5-foo' ) ).to.equal( true ); - expect( logMessage[ 2 ].includes( ' * @ckeditor/ckeditor5-bar' ) ).to.equal( true ); - } ); + expect( vi.mocked( chalk ).underline ).toHaveBeenCalledOnce(); + } ); - it( 'does not display if given list is empty', () => { - displaySkippedPackages( new Set() ); - expect( stubs.logger.info.calledOnce ).to.equal( false ); - } ); + it( 'does not display if given list is empty', () => { + displaySkippedPackages( new Set() ); + expect( stubs.logger.info ).not.toHaveBeenCalledOnce(); } ); } ); From d91fcac3a3321dc8868266ad93aa01c3a80815fc Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 14:33:45 +0200 Subject: [PATCH 062/172] Make "executeInParallel()" tests green again. --- .../ckeditor5-dev-release-tools/lib/utils/executeinparallel.js | 2 +- .../tests/utils/executeinparallel.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js index d7084080d..13f87841e 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js @@ -13,7 +13,7 @@ import { Worker } from 'worker_threads'; import { glob } from 'glob'; import { registerAbortController, deregisterAbortController } from './abortcontroller.js'; -const WORKER_SCRIPT = new URL( './abortcontroller.js', import.meta.url ); +const WORKER_SCRIPT = new URL( './parallelworker.js', import.meta.url ); /** * This util allows executing a specified task in parallel using Workers. It can be helpful when executing a not resource-consuming diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js index 067e7bddd..e7f0e3934 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js @@ -23,7 +23,7 @@ const stubs = vi.hoisted( () => ( { this.on = vi.fn(); this.terminate = vi.fn(); - expect( script.endsWith( 'parallelworker.js' ) ).toEqual( true ); + expect( script.toString().endsWith( 'parallelworker.js' ) ).toEqual( true ); } } } ) ); From 4ba08cbd2a4ea06b710ef0dc0b57bcff97ce1a94 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 10 Sep 2024 15:11:12 +0200 Subject: [PATCH 063/172] Make all tests green (almost). --- .../lib/tasks/reassignnpmtags.js | 2 +- .../lib/utils/executeinparallel.js | 2 +- .../lib/utils/getchangedfilesforcommit.js | 2 +- .../lib/utils/getpackagespaths.js | 4 ++-- .../lib/utils/parallelworker.js | 2 +- .../lib/utils/transformcommitfactory.js | 4 ++-- .../lib/utils/transformcommitutils.js | 2 +- .../utils/executeinparallel-integration.js | 2 +- .../tests/utils/executeinparallel.js | 18 +++++++++--------- .../tests/utils/getchangedfilesforcommit.js | 2 +- .../tests/utils/getpackagespaths.js | 4 ++-- .../tests/utils/transformcommitfactory.js | 10 ++-------- .../tests/utils/transformcommitutils.js | 2 +- .../vitest.config.js | 2 +- 14 files changed, 26 insertions(+), 32 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js index 2a0715a1a..a1d0c7309 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js @@ -10,7 +10,7 @@ import columns from 'cli-columns'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; import util from 'util'; import shellEscape from 'shell-escape'; -import assertNpmAuthorization from '../utils/assertnpmauthorization'; +import assertNpmAuthorization from '../utils/assertnpmauthorization.js'; import { exec } from 'child_process'; const execPromise = util.promisify( exec ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js index 13f87841e..77e49cd79 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js @@ -60,7 +60,7 @@ export default async function executeInParallel( options ) { const packagesInThreads = getPackagesGroupedByThreads( packagesToProcess, concurrency ); - const callbackModule = upath.join( cwd, crypto.randomUUID() + '.js' ); + const callbackModule = upath.join( cwd, crypto.randomUUID() + '.mjs' ); await fs.writeFile( callbackModule, `export default ${ taskToExecute };`, 'utf-8' ); const onPackageDone = progressFactory( listrTask, packagesToProcess.length ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js index 3688edfc4..c6089876b 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js @@ -11,7 +11,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; * @param {String} commitId * @returns {Array.} */ -export function getChangedFilesForCommit( commitId ) { +export default function getChangedFilesForCommit( commitId ) { const gitCommand = `git log -m -1 --name-only --pretty="format:" ${ commitId }`; const changedFiles = tools.shExec( gitCommand, { verbosity: 'error' } ).trim(); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js index 1348ac913..2530f55df 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js @@ -6,7 +6,7 @@ import path from 'path'; import minimatch from 'minimatch'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import { getPackageJson } from './getpackagejson.js'; +import getPackageJson from './getpackagejson.js'; /** * Returns an object with two collections of paths to packages which are located in single repository. @@ -26,7 +26,7 @@ import { getPackageJson } from './getpackagejson.js'; * @param {Boolean} [options.skipMainRepository=false] If set on true, package found in `options.cwd` will be skipped. * @returns {PathsCollection} */ -export function getPackagesPaths( options ) { +export default function getPackagesPaths( options ) { const pathsCollection = { matched: new Set(), skipped: new Set() diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js index 3e0771763..07b816fbf 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js @@ -12,7 +12,7 @@ * @param {Array.} packages */ const { parentPort, workerData } = await import( 'worker_threads' ); - const callback = await import( workerData.callbackModule ); + const { default: callback } = await import( workerData.callbackModule ); for ( const packagePath of workerData.packages ) { await callback( packagePath, workerData.taskOptions ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js index 01e5c8b21..56e8b256c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js @@ -5,7 +5,7 @@ import { cloneDeepWith } from 'lodash'; import * as utils from './transformcommitutils.js'; -import { getChangedFilesForCommit } from './getchangedfilesforcommit.js'; +import getChangedFilesForCommit from './getchangedfilesforcommit.js'; // Squash commit follows the pattern: "A pull request title (#{number})". const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; @@ -29,7 +29,7 @@ const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; * as "BREAKING CHANGES". * @returns {TransformCommit} */ -export function transformCommitFactory( options = {} ) { +export default function transformCommitFactory( options = {} ) { return rawCommit => { const commit = transformCommit( rawCommit ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js index ee0ef2860..c76d26e74 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import { getPackageJson } from './getpackagejson.js'; +import getPackageJson from './getpackagejson.js'; /** * A regexp for extracting additional changelog entries from the single commit. diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js index 3acb9d60a..961260650 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import { describe, it, expect, beforeEach } from 'vitest'; +import { beforeEach, describe, expect, it } from 'vitest'; import fs from 'fs'; import path from 'path'; import { glob } from 'glob'; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js index e7f0e3934..121d1ad90 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js @@ -100,16 +100,16 @@ describe( 'dev-release-tools/utils', () => { expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); expect( fs.writeFile ).toHaveBeenCalledWith( - '/home/ckeditor/uuid-4.js', + '/home/ckeditor/uuid-4.mjs', 'export default packagePath => console.log( \'pwd\', packagePath );', 'utf-8' ); expect( firstWorker.workerData ).toBeInstanceOf( Object ); - expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.js' ); + expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.mjs' ); expect( firstWorker.workerData ).toHaveProperty( 'packages' ); expect( secondWorker.workerData ).toBeInstanceOf( Object ); - expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.js' ); + expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.mjs' ); expect( secondWorker.workerData ).toHaveProperty( 'packages' ); // Workers did not emit an error. @@ -292,7 +292,7 @@ describe( 'dev-release-tools/utils', () => { expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); expect( fs.writeFile ).toHaveBeenCalledWith( - 'C:/Users/ckeditor/uuid-4.js', + 'C:/Users/ckeditor/uuid-4.mjs', 'export default packagePath => console.log( \'pwd\', packagePath );', 'utf-8' ); @@ -303,11 +303,11 @@ describe( 'dev-release-tools/utils', () => { const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; expect( firstWorker.workerData ).toBeInstanceOf( Object ); - expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.js' ); + expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.mjs' ); expect( firstWorker.workerData ).toHaveProperty( 'packages' ); expect( secondWorker.workerData ).toBeInstanceOf( Object ); - expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.js' ); + expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.mjs' ); expect( secondWorker.workerData ).toHaveProperty( 'packages' ); // Workers did not emit an error. @@ -443,7 +443,7 @@ describe( 'dev-release-tools/utils', () => { await promise; expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); - expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.js' ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); } ); it( 'should remove the temporary module if the process is aborted', async () => { @@ -461,7 +461,7 @@ describe( 'dev-release-tools/utils', () => { await promise; expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); - expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.js' ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); } ); it( 'should remove the temporary module if the promise rejected', async () => { @@ -480,7 +480,7 @@ describe( 'dev-release-tools/utils', () => { }, () => { expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); - expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.js' ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js index 6b4d4c21e..b11900e08 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js @@ -7,7 +7,7 @@ import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from import fs from 'fs'; import path from 'path'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import { getChangedFilesForCommit } from '../../lib/utils/getchangedfilesforcommit.js'; +import getChangedFilesForCommit from '../../lib/utils/getchangedfilesforcommit.js'; describe( 'dev-release-tools/utils', () => { let tmpCwd, cwd; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js index 13313f55e..30a87387d 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js @@ -5,8 +5,8 @@ import { describe, it, expect, vi } from 'vitest'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import { getPackageJson } from '../../lib/utils/getpackagejson.js'; -import { getPackagesPaths } from '../../lib/utils/getpackagespaths.js'; +import getPackageJson from '../../lib/utils/getpackagejson.js'; +import getPackagesPaths from '../../lib/utils/getpackagespaths.js'; vi.mock( 'path', () => ( { default: { diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js index 6dec93802..0714003ff 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js @@ -4,16 +4,10 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { getChangedFilesForCommit } from '../../lib/utils/getchangedfilesforcommit.js'; - -import { transformCommitFactory } from '../../lib/utils/transformcommitfactory.js'; +import getChangedFilesForCommit from '../../lib/utils/getchangedfilesforcommit.js'; +import transformCommitFactory from '../../lib/utils/transformcommitfactory.js'; vi.mock( '../../lib/utils/getchangedfilesforcommit.js' ); -vi.mock( '../../lib/utils/getpackagejson.js', () => ( { - getPackageJson: vi.fn( () => ( { - repository: 'https://github.com/ckeditor/ckeditor5-dev' - } ) ) -} ) ); describe( 'dev-release-tools/utils', () => { describe( 'transformCommitFactory()', () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js index 8382d1560..e597b447c 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { getPackageJson } from '../../lib/utils/getpackagejson.js'; +import getPackageJson from '../../lib/utils/getpackagejson.js'; import * as transformCommitUtils from '../../lib/utils/transformcommitutils.js'; diff --git a/packages/ckeditor5-dev-release-tools/vitest.config.js b/packages/ckeditor5-dev-release-tools/vitest.config.js index 17bbb4835..83a5070b1 100644 --- a/packages/ckeditor5-dev-release-tools/vitest.config.js +++ b/packages/ckeditor5-dev-release-tools/vitest.config.js @@ -17,7 +17,7 @@ export default defineConfig( { 'tests/**/*.js' ], exclude: [ - './tests/_utils/**/*.j' + './tests/_utils/**/*.js' ], coverage: { provider: 'v8', From 86fe4dda867bf35bda0ef089646f197f51c308ed Mon Sep 17 00:00:00 2001 From: Marcin Panek Date: Tue, 10 Sep 2024 16:15:46 +0200 Subject: [PATCH 064/172] Converted `ckeditor5-dev-transtaltions` to ESM. --- .eslintrc.js | 1 + .../lib/ckeditortranslationsplugin.js | 12 +- .../lib/cleanpofilecontent.js | 8 +- .../lib/createdictionaryfrompofilecontent.js | 8 +- .../lib/findmessages.js | 10 +- .../ckeditor5-dev-translations/lib/index.js | 14 +- .../lib/multiplelanguagetranslationservice.js | 17 +- .../lib/servetranslations.js | 16 +- .../lib/translatesourceloader.js | 6 +- .../ckeditor5-dev-translations/package.json | 10 +- .../tests/ckeditortranslationsplugin.js | 148 +++++++----------- .../tests/cleanpofilecontent.js | 7 +- .../createdictionaryfrompofilecontent.js | 7 +- .../tests/findmessages.js | 13 +- .../multiplelanguagetranslationservice.js | 110 ++++++------- .../tests/translatesourceloader.js | 29 ++-- .../vitest.config.js | 23 +++ 17 files changed, 193 insertions(+), 246 deletions(-) create mode 100644 packages/ckeditor5-dev-translations/vitest.config.js diff --git a/.eslintrc.js b/.eslintrc.js index d84357e75..269261160 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { { files: [ // TODO: add packages as they are migrated to ESM. + './packages/ckeditor5-dev-translations/**/*', './packages/ckeditor5-dev-stale-bot/**/*', './packages/ckeditor5-dev-ci/**/*', './packages/ckeditor5-dev-docs/**/*' diff --git a/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js b/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js index 5c9d01471..70c6b92cf 100644 --- a/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js +++ b/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const serveTranslations = require( './servetranslations' ); -const MultipleLanguageTranslationService = require( './multiplelanguagetranslationservice' ); +import chalk from 'chalk'; +import serveTranslations from './servetranslations.js'; +import MultipleLanguageTranslationService from './multiplelanguagetranslationservice.js'; /** * CKEditorTranslationsPlugin, for now, consists only of the translation mechanism (@ckeditor/ckeditor5#624, @ckeditor/ckeditor5#387, @@ -29,7 +27,7 @@ const MultipleLanguageTranslationService = require( './multiplelanguagetranslati * This plugin tries to clean the output translation directory before each build to make sure, that all translations are correct. * See https://github.com/ckeditor/ckeditor5/issues/700 for more information. */ -module.exports = class CKEditorTranslationsPlugin { +export default class CKEditorTranslationsPlugin { /** * @param {CKEditorTranslationsPluginOptions} options Plugin options. */ @@ -102,7 +100,7 @@ module.exports = class CKEditorTranslationsPlugin { serveTranslations( compiler, this.options, translationService ); } -}; +} /** * @callback AssetNamesFilter diff --git a/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js b/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js index 3d2761c4f..0bf291c8b 100644 --- a/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js +++ b/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const PO = require( 'pofile' ); +import PO from 'pofile'; /** * Returns translations stripped from the personal data, but with an added banner @@ -16,7 +14,7 @@ const PO = require( 'pofile' ); * @param {Boolean} [options.simplifyLicenseHeader] Whether to skip adding the contribute URL in the header. * @returns {String} */ -module.exports = function cleanPoFileContent( poFileContent, options = {} ) { +export default function cleanPoFileContent( poFileContent, options = {} ) { const po = PO.parse( poFileContent ); // Remove personal data from headers. @@ -53,4 +51,4 @@ module.exports = function cleanPoFileContent( poFileContent, options = {} ) { } return po.toString(); -}; +} diff --git a/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js b/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js index c828e2095..4d0cfb944 100644 --- a/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js +++ b/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const PO = require( 'pofile' ); +import PO from 'pofile'; /** * Returns object with key-value pairs from parsed po file. @@ -13,7 +11,7 @@ const PO = require( 'pofile' ); * @param {String} poFileContent Content of the translation file. * @returns {Object.} */ -module.exports = function createDictionaryFromPoFileContent( poFileContent ) { +export default function createDictionaryFromPoFileContent( poFileContent ) { const po = PO.parse( poFileContent ); const keys = {}; @@ -26,4 +24,4 @@ module.exports = function createDictionaryFromPoFileContent( poFileContent ) { } return keys; -}; +} diff --git a/packages/ckeditor5-dev-translations/lib/findmessages.js b/packages/ckeditor5-dev-translations/lib/findmessages.js index a926427dc..631bbed1b 100644 --- a/packages/ckeditor5-dev-translations/lib/findmessages.js +++ b/packages/ckeditor5-dev-translations/lib/findmessages.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const parser = require( '@babel/parser' ); -const traverse = require( '@babel/traverse' ).default; +import parser from '@babel/parser'; +import traverse from '@babel/traverse'; /** * Parses source and finds messages from the first argument of `t()` calls. @@ -17,7 +15,7 @@ const traverse = require( '@babel/traverse' ).default; * @param {(err: string) => void} onErrorFound * @returns {String} Transformed source. */ -module.exports = function findMessages( source, sourceFile, onMessageFound, onErrorFound ) { +export default function findMessages( source, sourceFile, onMessageFound, onErrorFound ) { const ast = parser.parse( source, { sourceType: 'module', ranges: true, @@ -111,7 +109,7 @@ module.exports = function findMessages( source, sourceFile, onMessageFound, onEr `First t() call argument should be a string literal or an object literal (${ sourceFile }).` ); } -}; +} // Get property from the list of properties // It supports both forms: `{ propertyName: foo }` and `{ 'propertyName': 'foo' }` diff --git a/packages/ckeditor5-dev-translations/lib/index.js b/packages/ckeditor5-dev-translations/lib/index.js index b42e10c11..20e0293da 100644 --- a/packages/ckeditor5-dev-translations/lib/index.js +++ b/packages/ckeditor5-dev-translations/lib/index.js @@ -3,15 +3,13 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import findMessages from './findmessages.js'; +import cleanPoFileContent from './cleanpofilecontent.js'; +import MultipleLanguageTranslationService from './multiplelanguagetranslationservice.js'; +import createDictionaryFromPoFileContent from './createdictionaryfrompofilecontent.js'; +import CKEditorTranslationsPlugin from './ckeditortranslationsplugin.js'; -const findMessages = require( './findmessages' ); -const cleanPoFileContent = require( './cleanpofilecontent' ); -const MultipleLanguageTranslationService = require( './multiplelanguagetranslationservice' ); -const createDictionaryFromPoFileContent = require( './createdictionaryfrompofilecontent' ); -const CKEditorTranslationsPlugin = require( './ckeditortranslationsplugin' ); - -module.exports = { +export default { findMessages, cleanPoFileContent, MultipleLanguageTranslationService, diff --git a/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js b/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js index b800b7074..3fba89e99 100644 --- a/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js +++ b/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js @@ -3,18 +3,17 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const fs = require( 'fs' ); -const findMessages = require( './findmessages' ); -const { EventEmitter } = require( 'events' ); -const PO = require( 'pofile' ); +import path from 'path'; +import fs from 'fs'; +import findMessages from './findmessages.js'; +import { EventEmitter } from 'events'; +import PO from 'pofile'; /** * A service that serves translations assets based on the found PO files in the registered packages. */ -module.exports = class MultipleLanguageTranslationService extends EventEmitter { +export default class MultipleLanguageTranslationService extends EventEmitter { + // TODO maybe fix the jsdoc types /** * @param {Object} options * @param {String} options.mainLanguage The target language that will be bundled into the main webpack asset. @@ -418,7 +417,7 @@ module.exports = class MultipleLanguageTranslationService extends EventEmitter { return path.join( 'lang', 'translations' ); } -}; +} /** * @param {String|Function|RegExp} predicate diff --git a/packages/ckeditor5-dev-translations/lib/servetranslations.js b/packages/ckeditor5-dev-translations/lib/servetranslations.js index 30ad1ba6c..515780292 100644 --- a/packages/ckeditor5-dev-translations/lib/servetranslations.js +++ b/packages/ckeditor5-dev-translations/lib/servetranslations.js @@ -3,13 +3,11 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const rimraf = require( 'rimraf' ); -const fs = require( 'fs' ); -const path = require( 'path' ); -const { RawSource, ConcatSource } = require( 'webpack-sources' ); +import chalk from 'chalk'; +import rimraf from 'rimraf'; +import fs from 'fs'; +import path from 'path'; +import { RawSource, ConcatSource } from 'webpack-sources'; /** * Serve translations depending on the used translation service and passed options. @@ -29,7 +27,7 @@ const { RawSource, ConcatSource } = require( 'webpack-sources' ); * @param {TranslationService} translationService Translation service that will load PO files, replace translation keys and generate assets. * ckeditor5 - independent without hard-to-test logic. */ -module.exports = function serveTranslations( compiler, options, translationService ) { +export default function serveTranslations( compiler, options, translationService ) { const cwd = process.cwd(); // A set of unique messages that prevents message duplications. @@ -176,7 +174,7 @@ module.exports = function serveTranslations( compiler, options, translationServi console.warn( chalk.yellow( `[CKEditorTranslationsPlugin] Warning: ${ warning }` ) ); } } -}; +} /** * Return path to the package if the resource comes from `ckeditor5-*` package. diff --git a/packages/ckeditor5-dev-translations/lib/translatesourceloader.js b/packages/ckeditor5-dev-translations/lib/translatesourceloader.js index 56b47911a..3c92b4f5a 100644 --- a/packages/ckeditor5-dev-translations/lib/translatesourceloader.js +++ b/packages/ckeditor5-dev-translations/lib/translatesourceloader.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Very simple loader that runs the translateSource function only on the source. * translateSource is provided by the CKEditorTranslationsPlugin. @@ -12,8 +10,8 @@ * @param {String} source Content of the resource file * @param {Object} map A source map consumed by the `source-map` package. */ -module.exports = function translateSourceLoader( source, map ) { +export default function translateSourceLoader( source, map ) { const output = this.query.translateSource( source, this.resourcePath ); this.callback( null, output, map ); -}; +} diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index 8f7f9735f..f70a06f44 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -17,6 +17,7 @@ "npm": ">=5.7.1" }, "main": "lib/index.js", + "type": "module", "files": [ "lib" ], @@ -29,13 +30,10 @@ "pofile": "^1.0.9" }, "devDependencies": { - "chai": "^4.2.0", - "mocha": "^7.1.2", - "proxyquire": "^2.1.3", - "sinon": "^9.2.4" + "vitest": "^2.0.5" }, "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.js --coverage" } } diff --git a/packages/ckeditor5-dev-translations/tests/ckeditortranslationsplugin.js b/packages/ckeditor5-dev-translations/tests/ckeditortranslationsplugin.js index fab4df076..e4f14ef1e 100644 --- a/packages/ckeditor5-dev-translations/tests/ckeditortranslationsplugin.js +++ b/packages/ckeditor5-dev-translations/tests/ckeditortranslationsplugin.js @@ -3,35 +3,15 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import CKEditorTranslationsPlugin from '../lib/ckeditortranslationsplugin.js'; +import serveTranslations from '../lib/servetranslations.js'; +import MultipleLanguageTranslationService from '../lib/multiplelanguagetranslationservice.js'; -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +vi.mock( '../lib/servetranslations' ); +vi.mock( '../lib/multiplelanguagetranslationservice', () => ( { default: vi.fn() } ) ); describe( 'dev-translations/CKEditorTranslationsPlugin', () => { - let sandbox, CKEditorTranslationsPlugin, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - serveTranslations: sandbox.stub().returns( {} ), - MultipleLanguageTranslationService: sandbox.stub().returns( {} ) - }; - - CKEditorTranslationsPlugin = proxyquire( '../lib/ckeditortranslationsplugin', { - './servetranslations': stubs.serveTranslations, - './multiplelanguagetranslationservice': stubs.MultipleLanguageTranslationService - } ); - - sandbox.stub( console, 'warn' ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - describe( 'constructor()', () => { it( 'should initialize with passed options', () => { const options = { language: 'pl' }; @@ -128,7 +108,7 @@ describe( 'dev-translations/CKEditorTranslationsPlugin', () => { const ckEditorTranslationsPlugin = new CKEditorTranslationsPlugin( options ); ckEditorTranslationsPlugin.apply( compiler ); - sinon.assert.calledOnce( stubs.serveTranslations ); + expect( serveTranslations ).toHaveBeenCalledOnce(); } ); describe( 'should create an instance of `MultipleLanguageTranslationService`', () => { @@ -140,21 +120,18 @@ describe( 'dev-translations/CKEditorTranslationsPlugin', () => { const ckEditorTranslationsPlugin = new CKEditorTranslationsPlugin( options ); ckEditorTranslationsPlugin.apply( {} ); - sinon.assert.calledOnce( stubs.serveTranslations ); - - sinon.assert.calledOnce( stubs.MultipleLanguageTranslationService ); - sinon.assert.calledWithExactly( - stubs.MultipleLanguageTranslationService, - { - mainLanguage: 'pl', - compileAllLanguages: false, - additionalLanguages: [], - buildAllTranslationsToSeparateFiles: false, - addMainLanguageTranslationsToAllAssets: false, - translationsOutputFile: undefined, - skipPluralFormFunction: false - } - ); + expect( serveTranslations ).toHaveBeenCalledOnce(); + + expect( MultipleLanguageTranslationService ).toHaveBeenCalledOnce(); + expect( MultipleLanguageTranslationService ).toHaveBeenCalledWith( expect.objectContaining( { + mainLanguage: 'pl', + compileAllLanguages: false, + additionalLanguages: [], + buildAllTranslationsToSeparateFiles: false, + addMainLanguageTranslationsToAllAssets: false, + translationsOutputFile: undefined, + skipPluralFormFunction: false + } ) ); } ); it( 'for additional languages provided', () => { @@ -166,24 +143,23 @@ describe( 'dev-translations/CKEditorTranslationsPlugin', () => { const ckEditorTranslationsPlugin = new CKEditorTranslationsPlugin( options ); ckEditorTranslationsPlugin.apply( {} ); - sinon.assert.calledOnce( stubs.serveTranslations ); - - sinon.assert.calledOnce( stubs.MultipleLanguageTranslationService ); - sinon.assert.calledWithExactly( - stubs.MultipleLanguageTranslationService, - { - mainLanguage: 'pl', - compileAllLanguages: false, - additionalLanguages: [ 'en' ], - buildAllTranslationsToSeparateFiles: false, - addMainLanguageTranslationsToAllAssets: false, - translationsOutputFile: undefined, - skipPluralFormFunction: false - } - ); + expect( serveTranslations ).toHaveBeenCalledOnce(); + + expect( MultipleLanguageTranslationService ).toHaveBeenCalledOnce(); + expect( MultipleLanguageTranslationService ).toHaveBeenCalledWith( expect.objectContaining( { + mainLanguage: 'pl', + compileAllLanguages: false, + additionalLanguages: [ 'en' ], + buildAllTranslationsToSeparateFiles: false, + addMainLanguageTranslationsToAllAssets: false, + translationsOutputFile: undefined, + skipPluralFormFunction: false + } ) ); } ); it( 'for `additionalLanguages` set to `all`', () => { + const consoleWarnSpy = vi.spyOn( console, 'warn' ); + const options = { language: 'en', additionalLanguages: 'all' @@ -192,23 +168,20 @@ describe( 'dev-translations/CKEditorTranslationsPlugin', () => { const ckEditorTranslationsPlugin = new CKEditorTranslationsPlugin( options ); ckEditorTranslationsPlugin.apply( {} ); - sinon.assert.calledOnce( stubs.serveTranslations ); - - sinon.assert.calledOnce( stubs.MultipleLanguageTranslationService ); - sinon.assert.calledWithExactly( - stubs.MultipleLanguageTranslationService, - { - mainLanguage: 'en', - compileAllLanguages: true, - additionalLanguages: [], - buildAllTranslationsToSeparateFiles: false, - addMainLanguageTranslationsToAllAssets: false, - translationsOutputFile: undefined, - skipPluralFormFunction: false - } - ); - - sinon.assert.notCalled( console.warn ); + expect( serveTranslations ).toHaveBeenCalledOnce(); + + expect( MultipleLanguageTranslationService ).toHaveBeenCalledOnce(); + expect( MultipleLanguageTranslationService ).toHaveBeenCalledWith( expect.objectContaining( { + mainLanguage: 'en', + compileAllLanguages: true, + additionalLanguages: [], + buildAllTranslationsToSeparateFiles: false, + addMainLanguageTranslationsToAllAssets: false, + translationsOutputFile: undefined, + skipPluralFormFunction: false + } ) ); + + expect( consoleWarnSpy ).not.toBeCalled(); } ); it( 'passes the skipPluralFormFunction option to the translation service', () => { @@ -220,21 +193,18 @@ describe( 'dev-translations/CKEditorTranslationsPlugin', () => { const ckEditorTranslationsPlugin = new CKEditorTranslationsPlugin( options ); ckEditorTranslationsPlugin.apply( {} ); - sinon.assert.calledOnce( stubs.serveTranslations ); - - sinon.assert.calledOnce( stubs.MultipleLanguageTranslationService ); - sinon.assert.calledWithExactly( - stubs.MultipleLanguageTranslationService, - { - mainLanguage: 'pl', - compileAllLanguages: false, - additionalLanguages: [], - buildAllTranslationsToSeparateFiles: false, - addMainLanguageTranslationsToAllAssets: false, - translationsOutputFile: undefined, - skipPluralFormFunction: true - } - ); + expect( serveTranslations ).toHaveBeenCalledOnce(); + + expect( MultipleLanguageTranslationService ).toHaveBeenCalledOnce(); + expect( MultipleLanguageTranslationService ).toHaveBeenCalledWith( expect.objectContaining( { + mainLanguage: 'pl', + compileAllLanguages: false, + additionalLanguages: [], + buildAllTranslationsToSeparateFiles: false, + addMainLanguageTranslationsToAllAssets: false, + translationsOutputFile: undefined, + skipPluralFormFunction: true + } ) ); } ); } ); diff --git a/packages/ckeditor5-dev-translations/tests/cleanpofilecontent.js b/packages/ckeditor5-dev-translations/tests/cleanpofilecontent.js index c7e02f95b..77a7d3774 100644 --- a/packages/ckeditor5-dev-translations/tests/cleanpofilecontent.js +++ b/packages/ckeditor5-dev-translations/tests/cleanpofilecontent.js @@ -3,11 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const cleanPoFileContent = require( '../lib/cleanpofilecontent' ); +import { describe, expect, it } from 'vitest'; +import cleanPoFileContent from '../lib/cleanpofilecontent.js'; describe( 'translations', () => { describe( 'cleanPoFileContent()', () => { diff --git a/packages/ckeditor5-dev-translations/tests/createdictionaryfrompofilecontent.js b/packages/ckeditor5-dev-translations/tests/createdictionaryfrompofilecontent.js index 050d552f5..2e243e57e 100644 --- a/packages/ckeditor5-dev-translations/tests/createdictionaryfrompofilecontent.js +++ b/packages/ckeditor5-dev-translations/tests/createdictionaryfrompofilecontent.js @@ -3,11 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const createDictionaryFromPoFileContent = require( '../lib/createdictionaryfrompofilecontent' ); +import { describe, expect, it } from 'vitest'; +import createDictionaryFromPoFileContent from '../lib/createdictionaryfrompofilecontent.js'; describe( 'translations', () => { describe( 'parsePoFileContent()', () => { diff --git a/packages/ckeditor5-dev-translations/tests/findmessages.js b/packages/ckeditor5-dev-translations/tests/findmessages.js index 268fc4d22..b0c975df6 100644 --- a/packages/ckeditor5-dev-translations/tests/findmessages.js +++ b/packages/ckeditor5-dev-translations/tests/findmessages.js @@ -3,19 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const findMessages = require( '../lib/findmessages' ); +import { describe, expect, it } from 'vitest'; +import findMessages from '../lib/findmessages.js'; describe( 'findMessages', () => { - const sandbox = sinon.createSandbox(); - - afterEach( () => { - sandbox.restore(); - } ); - it( 'should parse provided code and find messages from `t()` function calls on string literals', () => { const messages = []; diff --git a/packages/ckeditor5-dev-translations/tests/multiplelanguagetranslationservice.js b/packages/ckeditor5-dev-translations/tests/multiplelanguagetranslationservice.js index 2a448c85c..a1489fc37 100644 --- a/packages/ckeditor5-dev-translations/tests/multiplelanguagetranslationservice.js +++ b/packages/ckeditor5-dev-translations/tests/multiplelanguagetranslationservice.js @@ -5,16 +5,22 @@ /* eslint-disable no-eval */ -'use strict'; - -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const path = require( 'path' ); -const proxyquire = require( 'proxyquire' ); +import { describe, expect, it, vi, beforeEach } from 'vitest'; +import path from 'path'; +import fs from 'fs'; +import MultipleLanguageTranslationService from '../lib/multiplelanguagetranslationservice.js'; + +vi.mock( 'fs', () => ( { + default: { + existsSync: vi.fn(), + readFileSync: vi.fn(), + readdirSync: vi.fn() + } +} ) ); describe( 'translations', () => { describe( 'MultipleLanguageTranslationService', () => { - let MultipleLanguageTranslationService, stubs, filesAndDirs, fileContents, dirContents; + let filesAndDirs, fileContents, dirContents; let window; beforeEach( () => { @@ -22,25 +28,13 @@ describe( 'translations', () => { fileContents = {}; dirContents = {}; - stubs = { - fs: { - existsSync: path => filesAndDirs.includes( path ), - readFileSync: path => fileContents[ path ], - readdirSync: dir => dirContents[ dir ] - } - }; - - MultipleLanguageTranslationService = proxyquire( '../lib/multiplelanguagetranslationservice', { - 'fs': stubs.fs - } ); + vi.mocked( fs.existsSync ).mockImplementation( path => filesAndDirs.includes( path ) ); + vi.mocked( fs.readFileSync ).mockImplementation( path => fileContents[ path ] ); + vi.mocked( fs.readdirSync ).mockImplementation( dir => dirContents[ dir ] ); window = {}; } ); - afterEach( () => { - sinon.restore(); - } ); - describe( 'constructor()', () => { it( 'should initialize `SingleLanguageTranslationService`', () => { const translationService = new MultipleLanguageTranslationService( { mainLanguage: 'en', additionalLanguages: [ 'pl' ] } ); @@ -95,7 +89,7 @@ describe( 'translations', () => { it( 'should load PO file from the package only once per language', () => { const translationService = new MultipleLanguageTranslationService( { mainLanguage: 'pl', additionalLanguages: [ 'de' ] } ); - const loadPoFileSpy = sinon.stub( translationService, '_loadPoFile' ); + const loadPoFileSpy = vi.spyOn( translationService, '_loadPoFile' ); const pathToTranslationsDirectory = path.join( 'pathToPackage', 'lang', 'translations' ); @@ -105,7 +99,7 @@ describe( 'translations', () => { translationService.loadPackage( 'pathToPackage' ); translationService.loadPackage( 'pathToPackage' ); - sinon.assert.calledTwice( loadPoFileSpy ); + expect( loadPoFileSpy ).toBeCalledTimes( 2 ); } ); it( 'should load all PO files for the current package and add languages to the language list', () => { @@ -447,7 +441,7 @@ describe( 'translations', () => { additionalLanguages: [ 'xxx' ] } ); - const spy = sinon.spy(); + const spy = vi.fn(); translationService.on( 'error', spy ); @@ -482,7 +476,7 @@ describe( 'translations', () => { additionalLanguages: [ 'xxx' ] } ); - const spy = sinon.spy(); + const spy = vi.fn(); translationService.on( 'error', spy ); @@ -525,7 +519,7 @@ describe( 'translations', () => { mainLanguage: 'pl', additionalLanguages: [ 'xxx' ] } ); - const spy = sinon.spy(); + const spy = vi.fn(); translationService.on( 'error', spy ); @@ -546,8 +540,8 @@ describe( 'translations', () => { compilationAssetNames: [ 'ckeditor.js' ] } ); - sinon.assert.calledOnce( spy ); - sinon.assert.calledWithExactly( spy, 'No translation has been found for the xxx language.' ); + expect( spy ).toHaveBeenCalledOnce(); + expect( spy ).toBeCalledWith( 'No translation has been found for the xxx language.' ); } ); it( 'should emit an error if translations for the main language are missing', () => { @@ -556,7 +550,7 @@ describe( 'translations', () => { additionalLanguages: [ 'pl' ] } ); - const errorSpy = sinon.spy(); + const errorSpy = vi.fn(); translationService.on( 'error', errorSpy ); @@ -577,8 +571,8 @@ describe( 'translations', () => { compilationAssetNames: [ 'ckeditor.js' ] } ); - sinon.assert.calledOnce( errorSpy ); - sinon.assert.calledWithExactly( errorSpy, 'No translation has been found for the xxx language.' ); + expect( errorSpy ).toHaveBeenCalledOnce(); + expect( errorSpy ).toBeCalledWith( 'No translation has been found for the xxx language.' ); } ); it( 'should emit a warning if the translation is missing', () => { @@ -586,7 +580,7 @@ describe( 'translations', () => { mainLanguage: 'pl', additionalLanguages: [] } ); - const warningSpy = sinon.spy(); + const warningSpy = vi.fn(); translationService.on( 'warning', warningSpy ); @@ -608,13 +602,13 @@ describe( 'translations', () => { compilationAssetNames: [ 'ckeditor.js' ] } ); - sinon.assert.calledOnce( warningSpy ); - sinon.assert.calledWithExactly( warningSpy, 'A translation is missing for \'Save\' in the \'pl\' language.' ); + expect( warningSpy ).toHaveBeenCalledOnce(); + expect( warningSpy ).toBeCalledWith( 'A translation is missing for \'Save\' in the \'pl\' language.' ); } ); it( 'should emit an error when there are multiple JS assets', () => { const translationService = new MultipleLanguageTranslationService( { mainLanguage: 'pl', additionalLanguages: [] } ); - const errorSpy = sinon.spy(); + const errorSpy = vi.fn(); translationService.on( 'error', errorSpy ); @@ -635,8 +629,8 @@ describe( 'translations', () => { compilationAssetNames: [ 'ckeditor.js', 'ckeditor1.js' ] } ); - sinon.assert.calledOnce( errorSpy ); - expect( errorSpy.getCalls()[ 0 ] ).to.match( + expect( errorSpy ).toHaveBeenCalledOnce(); + expect( errorSpy.mock.calls[ 0 ] ).to.match( /Too many JS assets has been found during the compilation./ ); @@ -652,7 +646,7 @@ describe( 'translations', () => { addMainLanguageTranslationsToAllAssets: true } ); - const errorSpy = sinon.spy(); + const errorSpy = vi.fn(); translationService.on( 'error', errorSpy ); @@ -673,7 +667,7 @@ describe( 'translations', () => { compilationAssetNames: [ 'foo.js', 'bar.js' ] } ); - sinon.assert.notCalled( errorSpy ); + expect( errorSpy ).not.toHaveBeenCalled(); expect( assets ).to.have.length( 2 ); @@ -691,7 +685,7 @@ describe( 'translations', () => { buildAllTranslationsToSeparateFiles: true } ); - const errorSpy = sinon.spy(); + const errorSpy = vi.fn(); translationService.on( 'error', errorSpy ); @@ -712,7 +706,7 @@ describe( 'translations', () => { compilationAssetNames: [] } ); - sinon.assert.notCalled( errorSpy ); + expect( errorSpy ).not.toHaveBeenCalled(); expect( assets ).to.have.length( 0 ); } ); @@ -724,8 +718,8 @@ describe( 'translations', () => { translationsOutputFile: 'foo/bar' } ); - const errorSpy = sinon.spy(); - const warningSpy = sinon.spy(); + const errorSpy = vi.fn(); + const warningSpy = vi.fn(); translationService.on( 'error', errorSpy ); translationService.on( 'warning', warningSpy ); @@ -757,8 +751,8 @@ describe( 'translations', () => { expect( assets[ 0 ] ).to.have.property( 'outputBody' ); expect( assets[ 0 ].outputBody ).to.have.length.greaterThan( 0 ); - expect( warningSpy.called ).to.equal( false ); - expect( errorSpy.called ).to.equal( false ); + expect( warningSpy ).not.toHaveBeenCalled(); + expect( errorSpy ).not.toHaveBeenCalled(); } ); it( 'should emit all files to a file specified by the `translationsOutputFile` option when it is specified (as regexp)', () => { @@ -768,8 +762,8 @@ describe( 'translations', () => { translationsOutputFile: /app\.js/ } ); - const errorSpy = sinon.spy(); - const warningSpy = sinon.spy(); + const errorSpy = vi.fn(); + const warningSpy = vi.fn(); translationService.on( 'error', errorSpy ); translationService.on( 'warning', warningSpy ); @@ -802,8 +796,8 @@ describe( 'translations', () => { expect( assets[ 0 ] ).to.have.property( 'outputBody' ); expect( assets[ 0 ].outputBody ).to.have.length.greaterThan( 0 ); - expect( warningSpy.called ).to.equal( false ); - expect( errorSpy.called ).to.equal( false ); + expect( warningSpy ).not.toHaveBeenCalled(); + expect( errorSpy ).not.toHaveBeenCalled(); } ); it( 'should emit all files to a file specified by the `translationsOutputFile` option when it is specified (as func.)', () => { @@ -813,8 +807,8 @@ describe( 'translations', () => { translationsOutputFile: name => /app\.js/.test( name ) } ); - const errorSpy = sinon.spy(); - const warningSpy = sinon.spy(); + const errorSpy = vi.fn(); + const warningSpy = vi.fn(); translationService.on( 'error', errorSpy ); translationService.on( 'warning', warningSpy ); @@ -847,8 +841,8 @@ describe( 'translations', () => { expect( assets[ 0 ] ).to.have.property( 'outputBody' ); expect( assets[ 0 ].outputBody ).to.have.length.greaterThan( 0 ); - expect( warningSpy.called ).to.equal( false ); - expect( errorSpy.called ).to.equal( false ); + expect( warningSpy ).not.toHaveBeenCalled(); + expect( errorSpy ).not.toHaveBeenCalled(); } ); it( 'should use the `outputDirectory` option for translation assets generated as new files', () => { @@ -857,8 +851,8 @@ describe( 'translations', () => { additionalLanguages: [ 'en' ] } ); - const warningSpy = sinon.spy(); - const errorSpy = sinon.spy(); + const warningSpy = vi.fn(); + const errorSpy = vi.fn(); translationService.on( 'warning', warningSpy ); translationService.on( 'error', errorSpy ); @@ -886,8 +880,8 @@ describe( 'translations', () => { expect( assets[ 0 ].outputPath ).to.equal( 'ckeditor.js' ); expect( assets[ 1 ].outputPath ).to.equal( path.join( 'custom-lang-path', 'en.js' ) ); - expect( warningSpy.called ).to.equal( false ); - expect( errorSpy.called ).to.equal( false ); + expect( warningSpy ).not.toHaveBeenCalled(); + expect( errorSpy ).not.toHaveBeenCalled(); } ); it( diff --git a/packages/ckeditor5-dev-translations/tests/translatesourceloader.js b/packages/ckeditor5-dev-translations/tests/translatesourceloader.js index 3a055b2ef..1b277bf1e 100644 --- a/packages/ckeditor5-dev-translations/tests/translatesourceloader.js +++ b/packages/ckeditor5-dev-translations/tests/translatesourceloader.js @@ -3,37 +3,28 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const translateSourceLoader = require( '../lib/translatesourceloader' ); +import { describe, expect, it, vi } from 'vitest'; +import translateSourceLoader from '../lib/translatesourceloader.js'; describe( 'dev-translations/translateSourceLoader()', () => { - const sandbox = sinon.createSandbox(); - - afterEach( () => { - sandbox.restore(); - } ); - it( 'should return translated code', () => { const ctx = { query: { - translateSource: sandbox.spy( () => 'output' ) + translateSource: vi.fn( () => 'output' ) }, resourcePath: 'file.js', - callback: sinon.stub() + callback: vi.fn() }; const map = {}; translateSourceLoader.call( ctx, 'Source', map ); - sinon.assert.calledOnce( ctx.query.translateSource ); - sinon.assert.calledWithExactly( ctx.query.translateSource, 'Source', 'file.js' ); + expect( ctx.query.translateSource ).toHaveBeenCalledOnce(); + expect( ctx.query.translateSource ).toHaveBeenCalledWith( 'Source', 'file.js' ); - expect( ctx.callback.calledOnce ).to.equal( true ); - expect( ctx.callback.firstCall.args[ 0 ] ).to.equal( null ); - expect( ctx.callback.firstCall.args[ 1 ] ).to.equal( 'output' ); - expect( ctx.callback.firstCall.args[ 2 ] ).to.equal( map ); + expect( ctx.callback ).toHaveBeenCalledOnce(); + expect( ctx.callback.mock.calls[ 0 ][ 0 ] ).to.equal( null ); + expect( ctx.callback.mock.calls[ 0 ][ 1 ] ).to.equal( 'output' ); + expect( ctx.callback.mock.calls[ 0 ][ 2 ] ).to.equal( map ); } ); } ); diff --git a/packages/ckeditor5-dev-translations/vitest.config.js b/packages/ckeditor5-dev-translations/vitest.config.js new file mode 100644 index 000000000..5ad784a28 --- /dev/null +++ b/packages/ckeditor5-dev-translations/vitest.config.js @@ -0,0 +1,23 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + testTimeout: 10000, + restoreMocks: true, + include: [ + 'tests/**/*.js' + ], + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From eea371744b62113419b7ad680cf5a1959becb140 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 08:58:33 +0200 Subject: [PATCH 065/172] Replaced an integration test with a unit one for a worker script. --- .../lib/utils/parallelworker.js | 3 +- .../utils/executeinparallel-integration.js | 117 ------------------ .../tests/utils/parallelworker.js | 48 +++++++ .../vitest.config.js | 22 ++++ 4 files changed, 72 insertions(+), 118 deletions(-) delete mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js create mode 100644 packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js index 07b816fbf..0f74809fb 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js @@ -5,13 +5,14 @@ // This file is covered by the "executeInParallel() - integration" test cases. +import { parentPort, workerData } from 'worker_threads'; + // Required due to top-level await. ( async () => { /** * @param {String} callbackModule * @param {Array.} packages */ - const { parentPort, workerData } = await import( 'worker_threads' ); const { default: callback } = await import( workerData.callbackModule ); for ( const packagePath of workerData.packages ) { diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js deleted file mode 100644 index 961260650..000000000 --- a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel-integration.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import { beforeEach, describe, expect, it } from 'vitest'; -import fs from 'fs'; -import path from 'path'; -import { glob } from 'glob'; -import executeInParallel from '../../lib/utils/executeinparallel.js'; - -const REPOSITORY_ROOT = path.join( __dirname, '..', '..', '..', '..' ); - -// This file covers the "parallelworker.js" file. - -describe( 'dev-release-tools/utils', () => { - let abortController; - - beforeEach( () => { - abortController = new AbortController(); - } ); - - describe( 'executeInParallel() - integration', () => { - it( 'should store current time in all found packages', async () => { - const timeBefore = new Date().getTime(); - - await executeInParallel( { - cwd: REPOSITORY_ROOT, - concurrency: 2, - packagesDirectory: 'packages', - signal: abortController.signal, - taskToExecute: async packagePath => { - const fs = await import( 'fs/promises' ); - const path = await import( 'path' ); - const filePath = path.join( packagePath, 'executeinparallel-integration.log' ); - - fs.writeFile( filePath, new Date().getTime().toString() ); - }, - listrTask: { - output: '' - } - } ); - - const timeAfter = new Date().getTime(); - - const data = glob.sync( 'packages/*/executeinparallel-integration.log', { cwd: REPOSITORY_ROOT, absolute: true } ) - .map( logFile => { - return { - source: logFile, - value: parseInt( fs.readFileSync( logFile, 'utf-8' ) ), - packageName: logFile.split( '/' ).reverse().slice( 1, 2 ).pop() - }; - } ); - - for ( const { value, packageName, source } of data ) { - expect( value > timeBefore, `comparing timeBefore (${ packageName })` ).toEqual( true ); - expect( value < timeAfter, `comparing timeAfter (${ packageName })` ).toEqual( true ); - - fs.unlinkSync( source ); - } - } ); - - it( 'should pass task options to the worker', async () => { - await executeInParallel( { - cwd: REPOSITORY_ROOT, - concurrency: 2, - packagesDirectory: 'packages', - signal: abortController.signal, - taskToExecute: async ( packagePath, taskOptions ) => { - const fs = await import( 'fs/promises' ); - const path = await import( 'path' ); - const filePath = path.join( packagePath, 'executeinparallel-integration.log' ); - - await fs.writeFile( filePath, JSON.stringify( taskOptions ) ); - }, - taskOptions: { - property: 'Example of the property.', - some: { - deeply: { - nested: { - property: 'Example the deeply nested property.' - } - } - } - }, - listrTask: { - output: '' - } - } ); - - const data = glob.sync( 'packages/*/executeinparallel-integration.log', { cwd: REPOSITORY_ROOT, absolute: true } ) - .map( logFile => { - return { - source: logFile, - value: JSON.parse( fs.readFileSync( logFile, 'utf-8' ) ), - packageName: logFile.split( '/' ).reverse().slice( 1, 2 ).pop() - }; - } ); - - for ( const { value, packageName, source } of data ) { - expect( value, `comparing taskOptions (${ packageName })` ).toEqual( { - property: 'Example of the property.', - some: { - deeply: { - nested: { - property: 'Example the deeply nested property.' - } - } - } - } ); - - fs.unlinkSync( source ); - } - } ); - } ); -} ); - diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js b/packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js new file mode 100644 index 000000000..f5781bfcb --- /dev/null +++ b/packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js @@ -0,0 +1,48 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, it, expect, vi } from 'vitest'; +import { parentPort } from 'worker_threads'; +import virtual from 'virtual:parallelworker-integration-module'; + +const taskOptions = vi.hoisted( () => ( { + aNumber: 1, + bBoolean: false, + cString: 'foo' +} ) ); + +vi.mock( 'virtual:parallelworker-integration-module' ); +vi.mock( 'worker_threads', () => ( { + parentPort: { + postMessage: vi.fn() + }, + workerData: { + callbackModule: 'virtual:parallelworker-integration-module', + packages: [ + '/home/ckeditor/packages/a', + '/home/ckeditor/packages/b' + ], + taskOptions + } +} ) ); + +describe( 'parallelWorker (worker defined in executeInParallel())', () => { + it( 'should execute a module from specified path and pass a package path and task options as arguments', async () => { + await import( '../../lib/utils/parallelworker.js' ); + + // It's needed because `parallelworker` does not export anything. Instead, it processes + // an asynchronous loop. We must wait until the current JavaScript loop ends. Adding a new promise at the end + // forces it. + await new Promise( resolve => { + setTimeout( resolve, 0 ); + } ); + + expect( vi.mocked( parentPort ).postMessage ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( parentPort ).postMessage ).toHaveBeenCalledWith( 'done:package' ); + expect( vi.mocked( virtual ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( virtual ) ).toHaveBeenCalledWith( '/home/ckeditor/packages/a', taskOptions ); + expect( vi.mocked( virtual ) ).toHaveBeenCalledWith( '/home/ckeditor/packages/b', taskOptions ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/vitest.config.js b/packages/ckeditor5-dev-release-tools/vitest.config.js index 83a5070b1..ee575ac4e 100644 --- a/packages/ckeditor5-dev-release-tools/vitest.config.js +++ b/packages/ckeditor5-dev-release-tools/vitest.config.js @@ -6,6 +6,9 @@ import { defineConfig } from 'vitest/config'; export default defineConfig( { + plugins: [ + executeInParallelVirtualModulePlugin() + ], test: { setupFiles: [ './tests/_utils/testsetup.js' @@ -29,3 +32,22 @@ export default defineConfig( { } } } ); + +function executeInParallelVirtualModulePlugin() { + const virtualModuleId = 'virtual:parallelworker-integration-module'; + const resolvedVirtualModuleId = '\0' + virtualModuleId; + + return { + name: 'execute-in-parallel-virtual-module-plugin', + resolveId( id ) { + if ( id === virtualModuleId ) { + return resolvedVirtualModuleId; + } + }, + load( id ) { + if ( id === resolvedVirtualModuleId ) { + return 'export default function virtualModule() {}'; + } + } + }; +} From 2cb19d563f81c2ce4664d228ccf6e67461298d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Wed, 11 Sep 2024 07:38:40 +0200 Subject: [PATCH 066/172] Do not redeclare already mocked method. --- .../tests/tasks/preparerepository.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js index bd7317204..f2bd4bb61 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/preparerepository.js @@ -87,7 +87,7 @@ describe( 'prepareRepository()', () => { it( 'should normalize Windows slashes "\\" from "process.cwd()"', async () => { vi.mocked( fs ).readdir.mockResolvedValue( [] ); - vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\windows\\working\\dir' ); + process.cwd.mockReturnValue( 'C:\\windows\\working\\dir' ); options.packagesDirectory = 'packages'; await prepareRepository( options ); From 7982e071225949da9ba0036c48cd8f9934488cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Wed, 11 Sep 2024 07:17:41 +0200 Subject: [PATCH 067/172] Aligned tests in `utils/displaycommits.js`. --- .../lib/utils/displaycommits.js | 2 +- .../tests/utils/displaycommits.js | 264 +++++++++++------- 2 files changed, 167 insertions(+), 99 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js index 9c24557a1..553a72b56 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js @@ -56,7 +56,7 @@ export function displayCommits( commits, options = {} ) { if ( hasCorrectType && isCommitIncluded ) { logMessage += chalk.green( 'INCLUDED' ); } else if ( hasCorrectType && !isCommitIncluded ) { - logMessage += chalk.grey( 'SKIPPED' ); + logMessage += chalk.gray( 'SKIPPED' ); } else { logMessage += chalk.red( 'INVALID' ); } diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js index c7b800396..690e43ba6 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js @@ -3,45 +3,53 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import { displayCommits } from '../../lib/utils/displaycommits.js'; + +const stubs = vi.hoisted( () => { + const values = { + logger: { + info: vi.fn() + }, + chalk: { + bold: vi.fn( input => input ), + italic: vi.fn( input => input ), + underline: vi.fn( input => input ), + gray: vi.fn( input => input ), + green: vi.fn( input => input ), + yellow: vi.fn( input => input ), + red: vi.fn( input => input ) + } + }; + + // To make `chalk.bold.yellow.red()` working. + for ( const rootKey of Object.keys( values.chalk ) ) { + for ( const nestedKey of Object.keys( values.chalk ) ) { + values.chalk[ rootKey ][ nestedKey ] = values.chalk[ nestedKey ]; + } + } + + return values; +} ); -const expect = require( 'chai' ).expect; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +vi.mock( 'chalk', () => ( { + default: stubs.chalk +} ) ); +vi.mock( '@ckeditor/ckeditor5-dev-utils', () => ( { + logger: vi.fn( () => stubs.logger ) +} ) ); describe( 'dev-release-tools/utils', () => { - let displayCommits, sandbox, stubs; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - logger: { - info: sandbox.spy(), - warning: sandbox.spy(), - error: sandbox.spy() - } - }; - - displayCommits = proxyquire( '../../lib/utils/displaycommits', { - '@ckeditor/ckeditor5-dev-utils': { - logger() { - return stubs.logger; - } - } - } ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - describe( 'displayCommits()', () => { it( 'prints if there is no commit to display', () => { displayCommits( [] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ] ).includes( 'No commits to display.' ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'No commits to display.' ); } ); it( 'attaches valid "external" commit to the changelog (as Array)', () => { @@ -58,9 +66,13 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'Fix: Simple fix.' ) ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'INCLUDED' ) ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'Fix: Simple fix.' ); + expect( firstArgument ).toContain( 'INCLUDED' ); } ); it( 'attaches valid "external" commit to the changelog (as Set)', () => { @@ -77,9 +89,13 @@ describe( 'dev-release-tools/utils', () => { displayCommits( new Set( [ commit ] ) ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'Fix: Simple fix.' ) ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'INCLUDED' ) ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'Fix: Simple fix.' ); + expect( firstArgument ).toContain( 'INCLUDED' ); } ); it( 'truncates too long commit\'s subject', () => { @@ -98,11 +114,15 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'Fix: Reference site about Lorem Ipsum, giving information on its origins, as well as a random Lip...' - ) ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'INCLUDED' ) ).to.equal( true ); + ); + expect( firstArgument ).toContain( 'INCLUDED' ); } ); it( 'does not attach valid "internal" commit to the changelog', () => { @@ -119,9 +139,13 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'Docs: README.' ) ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'SKIPPED' ) ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'Docs: README.' ); + expect( firstArgument ).toContain( 'SKIPPED' ); } ); it( 'does not attach invalid commit to the changelog', () => { @@ -137,9 +161,13 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'Invalid commit.' ) ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ].includes( 'INVALID' ) ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'Invalid commit.' ); + expect( firstArgument ).toContain( 'INVALID' ); } ); it( 'attaches additional subject for merge commits to the commit list', () => { @@ -158,16 +186,15 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); - expect( stubs.logger.info.firstCall.args[ 0 ] ).to.be.a( 'string' ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const logMessageAsArray = stubs.logger.info.firstCall.args[ 0 ].split( '\n' ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const logMessageAsArray = firstArgument.split( '\n' ); - expect( logMessageAsArray[ 0 ].includes( - 'Feature: Introduced a brand new release tools with a new set of requirements.' - ) ).to.equal( true ); - expect( logMessageAsArray[ 0 ].includes( 'INCLUDED' ) ).to.equal( true ); - expect( logMessageAsArray[ 1 ].includes( 'Merge pull request #75 from ckeditor/t/64' ) ).to.equal( true ); + expect( logMessageAsArray[ 0 ] ).toContain( 'Feature: Introduced a brand new release tools with a new set of requirements.' ); + expect( logMessageAsArray[ 0 ] ).toContain( 'INCLUDED' ); + expect( logMessageAsArray[ 1 ] ).toContain( 'Merge pull request #75 from ckeditor/t/64' ); } ); it( 'displays proper log if commit does not contain the second line', () => { @@ -192,11 +219,14 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + // The merge commit displays two lines: // Prefix: Changes. // Merge ... // If the merge commit does not contain the second line, it should display only the one. - expect( stubs.logger.info.firstCall.args[ 0 ].split( '\n' ) ).length( 1 ); + expect( firstArgument.split( '\n' ) ).toHaveLength( 1 ); } ); it( 'attaches breaking changes notes to displayed message', () => { @@ -229,13 +259,15 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - const message = stubs.logger.info.firstCall.args[ 0 ].split( '\n' ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const message = firstArgument.split( '\n' ); /* eslint-disable max-len */ - expect( message[ 0 ].includes( 'Feature: Simple foo.' ) ).to.equal( true ); - expect( message[ 1 ].includes( 'MAJOR BREAKING CHANGES: 1 - Reference site about Lorem Ipsum, giving information on its origins, as...' ) ).to.equal( true ); - expect( message[ 2 ].includes( 'MAJOR BREAKING CHANGES: 2 - Reference site about Lorem Ipsum, giving information on its origins, as...' ) ).to.equal( true ); - expect( message[ 3 ].includes( 'MINOR BREAKING CHANGES: 3 - Reference site about Lorem Ipsum, giving information on its origins, as...' ) ).to.equal( true ); + expect( message[ 0 ] ).toContain( 'Feature: Simple foo.' ); + expect( message[ 1 ] ).toContain( 'MAJOR BREAKING CHANGES: 1 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); + expect( message[ 2 ] ).toContain( 'MAJOR BREAKING CHANGES: 2 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); + expect( message[ 3 ] ).toContain( 'MINOR BREAKING CHANGES: 3 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); /* eslint-enable max-len */ } ); @@ -255,13 +287,15 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ], { attachLinkToCommit: true } ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const logMessage = stubs.logger.info.firstCall.args[ 0 ].split( '\n' ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const logMessage = firstArgument.split( '\n' ); - expect( logMessage[ 0 ].includes( 'Fix: Simple fix.' ) ).to.equal( true ); - expect( logMessage[ 0 ].includes( 'INCLUDED' ) ).to.equal( true ); - expect( logMessage[ 1 ].includes( 'https://github.com/ckeditor/ckeditor5-foo/commit/684997d' ) ).to.equal( true ); + expect( logMessage[ 0 ] ).toContain( 'Fix: Simple fix.' ); + expect( logMessage[ 0 ] ).toContain( 'INCLUDED' ); + expect( logMessage[ 1 ] ).toContain( 'https://github.com/ckeditor/ckeditor5-foo/commit/684997d' ); } ); } ); @@ -280,11 +314,12 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const logMessage = stubs.logger.info.firstCall.args[ 0 ]; + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; - expect( logMessage.substring( 0, 3 ) ).to.equal( ' ' ); + expect( firstArgument.substring( 0, 3 ) ).toEqual( ' ' ); } ); it( 'indents second line properly', () => { @@ -302,12 +337,14 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ] ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const [ firstLine, secondLine ] = stubs.logger.info.firstCall.args[ 0 ].split( '\n' ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstLine, secondLine ] = firstArgument.split( '\n' ); - expect( firstLine.substring( 0, 3 ) ).to.equal( ' '.repeat( 3 ) ); - expect( secondLine.substring( 0, 13 ) ).to.equal( ' '.repeat( 13 ) ); + expect( firstLine.substring( 0, 3 ) ).toEqual( ' '.repeat( 3 ) ); + expect( secondLine.substring( 0, 13 ) ).toEqual( ' '.repeat( 13 ) ); } ); it( 'works with "options.attachLinkToCommit"', () => { @@ -326,13 +363,15 @@ describe( 'dev-release-tools/utils', () => { displayCommits( [ commit ], { attachLinkToCommit: true, indentLevel: 2 } ); - expect( stubs.logger.info.calledOnce ).to.equal( true ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const [ firstLine, secondLine, thirdLine ] = stubs.logger.info.firstCall.args[ 0 ].split( '\n' ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const [ firstLine, secondLine, thirdLine ] = firstArgument.split( '\n' ); - expect( firstLine.substring( 0, 6 ) ).to.equal( ' '.repeat( 6 ) ); - expect( secondLine.substring( 0, 16 ) ).to.equal( ' '.repeat( 16 ) ); - expect( thirdLine.substring( 0, 16 ) ).to.equal( ' '.repeat( 16 ) ); + expect( firstLine.substring( 0, 6 ) ).toEqual( ' '.repeat( 6 ) ); + expect( secondLine.substring( 0, 16 ) ).toEqual( ' '.repeat( 16 ) ); + expect( thirdLine.substring( 0, 16 ) ).toEqual( ' '.repeat( 16 ) ); } ); } ); @@ -390,10 +429,15 @@ describe( 'dev-release-tools/utils', () => { } ] ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 6 ); + + const [ , secondCall, , , fifthCall ] = stubs.logger.info.mock.calls; + const [ secondCallfirstArgument ] = secondCall; + const [ fifthCallfirstArgument ] = fifthCall; + // Calls: 0, 2, 3, and 5 display the commit data. - expect( stubs.logger.info.callCount ).to.equal( 6 ); - expect( stubs.logger.info.getCall( 1 ).args[ 0 ] ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 4 ).args[ 0 ] ).to.match( /-----/ ); + expect( secondCallfirstArgument ).toMatch( /-----/ ); + expect( fifthCallfirstArgument ).toMatch( /-----/ ); } ); it( 'works for a group of two commits that follows a single commit group', () => { @@ -437,10 +481,15 @@ describe( 'dev-release-tools/utils', () => { } ] ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 5 ); + + const [ , secondCall, , , fifthCall ] = stubs.logger.info.mock.calls; + const [ secondCallfirstArgument ] = secondCall; + const [ fifthCallfirstArgument ] = fifthCall; + // Calls: 0, 2, and 3 display the commit data. - expect( stubs.logger.info.callCount ).to.equal( 5 ); - expect( stubs.logger.info.getCall( 1 ).args[ 0 ] ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 4 ).args[ 0 ] ).to.match( /-----/ ); + expect( secondCallfirstArgument ).toMatch( /-----/ ); + expect( fifthCallfirstArgument ).toMatch( /-----/ ); } ); it( 'works for a single commit group that follows group of two commits ', () => { @@ -485,10 +534,15 @@ describe( 'dev-release-tools/utils', () => { } ] ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 5 ); + + const [ firstCall, , , fourthCall ] = stubs.logger.info.mock.calls; + const [ firstCallfirstArgument ] = firstCall; + const [ fourthCallfirstArgument ] = fourthCall; + // Calls: 1, 2, and 4 display the commit data. - expect( stubs.logger.info.callCount ).to.equal( 5 ); - expect( stubs.logger.info.getCall( 0 ).args[ 0 ] ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 3 ).args[ 0 ] ).to.match( /-----/ ); + expect( firstCallfirstArgument ).toMatch( /-----/ ); + expect( fourthCallfirstArgument ).toMatch( /-----/ ); } ); it( 'does not duplicate the separator for commit groups', () => { @@ -545,12 +599,19 @@ describe( 'dev-release-tools/utils', () => { } ] ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 7 ); + + const [ firstCall, , , fourthCall, fifthCall, , seventhCall ] = stubs.logger.info.mock.calls; + const [ firstCallfirstArgument ] = firstCall; + const [ fourthCallfirstArgument ] = fourthCall; + const [ fifthCallfirstArgument ] = fifthCall; + const [ seventhCallfirstArgument ] = seventhCall; + // Calls: 1, 2, 4, and 5 display the commit data. - expect( stubs.logger.info.callCount ).to.equal( 7 ); - expect( stubs.logger.info.getCall( 0 ).args[ 0 ] ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 3 ).args[ 0 ] ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 4 ).args[ 0 ] ).to.not.match( /-----/ ); - expect( stubs.logger.info.getCall( 6 ).args[ 0 ] ).to.match( /-----/ ); + expect( firstCallfirstArgument ).toMatch( /-----/ ); + expect( fourthCallfirstArgument ).toMatch( /-----/ ); + expect( fifthCallfirstArgument ).not.toMatch( /-----/ ); + expect( seventhCallfirstArgument ).toMatch( /-----/ ); } ); it( 'groups two groups of commits separated by a single commit group', () => { @@ -619,13 +680,20 @@ describe( 'dev-release-tools/utils', () => { } ] ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 9 ); + + const [ firstCall, , , fourthCall, , sixthCall, , , ninethCall ] = stubs.logger.info.mock.calls; + const [ firstCallfirstArgument ] = firstCall; + const [ fourthCallfirstArgument ] = fourthCall; + const [ sixthCallfirstArgument ] = sixthCall; + const [ ninethCallfirstArgument ] = ninethCall; + // Calls: 1, 2, 4, 6, and 7 display the commit data. - expect( stubs.logger.info.callCount ).to.equal( 9 ); - expect( stubs.logger.info.getCall( 0 ).args[ 0 ] ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 3 ).args[ 0 ] ).to.match( /-----/ ); + expect( firstCallfirstArgument ).to.match( /-----/ ); + expect( fourthCallfirstArgument ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 5 ).args[ 0 ] ).to.match( /-----/ ); - expect( stubs.logger.info.getCall( 8 ).args[ 0 ] ).to.match( /-----/ ); + expect( sixthCallfirstArgument ).to.match( /-----/ ); + expect( ninethCallfirstArgument ).to.match( /-----/ ); } ); } ); } ); From ab28cdbace0a348fc9c268ddddd3f0c0d3af24ac Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 09:21:45 +0200 Subject: [PATCH 068/172] Fixed integration timeouts. --- .../ckeditor5-dev-release-tools/tests/utils/parallelworker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js b/packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js index f5781bfcb..75b0e42d1 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/parallelworker.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import { describe, it, expect, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { parentPort } from 'worker_threads'; import virtual from 'virtual:parallelworker-integration-module'; @@ -36,7 +36,7 @@ describe( 'parallelWorker (worker defined in executeInParallel())', () => { // an asynchronous loop. We must wait until the current JavaScript loop ends. Adding a new promise at the end // forces it. await new Promise( resolve => { - setTimeout( resolve, 0 ); + setTimeout( resolve, 100 ); } ); expect( vi.mocked( parentPort ).postMessage ).toHaveBeenCalledTimes( 2 ); From 30bafe50fe5fdd38a9477bb54c6813861353db94 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 09:27:53 +0200 Subject: [PATCH 069/172] Make coverage script works again. --- .gitignore | 3 +++ .../ckeditor5-dev-release-tools/tests/test-fixtures/.gitkeep | 0 .../tests/utils/getchangedfilesforcommit.js | 2 +- packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js | 2 +- packages/ckeditor5-dev-release-tools/vitest.config.js | 3 ++- 5 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 packages/ckeditor5-dev-release-tools/tests/test-fixtures/.gitkeep diff --git a/.gitignore b/.gitignore index 6050712d8..1b81873e8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ executeinparallel-integration.log # Compiled TS. packages/ckeditor5-dev-build-tools/dist + +packages/ckeditor5-dev-release-tools/tests/test-fixtures/** +!packages/ckeditor5-dev-release-tools/tests/test-fixtures/.gitkeep diff --git a/packages/ckeditor5-dev-release-tools/tests/test-fixtures/.gitkeep b/packages/ckeditor5-dev-release-tools/tests/test-fixtures/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js index b11900e08..8eb100753 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js @@ -15,7 +15,7 @@ describe( 'dev-release-tools/utils', () => { describe( 'getChangedFilesForCommit()', { timeout: 15000 }, function() { beforeAll( () => { cwd = process.cwd(); - tmpCwd = fs.mkdtempSync( __dirname + path.sep ); + tmpCwd = fs.mkdtempSync( path.join( __dirname, '..', 'test-fixtures' ) + path.sep ); process.chdir( tmpCwd ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 6ab46e827..72cd667ba 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -15,7 +15,7 @@ describe( 'dev-release-tools/utils', () => { describe( 'getCommits()', () => { beforeAll( () => { cwd = process.cwd(); - tmpCwd = fs.mkdtempSync( __dirname + path.sep ); + tmpCwd = fs.mkdtempSync( path.join( __dirname, '..', 'test-fixtures' ) + path.sep ); } ); afterAll( () => { diff --git a/packages/ckeditor5-dev-release-tools/vitest.config.js b/packages/ckeditor5-dev-release-tools/vitest.config.js index ee575ac4e..de04db7b6 100644 --- a/packages/ckeditor5-dev-release-tools/vitest.config.js +++ b/packages/ckeditor5-dev-release-tools/vitest.config.js @@ -20,7 +20,8 @@ export default defineConfig( { 'tests/**/*.js' ], exclude: [ - './tests/_utils/**/*.js' + './tests/_utils/**/*.js', + './tests/test-fixtures/**/*.js' ], coverage: { provider: 'v8', From 2cc0c8105c93434af093be0949fbc9104bcbfd35 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 09:32:38 +0200 Subject: [PATCH 070/172] Use default exports instead of named one. --- .../ckeditor5-dev-release-tools/lib/utils/displaycommits.js | 2 +- .../lib/utils/generatechangelog.js | 2 +- packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js | 2 +- .../lib/utils/getnewversiontype.js | 2 +- .../ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js | 2 +- .../ckeditor5-dev-release-tools/tests/utils/displaycommits.js | 2 +- .../tests/utils/generatechangelog.js | 4 ++-- .../ckeditor5-dev-release-tools/tests/utils/getcommits.js | 2 +- .../tests/utils/getnewversiontype.js | 2 +- .../tests/utils/getwriteroptions.js | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js index 553a72b56..dc7c59118 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js @@ -14,7 +14,7 @@ import { CLI_COMMIT_INDENT_SIZE, CLI_INDENT_SIZE } from './constants.js'; * @param {Boolean} [options.attachLinkToCommit=false] Whether to attach a link to parsed commit. * @param {Number} [options.indentLevel=1] The indent level. */ -export function displayCommits( commits, options = {} ) { +export default function displayCommits( commits, options = {} ) { const log = logger(); const attachLinkToCommit = options.attachLinkToCommit || false; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js index 99af19898..ce38601c3 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js @@ -36,7 +36,7 @@ const UPDATED_TRANSLATION_COMMIT = '* Updated translations.'; * * @returns {Promise.} */ -export function generateChangelog( commits, context, options ) { +export default function generateChangelog( commits, context, options ) { const commitStream = new Readable( { objectMode: true } ); /* istanbul ignore next */ commitStream._read = function() {}; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js index b58d488b8..b6fedbd83 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js @@ -20,7 +20,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; * @param {String} [options.mainBranch='master'] A name of the main branch in the repository. * @returns {Promise.>} */ -export function getCommits( transformCommit, options = {} ) { +export default function getCommits( transformCommit, options = {} ) { const releaseBranch = options.releaseBranch || 'master'; const mainBranch = options.mainBranch || 'master'; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js b/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js index 5f7490c43..cee74e04c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js @@ -9,7 +9,7 @@ * @param {Array.} commits * @returns {String|null} */ -export function getNewVersionType( commits ) { +export default function getNewVersionType( commits ) { // No commits = no changes. if ( !commits.length ) { return 'skip'; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js index d94a5b744..053008a79 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js @@ -13,7 +13,7 @@ const templatePath = path.join( __dirname, '..', 'templates' ); * @param {Function|Object} transform * @returns {Object} */ -export function getWriterOptions( transform ) { +export default function getWriterOptions( transform ) { return { transform, groupBy: 'type', diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js index 690e43ba6..5b902d46b 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js @@ -4,7 +4,7 @@ */ import { describe, expect, it, vi } from 'vitest'; -import { displayCommits } from '../../lib/utils/displaycommits.js'; +import displayCommits from '../../lib/utils/displaycommits.js'; const stubs = vi.hoisted( () => { const values = { diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js index 34c4928b9..a471a8350 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js @@ -5,8 +5,8 @@ import { describe, it, expect } from 'vitest'; import compareFunc from 'compare-func'; -import { getWriterOptions } from '../../lib/utils/getwriteroptions.js'; -import { generateChangelog } from '../../lib/utils/generatechangelog.js'; +import getWriterOptions from '../../lib/utils/getwriteroptions.js'; +import generateChangelog from '../../lib/utils/generatechangelog.js'; describe( 'dev-release-tools/utils', () => { const url = 'https://github.com/ckeditor/ckeditor5-package'; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 72cd667ba..11471f436 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -43,7 +43,7 @@ describe( 'dev-release-tools/utils', () => { devTools: ( await import( '@ckeditor/ckeditor5-dev-utils' ) ).tools }; - getCommits = ( await import( '../../lib/utils/getcommits.js' ) ).getCommits; + getCommits = ( await import( '../../lib/utils/getcommits.js' ) ).default; } ); afterEach( () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js b/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js index 194f1fbdc..b257ce66f 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js @@ -4,7 +4,7 @@ */ import { describe, it, expect } from 'vitest'; -import { getNewVersionType } from '../../lib/utils/getnewversiontype.js'; +import getNewVersionType from '../../lib/utils/getnewversiontype.js'; describe( 'dev-release-tools/utils', () => { describe( 'getSubPackagesPaths()', () => { diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js index a943aeac8..d724b791b 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js @@ -4,7 +4,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { getWriterOptions } from '../../lib/utils/getwriteroptions.js'; +import getWriterOptions from '../../lib/utils/getwriteroptions.js'; describe( 'dev-release-tools/utils', () => { describe( 'getWriterOptions()', () => { From 41f4ab7133e249562945d520c83b5eebc6e199a5 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 10:05:46 +0200 Subject: [PATCH 071/172] Minor fixes. --- .../ckeditor5-dev-release-tools/lib/utils/getformatteddate.js | 1 - .../tests/utils/transformcommitfactory.js | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js b/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js index fcd8fde29..6ed97df4e 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js @@ -11,4 +11,3 @@ import { format } from 'date-fns'; export default function getFormattedDate() { return format( new Date(), 'yyyy-MM-dd' ); } - diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js index 0714003ff..335fc1009 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js @@ -202,7 +202,6 @@ describe( 'dev-release-tools/utils', () => { const expectedFirstNoteText = 'See [ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1). ' + 'Thanks to [@CKEditor](https://github.com/CKEditor).'; - // eslint-disable-next-line max-len const expectedSecondNodeText = 'Read more [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2).'; expect( commit.notes[ 0 ].text ).to.equal( expectedFirstNoteText ); From 1f6c780561bd53c61d0c8a5dcdf2bcd6a0129f3c Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 11:44:00 +0200 Subject: [PATCH 072/172] Simplified the test descriptions. --- .../tests/utils/abortcontroller.js | 68 +- .../tests/utils/displaycommits.js | 1086 ++++--- .../tests/utils/executeinparallel.js | 844 +++--- .../tests/utils/generatechangelog.js | 1674 ++++++----- .../tests/utils/getchangedfilesforcommit.js | 286 +- .../tests/utils/getcommits.js | 500 ++-- .../tests/utils/getnewversiontype.js | 186 +- .../tests/utils/getnpmtagfromversion.js | 2 +- .../tests/utils/getpackagespaths.js | 494 ++-- .../tests/utils/getwriteroptions.js | 166 +- .../tests/utils/isversionpublishablefortag.js | 2 +- .../tests/utils/parseroptions.js | 8 +- .../utils/publishpackageonnpmcallback.js | 190 +- .../tests/utils/transformcommitfactory.js | 2552 ++++++++--------- .../tests/utils/transformcommitutils.js | 360 ++- .../utils/validaterepositorytorelease.js | 152 +- .../tests/utils/versions.js | 604 ++-- 17 files changed, 4576 insertions(+), 4598 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js b/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js index a64809755..dc53e7def 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/abortcontroller.js @@ -11,7 +11,7 @@ vi.stubGlobal( 'process', { removeListener: vi.fn() } ); -describe( 'abortController()', () => { +describe( 'abortcontroller', () => { let listeners; beforeEach( () => { @@ -30,51 +30,57 @@ describe( 'abortController()', () => { } ); } ); - it( 'should return AbortController instance', () => { - const abortControllerInstance = registerAbortController(); + describe( 'registerAbortController()', () => { + it( 'should return AbortController instance', () => { + const abortControllerInstance = registerAbortController(); - expect( abortControllerInstance ).to.be.instanceof( global.AbortController ); - } ); + expect( abortControllerInstance ).to.be.instanceof( global.AbortController ); + } ); - it( 'should store listener in internal property for further use', () => { - const abortControllerInstance = registerAbortController(); + it( 'should store listener in internal property for further use', () => { + const abortControllerInstance = registerAbortController(); - expect( abortControllerInstance._listener ).to.be.a( 'function' ); - } ); + expect( abortControllerInstance._listener ).to.be.a( 'function' ); + } ); - it( 'should register listener on SIGINT event', () => { - const abortControllerInstance = registerAbortController(); + it( 'should register listener on SIGINT event', () => { + const abortControllerInstance = registerAbortController(); - expect( vi.mocked( process ).prependOnceListener ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', abortControllerInstance._listener ); - } ); + expect( + vi.mocked( process ).prependOnceListener + ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', abortControllerInstance._listener ); + } ); - it( 'should call abort method on SIGINT event', () => { - const abortControllerInstance = registerAbortController(); + it( 'should call abort method on SIGINT event', () => { + const abortControllerInstance = registerAbortController(); - vi.spyOn( abortControllerInstance, 'abort' ); + vi.spyOn( abortControllerInstance, 'abort' ); - listeners.SIGINT.forEach( listener => listener() ); + listeners.SIGINT.forEach( listener => listener() ); - expect( abortControllerInstance.abort ).toHaveBeenCalledExactlyOnceWith( 'SIGINT' ); + expect( abortControllerInstance.abort ).toHaveBeenCalledExactlyOnceWith( 'SIGINT' ); + } ); } ); - it( 'should not deregister listener if AbortController instance is not set', () => { - deregisterAbortController(); - expect( vi.mocked( process ).removeListener ).not.toHaveBeenCalled(); - } ); + describe( 'deregisterAbortController()', () => { + it( 'should not deregister listener if AbortController instance is not set', () => { + deregisterAbortController(); + expect( vi.mocked( process ).removeListener ).not.toHaveBeenCalled(); + } ); - it( 'should not deregister listener if AbortController instance is not registered', () => { - const abortControllerInstance = new AbortController(); - deregisterAbortController( abortControllerInstance ); + it( 'should not deregister listener if AbortController instance is not registered', () => { + const abortControllerInstance = new AbortController(); + deregisterAbortController( abortControllerInstance ); - expect( vi.mocked( process ).removeListener ).not.toHaveBeenCalled(); - } ); + expect( vi.mocked( process ).removeListener ).not.toHaveBeenCalled(); + } ); - it( 'should deregister listener if AbortController instance is registered', () => { - const abortControllerInstance = registerAbortController(); + it( 'should deregister listener if AbortController instance is registered', () => { + const abortControllerInstance = registerAbortController(); - deregisterAbortController( abortControllerInstance ); + deregisterAbortController( abortControllerInstance ); - expect( vi.mocked( process ).removeListener ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', abortControllerInstance._listener ); + expect( vi.mocked( process ).removeListener ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', abortControllerInstance._listener ); + } ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js index 5b902d46b..a884622db 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/displaycommits.js @@ -39,124 +39,276 @@ vi.mock( '@ckeditor/ckeditor5-dev-utils', () => ( { logger: vi.fn( () => stubs.logger ) } ) ); -describe( 'dev-release-tools/utils', () => { - describe( 'displayCommits()', () => { - it( 'prints if there is no commit to display', () => { - displayCommits( [] ); +describe( 'displayCommits()', () => { + it( 'prints if there is no commit to display', () => { + displayCommits( [] ); - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; - expect( firstArgument ).toContain( 'No commits to display.' ); - } ); + expect( firstArgument ).toContain( 'No commits to display.' ); + } ); - it( 'attaches valid "external" commit to the changelog (as Array)', () => { - const commit = { - hash: '684997d', - header: 'Fix: Simple fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }; + it( 'attaches valid "external" commit to the changelog (as Array)', () => { + const commit = { + hash: '684997d', + header: 'Fix: Simple fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [] + }; - displayCommits( [ commit ] ); + displayCommits( [ commit ] ); - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; - expect( firstArgument ).toContain( 'Fix: Simple fix.' ); - expect( firstArgument ).toContain( 'INCLUDED' ); - } ); + expect( firstArgument ).toContain( 'Fix: Simple fix.' ); + expect( firstArgument ).toContain( 'INCLUDED' ); + } ); - it( 'attaches valid "external" commit to the changelog (as Set)', () => { - const commit = { - hash: '684997d', - header: 'Fix: Simple fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }; + it( 'attaches valid "external" commit to the changelog (as Set)', () => { + const commit = { + hash: '684997d', + header: 'Fix: Simple fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [] + }; - displayCommits( new Set( [ commit ] ) ); + displayCommits( new Set( [ commit ] ) ); - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; - expect( firstArgument ).toContain( 'Fix: Simple fix.' ); - expect( firstArgument ).toContain( 'INCLUDED' ); - } ); + expect( firstArgument ).toContain( 'Fix: Simple fix.' ); + expect( firstArgument ).toContain( 'INCLUDED' ); + } ); - it( 'truncates too long commit\'s subject', () => { - const commit = { - hash: '684997d', - header: 'Fix: Reference site about Lorem Ipsum, giving information on its origins, as well as ' + - 'a random Lipsum generator.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Reference site about Lorem Ipsum, giving information on its origins, as well as ' + - 'a random Lipsum generator.', - body: null, - footer: null, - notes: [] - }; + it( 'truncates too long commit\'s subject', () => { + const commit = { + hash: '684997d', + header: 'Fix: Reference site about Lorem Ipsum, giving information on its origins, as well as ' + + 'a random Lipsum generator.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Reference site about Lorem Ipsum, giving information on its origins, as well as ' + + 'a random Lipsum generator.', + body: null, + footer: null, + notes: [] + }; + + displayCommits( [ commit ] ); + + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( + 'Fix: Reference site about Lorem Ipsum, giving information on its origins, as well as a random Lip...' + ); + expect( firstArgument ).toContain( 'INCLUDED' ); + } ); - displayCommits( [ commit ] ); + it( 'does not attach valid "internal" commit to the changelog', () => { + const commit = { + hash: '684997d', + header: 'Docs: README.', + type: 'Docs', + rawType: 'Docs', + subject: 'README.', + body: null, + footer: null, + notes: [] + }; - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + displayCommits( [ commit ] ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - expect( firstArgument ).toContain( - 'Fix: Reference site about Lorem Ipsum, giving information on its origins, as well as a random Lip...' - ); - expect( firstArgument ).toContain( 'INCLUDED' ); - } ); + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'Docs: README.' ); + expect( firstArgument ).toContain( 'SKIPPED' ); + } ); + + it( 'does not attach invalid commit to the changelog', () => { + const commit = { + hash: '684997d', + header: 'Invalid commit.', + type: null, + subject: null, + body: null, + footer: null, + notes: [] + }; + + displayCommits( [ commit ] ); + + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); - it( 'does not attach valid "internal" commit to the changelog', () => { + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + expect( firstArgument ).toContain( 'Invalid commit.' ); + expect( firstArgument ).toContain( 'INVALID' ); + } ); + + it( 'attaches additional subject for merge commits to the commit list', () => { + const commit = { + merge: 'Merge pull request #75 from ckeditor/t/64', + hash: 'dea3501', + header: 'Feature: Introduced a brand new release tools with a new set of requirements.', + type: 'Feature', + rawType: 'Feature', + subject: 'Introduced a brand new release tools with a new set of requirements.', + body: null, + footer: null, + mentions: [], + notes: [] + }; + + displayCommits( [ commit ] ); + + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const logMessageAsArray = firstArgument.split( '\n' ); + + expect( logMessageAsArray[ 0 ] ).toContain( 'Feature: Introduced a brand new release tools with a new set of requirements.' ); + expect( logMessageAsArray[ 0 ] ).toContain( 'INCLUDED' ); + expect( logMessageAsArray[ 1 ] ).toContain( 'Merge pull request #75 from ckeditor/t/64' ); + } ); + + it( 'displays proper log if commit does not contain the second line', () => { + const commit = { + type: null, + subject: null, + merge: 'Merge branch \'master\' of github.com:ckeditor/ckeditor5-dev', + header: 'Merge branch \'master\' of github.com:ckeditor/ckeditor5-dev', + body: null, + footer: null, + notes: [], + references: [], + mentions: [], + revert: null, + rawType: undefined, + files: [], + scope: undefined, + isPublicCommit: false, + hash: 'a'.repeat( 40 ), + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' + }; + + displayCommits( [ commit ] ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + + // The merge commit displays two lines: + // Prefix: Changes. + // Merge ... + // If the merge commit does not contain the second line, it should display only the one. + expect( firstArgument.split( '\n' ) ).toHaveLength( 1 ); + } ); + + it( 'attaches breaking changes notes to displayed message', () => { + const commit = { + hash: '684997d', + header: 'Feature: Simple foo.', + type: 'Feature', + rawType: 'Feature', + subject: 'Simple foo.', + body: null, + footer: null, + notes: [ + { + title: 'MAJOR BREAKING CHANGES', + text: '1 - Reference site about Lorem Ipsum, giving information on its origins, as well as ' + + 'a random Lipsum generator.' + }, + { + title: 'MAJOR BREAKING CHANGES', + text: '2 - Reference site about Lorem Ipsum, giving information on its origins, as well as ' + + 'a random Lipsum generator.' + }, + { + title: 'MINOR BREAKING CHANGES', + text: '3 - Reference site about Lorem Ipsum, giving information on its origins, as well as ' + + 'a random Lipsum generator.' + } + ] + }; + + displayCommits( [ commit ] ); + + const [ firstCall ] = stubs.logger.info.mock.calls; + const [ firstArgument ] = firstCall; + const message = firstArgument.split( '\n' ); + + /* eslint-disable max-len */ + expect( message[ 0 ] ).toContain( 'Feature: Simple foo.' ); + expect( message[ 1 ] ).toContain( 'MAJOR BREAKING CHANGES: 1 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); + expect( message[ 2 ] ).toContain( 'MAJOR BREAKING CHANGES: 2 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); + expect( message[ 3 ] ).toContain( 'MINOR BREAKING CHANGES: 3 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); + /* eslint-enable max-len */ + } ); + + describe( 'options.attachLinkToCommit', () => { + it( 'adds a link to displayed commit', () => { const commit = { hash: '684997d', - header: 'Docs: README.', - type: 'Docs', - rawType: 'Docs', - subject: 'README.', + header: 'Fix: Simple fix.', + type: 'Bug fixes', + subject: 'Simple fix.', body: null, footer: null, - notes: [] + notes: [], + rawType: 'Fix', + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-foo' }; - displayCommits( [ commit ] ); + displayCommits( [ commit ], { attachLinkToCommit: true } ); expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); const [ firstCall ] = stubs.logger.info.mock.calls; const [ firstArgument ] = firstCall; + const logMessage = firstArgument.split( '\n' ); - expect( firstArgument ).toContain( 'Docs: README.' ); - expect( firstArgument ).toContain( 'SKIPPED' ); + expect( logMessage[ 0 ] ).toContain( 'Fix: Simple fix.' ); + expect( logMessage[ 0 ] ).toContain( 'INCLUDED' ); + expect( logMessage[ 1 ] ).toContain( 'https://github.com/ckeditor/ckeditor5-foo/commit/684997d' ); } ); + } ); - it( 'does not attach invalid commit to the changelog', () => { + describe( 'options.indentLevel', () => { + it( 'is equal to 1 by default', () => { const commit = { hash: '684997d', - header: 'Invalid commit.', - type: null, - subject: null, + header: 'Fix: Simple fix.', + type: 'Bug fixes', + subject: 'Simple fix.', body: null, footer: null, - notes: [] + notes: [], + rawType: 'Fix' }; displayCommits( [ commit ] ); @@ -166,22 +318,20 @@ describe( 'dev-release-tools/utils', () => { const [ firstCall ] = stubs.logger.info.mock.calls; const [ firstArgument ] = firstCall; - expect( firstArgument ).toContain( 'Invalid commit.' ); - expect( firstArgument ).toContain( 'INVALID' ); + expect( firstArgument.substring( 0, 3 ) ).toEqual( ' ' ); } ); - it( 'attaches additional subject for merge commits to the commit list', () => { + it( 'indents second line properly', () => { const commit = { + hash: '684997d', merge: 'Merge pull request #75 from ckeditor/t/64', - hash: 'dea3501', header: 'Feature: Introduced a brand new release tools with a new set of requirements.', type: 'Feature', - rawType: 'Feature', subject: 'Introduced a brand new release tools with a new set of requirements.', body: null, footer: null, - mentions: [], - notes: [] + notes: [], + rawType: 'Fix' }; displayCommits( [ commit ] ); @@ -190,511 +340,359 @@ describe( 'dev-release-tools/utils', () => { const [ firstCall ] = stubs.logger.info.mock.calls; const [ firstArgument ] = firstCall; - const logMessageAsArray = firstArgument.split( '\n' ); - - expect( logMessageAsArray[ 0 ] ).toContain( 'Feature: Introduced a brand new release tools with a new set of requirements.' ); - expect( logMessageAsArray[ 0 ] ).toContain( 'INCLUDED' ); - expect( logMessageAsArray[ 1 ] ).toContain( 'Merge pull request #75 from ckeditor/t/64' ); - } ); - - it( 'displays proper log if commit does not contain the second line', () => { - const commit = { - type: null, - subject: null, - merge: 'Merge branch \'master\' of github.com:ckeditor/ckeditor5-dev', - header: 'Merge branch \'master\' of github.com:ckeditor/ckeditor5-dev', - body: null, - footer: null, - notes: [], - references: [], - mentions: [], - revert: null, - rawType: undefined, - files: [], - scope: undefined, - isPublicCommit: false, - hash: 'a'.repeat( 40 ), - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - }; - - displayCommits( [ commit ] ); - - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; + const [ firstLine, secondLine ] = firstArgument.split( '\n' ); - // The merge commit displays two lines: - // Prefix: Changes. - // Merge ... - // If the merge commit does not contain the second line, it should display only the one. - expect( firstArgument.split( '\n' ) ).toHaveLength( 1 ); + expect( firstLine.substring( 0, 3 ) ).toEqual( ' '.repeat( 3 ) ); + expect( secondLine.substring( 0, 13 ) ).toEqual( ' '.repeat( 13 ) ); } ); - it( 'attaches breaking changes notes to displayed message', () => { + it( 'works with "options.attachLinkToCommit"', () => { const commit = { hash: '684997d', - header: 'Feature: Simple foo.', + merge: 'Merge pull request #75 from ckeditor/t/64', + header: 'Feature: Introduced a brand new release tools with a new set of requirements.', type: 'Feature', - rawType: 'Feature', - subject: 'Simple foo.', + subject: 'Introduced a brand new release tools with a new set of requirements.', body: null, footer: null, - notes: [ - { - title: 'MAJOR BREAKING CHANGES', - text: '1 - Reference site about Lorem Ipsum, giving information on its origins, as well as ' + - 'a random Lipsum generator.' - }, - { - title: 'MAJOR BREAKING CHANGES', - text: '2 - Reference site about Lorem Ipsum, giving information on its origins, as well as ' + - 'a random Lipsum generator.' - }, - { - title: 'MINOR BREAKING CHANGES', - text: '3 - Reference site about Lorem Ipsum, giving information on its origins, as well as ' + - 'a random Lipsum generator.' - } - ] + notes: [], + rawType: 'Fix', + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-foo' }; - displayCommits( [ commit ] ); + displayCommits( [ commit ], { attachLinkToCommit: true, indentLevel: 2 } ); + + expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); const [ firstCall ] = stubs.logger.info.mock.calls; const [ firstArgument ] = firstCall; - const message = firstArgument.split( '\n' ); - - /* eslint-disable max-len */ - expect( message[ 0 ] ).toContain( 'Feature: Simple foo.' ); - expect( message[ 1 ] ).toContain( 'MAJOR BREAKING CHANGES: 1 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); - expect( message[ 2 ] ).toContain( 'MAJOR BREAKING CHANGES: 2 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); - expect( message[ 3 ] ).toContain( 'MINOR BREAKING CHANGES: 3 - Reference site about Lorem Ipsum, giving information on its origins, as...' ); - /* eslint-enable max-len */ + const [ firstLine, secondLine, thirdLine ] = firstArgument.split( '\n' ); + + expect( firstLine.substring( 0, 6 ) ).toEqual( ' '.repeat( 6 ) ); + expect( secondLine.substring( 0, 16 ) ).toEqual( ' '.repeat( 16 ) ); + expect( thirdLine.substring( 0, 16 ) ).toEqual( ' '.repeat( 16 ) ); } ); + } ); - describe( 'options.attachLinkToCommit', () => { - it( 'adds a link to displayed commit', () => { - const commit = { - hash: '684997d', + describe( 'grouping commits', () => { + it( 'works for a group of two commits between single commit groups', () => { + // Displayed log: + // + // * aaaaaaa "Fix: Another fix." INCLUDED + // --------------------------------------------- + // |* bbbbbbb "Fix: Simple fix." INCLUDED + // |* bbbbbbb "Feature: A new feature." INCLUDED + // --------------------------------------------- + // * ccccccc "Fix: Another fix." INCLUDED + + displayCommits( [ + { + hash: 'a'.repeat( 40 ), + header: 'Fix: Another fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Another fix.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'b'.repeat( 40 ), header: 'Fix: Simple fix.', type: 'Bug fixes', + rawType: 'Fix', subject: 'Simple fix.', body: null, footer: null, - notes: [], + notes: [] + }, + { + hash: 'b'.repeat( 40 ), + header: 'Feature: A new feature.', + type: 'Features', + rawType: 'Feature', + subject: 'A new feature.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'c'.repeat( 40 ), + header: 'Fix: Another fix.', + type: 'Bug fixes', rawType: 'Fix', - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-foo' - }; - - displayCommits( [ commit ], { attachLinkToCommit: true } ); + subject: 'Another fix.', + body: null, + footer: null, + notes: [] + } + ] ); - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 6 ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; - const logMessage = firstArgument.split( '\n' ); + const [ , secondCall, , , fifthCall ] = stubs.logger.info.mock.calls; + const [ secondCallfirstArgument ] = secondCall; + const [ fifthCallfirstArgument ] = fifthCall; - expect( logMessage[ 0 ] ).toContain( 'Fix: Simple fix.' ); - expect( logMessage[ 0 ] ).toContain( 'INCLUDED' ); - expect( logMessage[ 1 ] ).toContain( 'https://github.com/ckeditor/ckeditor5-foo/commit/684997d' ); - } ); + // Calls: 0, 2, 3, and 5 display the commit data. + expect( secondCallfirstArgument ).toMatch( /-----/ ); + expect( fifthCallfirstArgument ).toMatch( /-----/ ); } ); - describe( 'options.indentLevel', () => { - it( 'is equal to 1 by default', () => { - const commit = { - hash: '684997d', + it( 'works for a group of two commits that follows a single commit group', () => { + // Displayed log: + // + // * aaaaaaa "Fix: Another fix." INCLUDED + // --------------------------------------------- + // |* bbbbbbb "Fix: Simple fix." INCLUDED + // |* bbbbbbb "Feature: A new feature." INCLUDED + // --------------------------------------------- + displayCommits( [ + { + hash: 'a'.repeat( 40 ), + header: 'Fix: Another fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Another fix.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'b'.repeat( 40 ), header: 'Fix: Simple fix.', type: 'Bug fixes', + rawType: 'Fix', subject: 'Simple fix.', body: null, footer: null, - notes: [], - rawType: 'Fix' - }; - - displayCommits( [ commit ] ); + notes: [] + }, + { + hash: 'b'.repeat( 40 ), + header: 'Feature: A new feature.', + type: 'Features', + rawType: 'Feature', + subject: 'A new feature.', + body: null, + footer: null, + notes: [] + } + ] ); - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 5 ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; + const [ , secondCall, , , fifthCall ] = stubs.logger.info.mock.calls; + const [ secondCallfirstArgument ] = secondCall; + const [ fifthCallfirstArgument ] = fifthCall; - expect( firstArgument.substring( 0, 3 ) ).toEqual( ' ' ); - } ); + // Calls: 0, 2, and 3 display the commit data. + expect( secondCallfirstArgument ).toMatch( /-----/ ); + expect( fifthCallfirstArgument ).toMatch( /-----/ ); + } ); - it( 'indents second line properly', () => { - const commit = { - hash: '684997d', - merge: 'Merge pull request #75 from ckeditor/t/64', - header: 'Feature: Introduced a brand new release tools with a new set of requirements.', - type: 'Feature', - subject: 'Introduced a brand new release tools with a new set of requirements.', + it( 'works for a single commit group that follows group of two commits ', () => { + // Displayed log: + // + // --------------------------------------------- + // |* bbbbbbb "Fix: Simple fix." INCLUDED + // |* bbbbbbb "Feature: A new feature." INCLUDED + // --------------------------------------------- + // * ccccccc "Fix: Another fix." INCLUDED + + displayCommits( [ + { + hash: 'b'.repeat( 40 ), + header: 'Fix: Simple fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Simple fix.', body: null, footer: null, - notes: [], - rawType: 'Fix' - }; - - displayCommits( [ commit ] ); + notes: [] + }, + { + hash: 'b'.repeat( 40 ), + header: 'Feature: A new feature.', + type: 'Features', + rawType: 'Feature', + subject: 'A new feature.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'c'.repeat( 40 ), + header: 'Fix: Another fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Another fix.', + body: null, + footer: null, + notes: [] + } + ] ); - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 5 ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; - const [ firstLine, secondLine ] = firstArgument.split( '\n' ); + const [ firstCall, , , fourthCall ] = stubs.logger.info.mock.calls; + const [ firstCallfirstArgument ] = firstCall; + const [ fourthCallfirstArgument ] = fourthCall; - expect( firstLine.substring( 0, 3 ) ).toEqual( ' '.repeat( 3 ) ); - expect( secondLine.substring( 0, 13 ) ).toEqual( ' '.repeat( 13 ) ); - } ); + // Calls: 1, 2, and 4 display the commit data. + expect( firstCallfirstArgument ).toMatch( /-----/ ); + expect( fourthCallfirstArgument ).toMatch( /-----/ ); + } ); - it( 'works with "options.attachLinkToCommit"', () => { - const commit = { - hash: '684997d', - merge: 'Merge pull request #75 from ckeditor/t/64', - header: 'Feature: Introduced a brand new release tools with a new set of requirements.', - type: 'Feature', - subject: 'Introduced a brand new release tools with a new set of requirements.', + it( 'does not duplicate the separator for commit groups', () => { + // Displayed log: + // + // --------------------------------------------- + // |* bbbbbbb "Fix: Simple fix." INCLUDED + // |* bbbbbbb "Feature: A new feature." INCLUDED + // --------------------------------------------- + // |* ccccccc "Fix: One Another fix." INCLUDED + // |* ccccccc "Fix: Another fix." INCLUDED + // --------------------------------------------- + + displayCommits( [ + { + hash: 'b'.repeat( 40 ), + header: 'Fix: Simple fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Simple fix.', body: null, footer: null, - notes: [], + notes: [] + }, + { + hash: 'b'.repeat( 40 ), + header: 'Feature: A new feature.', + type: 'Features', + rawType: 'Feature', + subject: 'A new feature.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'c'.repeat( 40 ), + header: 'Fix: One Another fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'One Another fix.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'c'.repeat( 40 ), + header: 'Fix: Another fix.', + type: 'Bug fixes', rawType: 'Fix', - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-foo' - }; + subject: 'Another fix.', + body: null, + footer: null, + notes: [] + } + ] ); + + expect( stubs.logger.info ).toHaveBeenCalledTimes( 7 ); + + const [ firstCall, , , fourthCall, fifthCall, , seventhCall ] = stubs.logger.info.mock.calls; + const [ firstCallfirstArgument ] = firstCall; + const [ fourthCallfirstArgument ] = fourthCall; + const [ fifthCallfirstArgument ] = fifthCall; + const [ seventhCallfirstArgument ] = seventhCall; + + // Calls: 1, 2, 4, and 5 display the commit data. + expect( firstCallfirstArgument ).toMatch( /-----/ ); + expect( fourthCallfirstArgument ).toMatch( /-----/ ); + expect( fifthCallfirstArgument ).not.toMatch( /-----/ ); + expect( seventhCallfirstArgument ).toMatch( /-----/ ); + } ); - displayCommits( [ commit ], { attachLinkToCommit: true, indentLevel: 2 } ); + it( 'groups two groups of commits separated by a single commit group', () => { + // Displayed log: + // + // --------------------------------------------- + // |* bbbbbbb "Fix: Simple fix." INCLUDED + // |* bbbbbbb "Feature: A new feature." INCLUDED + // --------------------------------------------- + // * aaaaaaa "Fix: Another fix." INCLUDED + // --------------------------------------------- + // |* ccccccc "Fix: One Another fix." INCLUDED + // |* ccccccc "Fix: Another fix." INCLUDED + // --------------------------------------------- + + displayCommits( [ + { + hash: 'b'.repeat( 40 ), + header: 'Fix: Simple fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'b'.repeat( 40 ), + header: 'Feature: A new feature.', + type: 'Features', + rawType: 'Feature', + subject: 'A new feature.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'a'.repeat( 40 ), + header: 'Fix: Another fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Another fix.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'c'.repeat( 40 ), + header: 'Fix: One Another fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'One Another fix.', + body: null, + footer: null, + notes: [] + }, + { + hash: 'c'.repeat( 40 ), + header: 'Fix: Another fix.', + type: 'Bug fixes', + rawType: 'Fix', + subject: 'Another fix.', + body: null, + footer: null, + notes: [] + } + ] ); - expect( stubs.logger.info ).toHaveBeenCalledTimes( 1 ); + expect( stubs.logger.info ).toHaveBeenCalledTimes( 9 ); - const [ firstCall ] = stubs.logger.info.mock.calls; - const [ firstArgument ] = firstCall; - const [ firstLine, secondLine, thirdLine ] = firstArgument.split( '\n' ); + const [ firstCall, , , fourthCall, , sixthCall, , , ninethCall ] = stubs.logger.info.mock.calls; + const [ firstCallfirstArgument ] = firstCall; + const [ fourthCallfirstArgument ] = fourthCall; + const [ sixthCallfirstArgument ] = sixthCall; + const [ ninethCallfirstArgument ] = ninethCall; - expect( firstLine.substring( 0, 6 ) ).toEqual( ' '.repeat( 6 ) ); - expect( secondLine.substring( 0, 16 ) ).toEqual( ' '.repeat( 16 ) ); - expect( thirdLine.substring( 0, 16 ) ).toEqual( ' '.repeat( 16 ) ); - } ); - } ); + // Calls: 1, 2, 4, 6, and 7 display the commit data. + expect( firstCallfirstArgument ).to.match( /-----/ ); + expect( fourthCallfirstArgument ).to.match( /-----/ ); - describe( 'grouping commits', () => { - it( 'works for a group of two commits between single commit groups', () => { - // Displayed log: - // - // * aaaaaaa "Fix: Another fix." INCLUDED - // --------------------------------------------- - // |* bbbbbbb "Fix: Simple fix." INCLUDED - // |* bbbbbbb "Feature: A new feature." INCLUDED - // --------------------------------------------- - // * ccccccc "Fix: Another fix." INCLUDED - - displayCommits( [ - { - hash: 'a'.repeat( 40 ), - header: 'Fix: Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Another fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'b'.repeat( 40 ), - header: 'Fix: Simple fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'b'.repeat( 40 ), - header: 'Feature: A new feature.', - type: 'Features', - rawType: 'Feature', - subject: 'A new feature.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'c'.repeat( 40 ), - header: 'Fix: Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Another fix.', - body: null, - footer: null, - notes: [] - } - ] ); - - expect( stubs.logger.info ).toHaveBeenCalledTimes( 6 ); - - const [ , secondCall, , , fifthCall ] = stubs.logger.info.mock.calls; - const [ secondCallfirstArgument ] = secondCall; - const [ fifthCallfirstArgument ] = fifthCall; - - // Calls: 0, 2, 3, and 5 display the commit data. - expect( secondCallfirstArgument ).toMatch( /-----/ ); - expect( fifthCallfirstArgument ).toMatch( /-----/ ); - } ); - - it( 'works for a group of two commits that follows a single commit group', () => { - // Displayed log: - // - // * aaaaaaa "Fix: Another fix." INCLUDED - // --------------------------------------------- - // |* bbbbbbb "Fix: Simple fix." INCLUDED - // |* bbbbbbb "Feature: A new feature." INCLUDED - // --------------------------------------------- - displayCommits( [ - { - hash: 'a'.repeat( 40 ), - header: 'Fix: Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Another fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'b'.repeat( 40 ), - header: 'Fix: Simple fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'b'.repeat( 40 ), - header: 'Feature: A new feature.', - type: 'Features', - rawType: 'Feature', - subject: 'A new feature.', - body: null, - footer: null, - notes: [] - } - ] ); - - expect( stubs.logger.info ).toHaveBeenCalledTimes( 5 ); - - const [ , secondCall, , , fifthCall ] = stubs.logger.info.mock.calls; - const [ secondCallfirstArgument ] = secondCall; - const [ fifthCallfirstArgument ] = fifthCall; - - // Calls: 0, 2, and 3 display the commit data. - expect( secondCallfirstArgument ).toMatch( /-----/ ); - expect( fifthCallfirstArgument ).toMatch( /-----/ ); - } ); - - it( 'works for a single commit group that follows group of two commits ', () => { - // Displayed log: - // - // --------------------------------------------- - // |* bbbbbbb "Fix: Simple fix." INCLUDED - // |* bbbbbbb "Feature: A new feature." INCLUDED - // --------------------------------------------- - // * ccccccc "Fix: Another fix." INCLUDED - - displayCommits( [ - { - hash: 'b'.repeat( 40 ), - header: 'Fix: Simple fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'b'.repeat( 40 ), - header: 'Feature: A new feature.', - type: 'Features', - rawType: 'Feature', - subject: 'A new feature.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'c'.repeat( 40 ), - header: 'Fix: Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Another fix.', - body: null, - footer: null, - notes: [] - } - ] ); - - expect( stubs.logger.info ).toHaveBeenCalledTimes( 5 ); - - const [ firstCall, , , fourthCall ] = stubs.logger.info.mock.calls; - const [ firstCallfirstArgument ] = firstCall; - const [ fourthCallfirstArgument ] = fourthCall; - - // Calls: 1, 2, and 4 display the commit data. - expect( firstCallfirstArgument ).toMatch( /-----/ ); - expect( fourthCallfirstArgument ).toMatch( /-----/ ); - } ); - - it( 'does not duplicate the separator for commit groups', () => { - // Displayed log: - // - // --------------------------------------------- - // |* bbbbbbb "Fix: Simple fix." INCLUDED - // |* bbbbbbb "Feature: A new feature." INCLUDED - // --------------------------------------------- - // |* ccccccc "Fix: One Another fix." INCLUDED - // |* ccccccc "Fix: Another fix." INCLUDED - // --------------------------------------------- - - displayCommits( [ - { - hash: 'b'.repeat( 40 ), - header: 'Fix: Simple fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'b'.repeat( 40 ), - header: 'Feature: A new feature.', - type: 'Features', - rawType: 'Feature', - subject: 'A new feature.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'c'.repeat( 40 ), - header: 'Fix: One Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'One Another fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'c'.repeat( 40 ), - header: 'Fix: Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Another fix.', - body: null, - footer: null, - notes: [] - } - ] ); - - expect( stubs.logger.info ).toHaveBeenCalledTimes( 7 ); - - const [ firstCall, , , fourthCall, fifthCall, , seventhCall ] = stubs.logger.info.mock.calls; - const [ firstCallfirstArgument ] = firstCall; - const [ fourthCallfirstArgument ] = fourthCall; - const [ fifthCallfirstArgument ] = fifthCall; - const [ seventhCallfirstArgument ] = seventhCall; - - // Calls: 1, 2, 4, and 5 display the commit data. - expect( firstCallfirstArgument ).toMatch( /-----/ ); - expect( fourthCallfirstArgument ).toMatch( /-----/ ); - expect( fifthCallfirstArgument ).not.toMatch( /-----/ ); - expect( seventhCallfirstArgument ).toMatch( /-----/ ); - } ); - - it( 'groups two groups of commits separated by a single commit group', () => { - // Displayed log: - // - // --------------------------------------------- - // |* bbbbbbb "Fix: Simple fix." INCLUDED - // |* bbbbbbb "Feature: A new feature." INCLUDED - // --------------------------------------------- - // * aaaaaaa "Fix: Another fix." INCLUDED - // --------------------------------------------- - // |* ccccccc "Fix: One Another fix." INCLUDED - // |* ccccccc "Fix: Another fix." INCLUDED - // --------------------------------------------- - - displayCommits( [ - { - hash: 'b'.repeat( 40 ), - header: 'Fix: Simple fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'b'.repeat( 40 ), - header: 'Feature: A new feature.', - type: 'Features', - rawType: 'Feature', - subject: 'A new feature.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'a'.repeat( 40 ), - header: 'Fix: Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Another fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'c'.repeat( 40 ), - header: 'Fix: One Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'One Another fix.', - body: null, - footer: null, - notes: [] - }, - { - hash: 'c'.repeat( 40 ), - header: 'Fix: Another fix.', - type: 'Bug fixes', - rawType: 'Fix', - subject: 'Another fix.', - body: null, - footer: null, - notes: [] - } - ] ); - - expect( stubs.logger.info ).toHaveBeenCalledTimes( 9 ); - - const [ firstCall, , , fourthCall, , sixthCall, , , ninethCall ] = stubs.logger.info.mock.calls; - const [ firstCallfirstArgument ] = firstCall; - const [ fourthCallfirstArgument ] = fourthCall; - const [ sixthCallfirstArgument ] = sixthCall; - const [ ninethCallfirstArgument ] = ninethCall; - - // Calls: 1, 2, 4, 6, and 7 display the commit data. - expect( firstCallfirstArgument ).to.match( /-----/ ); - expect( fourthCallfirstArgument ).to.match( /-----/ ); - - expect( sixthCallfirstArgument ).to.match( /-----/ ); - expect( ninethCallfirstArgument ).to.match( /-----/ ); - } ); + expect( sixthCallfirstArgument ).to.match( /-----/ ); + expect( ninethCallfirstArgument ).to.match( /-----/ ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js index 121d1ad90..0b959df91 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/executeinparallel.js @@ -48,7 +48,7 @@ vi.mock( 'glob' ); vi.mock( 'fs/promises' ); vi.mock( '../../lib/utils/abortcontroller.js' ); -describe( 'dev-release-tools/utils', () => { +describe( 'executeInParallel()', () => { let abortController, defaultOptions, outputHistory; beforeEach( () => { @@ -82,557 +82,555 @@ describe( 'dev-release-tools/utils', () => { stubs.WorkerMock.instances = []; } ); - describe( 'executeInParallel()', () => { - it( 'should execute the specified `taskToExecute` on all packages found in the `packagesDirectory`', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + it( 'should execute the specified `taskToExecute` on all packages found in the `packagesDirectory`', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - // By default the helper uses a half of available CPUs. - expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); + // By default the helper uses a half of available CPUs. + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( glob ).toHaveBeenCalledTimes( 1 ); - expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { - cwd: '/home/ckeditor', - absolute: true - } ) ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: '/home/ckeditor', + absolute: true + } ) ); - expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); - expect( fs.writeFile ).toHaveBeenCalledWith( - '/home/ckeditor/uuid-4.mjs', - 'export default packagePath => console.log( \'pwd\', packagePath );', - 'utf-8' - ); - expect( firstWorker.workerData ).toBeInstanceOf( Object ); - expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.mjs' ); - expect( firstWorker.workerData ).toHaveProperty( 'packages' ); + expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); + expect( fs.writeFile ).toHaveBeenCalledWith( + '/home/ckeditor/uuid-4.mjs', + 'export default packagePath => console.log( \'pwd\', packagePath );', + 'utf-8' + ); + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.mjs' ); + expect( firstWorker.workerData ).toHaveProperty( 'packages' ); + + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.mjs' ); + expect( secondWorker.workerData ).toHaveProperty( 'packages' ); - expect( secondWorker.workerData ).toBeInstanceOf( Object ); - expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', '/home/ckeditor/uuid-4.mjs' ); - expect( secondWorker.workerData ).toHaveProperty( 'packages' ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + await promise; + } ); - await promise; + it( 'should execute the specified `taskToExecute` on packages found in the `packagesDirectory` that are not filtered', async () => { + const options = Object.assign( {}, defaultOptions, { + // Skip "package-02". + packagesDirectoryFilter: packageDirectory => !packageDirectory.endsWith( 'package-02' ) } ); - it( 'should execute the specified `taskToExecute` on packages found in the `packagesDirectory` that are not filtered', async () => { - const options = Object.assign( {}, defaultOptions, { - // Skip "package-02". - packagesDirectoryFilter: packageDirectory => !packageDirectory.endsWith( 'package-02' ) - } ); + const promise = executeInParallel( options ); + await delay( 0 ); - const promise = executeInParallel( options ); - await delay( 0 ); + // By default the helper uses a half of available CPUs. + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - // By default the helper uses a half of available CPUs. - expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + expect( firstWorker.workerData.packages ).toEqual( [ + '/home/ckeditor/my-packages/package-01', + '/home/ckeditor/my-packages/package-04' + ] ); - expect( firstWorker.workerData.packages ).toEqual( [ - '/home/ckeditor/my-packages/package-01', - '/home/ckeditor/my-packages/package-04' - ] ); + expect( secondWorker.workerData.packages ).toEqual( [ + '/home/ckeditor/my-packages/package-03' + ] ); - expect( secondWorker.workerData.packages ).toEqual( [ - '/home/ckeditor/my-packages/package-03' - ] ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + await promise; + } ); - await promise; + it( 'should use the specified `cwd` when looking for packages', async () => { + const options = Object.assign( {}, defaultOptions, { + cwd: '/custom/cwd' } ); - it( 'should use the specified `cwd` when looking for packages', async () => { - const options = Object.assign( {}, defaultOptions, { - cwd: '/custom/cwd' - } ); + const promise = executeInParallel( options ); + await delay( 0 ); - const promise = executeInParallel( options ); - await delay( 0 ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: '/custom/cwd', + absolute: true + } ) ); - expect( glob ).toHaveBeenCalledTimes( 1 ); - expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { - cwd: '/custom/cwd', - absolute: true - } ) ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + await promise; + } ); - await promise; - } ); + it( 'should normalize the current working directory to unix-style (default value, Windows path)', async () => { + process.cwd.mockReturnValue( 'C:\\Users\\ckeditor' ); - it( 'should normalize the current working directory to unix-style (default value, Windows path)', async () => { - process.cwd.mockReturnValue( 'C:\\Users\\ckeditor' ); + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: 'C:/Users/ckeditor', + absolute: true + } ) ); - expect( glob ).toHaveBeenCalledTimes( 1 ); - expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { - cwd: 'C:/Users/ckeditor', - absolute: true - } ) ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + await promise; + } ); - await promise; + it( 'should normalize the current working directory to unix-style (`options.cwd`, Windows path)', async () => { + const options = Object.assign( {}, defaultOptions, { + cwd: 'C:\\Users\\ckeditor' } ); - it( 'should normalize the current working directory to unix-style (`options.cwd`, Windows path)', async () => { - const options = Object.assign( {}, defaultOptions, { - cwd: 'C:\\Users\\ckeditor' - } ); - - const promise = executeInParallel( options ); - await delay( 0 ); + const promise = executeInParallel( options ); + await delay( 0 ); - expect( glob ).toHaveBeenCalledTimes( 1 ); - expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { - cwd: 'C:/Users/ckeditor', - absolute: true - } ) ); + expect( glob ).toHaveBeenCalledTimes( 1 ); + expect( glob ).toHaveBeenCalledWith( 'my-packages/*/', expect.objectContaining( { + cwd: 'C:/Users/ckeditor', + absolute: true + } ) ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - await promise; - } ); + await promise; + } ); - it( 'should work on normalized paths to packages', async () => { - vi.mocked( glob ).mockResolvedValue( [ - 'C:/Users/workspace/ckeditor/my-packages/package-01', - 'C:/Users/workspace/ckeditor/my-packages/package-02', - 'C:/Users/workspace/ckeditor/my-packages/package-03', - 'C:/Users/workspace/ckeditor/my-packages/package-04' - ] ); + it( 'should work on normalized paths to packages', async () => { + vi.mocked( glob ).mockResolvedValue( [ + 'C:/Users/workspace/ckeditor/my-packages/package-01', + 'C:/Users/workspace/ckeditor/my-packages/package-02', + 'C:/Users/workspace/ckeditor/my-packages/package-03', + 'C:/Users/workspace/ckeditor/my-packages/package-04' + ] ); - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - // By default the helper uses a half of available CPUs. - expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); + // By default the helper uses a half of available CPUs. + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( firstWorker.workerData.packages ).toEqual( [ - 'C:/Users/workspace/ckeditor/my-packages/package-01', - 'C:/Users/workspace/ckeditor/my-packages/package-03' - ] ); + expect( firstWorker.workerData.packages ).toEqual( [ + 'C:/Users/workspace/ckeditor/my-packages/package-01', + 'C:/Users/workspace/ckeditor/my-packages/package-03' + ] ); - expect( secondWorker.workerData.packages ).toEqual( [ - 'C:/Users/workspace/ckeditor/my-packages/package-02', - 'C:/Users/workspace/ckeditor/my-packages/package-04' - ] ); + expect( secondWorker.workerData.packages ).toEqual( [ + 'C:/Users/workspace/ckeditor/my-packages/package-02', + 'C:/Users/workspace/ckeditor/my-packages/package-04' + ] ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - await promise; - } ); + await promise; + } ); - it( 'should pass task options to all workers', async () => { - const taskOptions = { - property: 'Example of the property.', - some: { - deeply: { - nested: { - property: 'Example the deeply nested property.' - } + it( 'should pass task options to all workers', async () => { + const taskOptions = { + property: 'Example of the property.', + some: { + deeply: { + nested: { + property: 'Example the deeply nested property.' } } - }; + } + }; - const options = Object.assign( {}, defaultOptions, { taskOptions } ); + const options = Object.assign( {}, defaultOptions, { taskOptions } ); - const promise = executeInParallel( options ); - await delay( 0 ); + const promise = executeInParallel( options ); + await delay( 0 ); - // By default the helper uses a half of available CPUs. - expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); + // By default the helper uses a half of available CPUs. + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( firstWorker.workerData ).toBeInstanceOf( Object ); - expect( firstWorker.workerData ).toHaveProperty( 'taskOptions', taskOptions ); + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'taskOptions', taskOptions ); - expect( secondWorker.workerData ).toBeInstanceOf( Object ); - expect( secondWorker.workerData ).toHaveProperty( 'taskOptions', taskOptions ); + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'taskOptions', taskOptions ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - await promise; - } ); - - it( 'should create the temporary module properly when using Windows-style paths', async () => { - process.cwd.mockReturnValue( 'C:\\Users\\ckeditor' ); + await promise; + } ); - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + it( 'should create the temporary module properly when using Windows-style paths', async () => { + process.cwd.mockReturnValue( 'C:\\Users\\ckeditor' ); - expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); - expect( fs.writeFile ).toHaveBeenCalledWith( - 'C:/Users/ckeditor/uuid-4.mjs', - 'export default packagePath => console.log( \'pwd\', packagePath );', - 'utf-8' - ); + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - // By default the helper uses a half of available CPUs. - expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); + expect( fs.writeFile ).toHaveBeenCalledTimes( 1 ); + expect( fs.writeFile ).toHaveBeenCalledWith( + 'C:/Users/ckeditor/uuid-4.mjs', + 'export default packagePath => console.log( \'pwd\', packagePath );', + 'utf-8' + ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + // By default the helper uses a half of available CPUs. + expect( stubs.WorkerMock.instances ).toHaveLength( 2 ); - expect( firstWorker.workerData ).toBeInstanceOf( Object ); - expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.mjs' ); - expect( firstWorker.workerData ).toHaveProperty( 'packages' ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - expect( secondWorker.workerData ).toBeInstanceOf( Object ); - expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.mjs' ); - expect( secondWorker.workerData ).toHaveProperty( 'packages' ); + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.mjs' ); + expect( firstWorker.workerData ).toHaveProperty( 'packages' ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'callbackModule', 'C:/Users/ckeditor/uuid-4.mjs' ); + expect( secondWorker.workerData ).toHaveProperty( 'packages' ); - await promise; - } ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - it( 'should use the specified number of threads (`concurrency`)', async () => { - const options = Object.assign( {}, defaultOptions, { - concurrency: 4 - } ); + await promise; + } ); - const promise = executeInParallel( options ); - await delay( 0 ); + it( 'should use the specified number of threads (`concurrency`)', async () => { + const options = Object.assign( {}, defaultOptions, { + concurrency: 4 + } ); - expect( stubs.WorkerMock.instances ).toHaveLength( 4 ); + const promise = executeInParallel( options ); + await delay( 0 ); - // Workers did not emit an error. - for ( const worker of stubs.WorkerMock.instances ) { - getExitCallback( worker )( 0 ); - } + expect( stubs.WorkerMock.instances ).toHaveLength( 4 ); - await promise; - } ); + // Workers did not emit an error. + for ( const worker of stubs.WorkerMock.instances ) { + getExitCallback( worker )( 0 ); + } - it( 'should resolve the promise if a worker finished (aborted) with a non-zero exit code (first worker)', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + await promise; + } ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + it( 'should resolve the promise if a worker finished (aborted) with a non-zero exit code (first worker)', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - getExitCallback( firstWorker )( 1 ); - getExitCallback( secondWorker )( 0 ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - await promise; - } ); + getExitCallback( firstWorker )( 1 ); + getExitCallback( secondWorker )( 0 ); - it( 'should resolve the promise if a worker finished (aborted) with a non-zero exit code (second worker)', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + await promise; + } ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + it( 'should resolve the promise if a worker finished (aborted) with a non-zero exit code (second worker)', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 1 ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - await promise; - } ); + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 1 ); - it( 'should reject the promise if a worker emitted an error (first worker)', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + await promise; + } ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - const error = new Error( 'Example error from a worker.' ); + it( 'should reject the promise if a worker emitted an error (first worker)', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - getErrorCallback( firstWorker )( error ); - getExitCallback( secondWorker )( 0 ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const error = new Error( 'Example error from a worker.' ); - return promise - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - err => { - expect( err ).toEqual( error ); - } - ); - } ); + getErrorCallback( firstWorker )( error ); + getExitCallback( secondWorker )( 0 ); - it( 'should reject the promise if a worker emitted an error (second worker)', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); - - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - const error = new Error( 'Example error from a worker.' ); + return promise + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + err => { + expect( err ).toEqual( error ); + } + ); + } ); - getExitCallback( firstWorker )( 0 ); - getErrorCallback( secondWorker )( error ); + it( 'should reject the promise if a worker emitted an error (second worker)', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - return promise - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - err => { - expect( err ).toEqual( error ); - } - ); - } ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const error = new Error( 'Example error from a worker.' ); - it( 'should split packages into threads one by one', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); - - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - - expect( firstWorker.workerData ).toBeInstanceOf( Object ); - expect( firstWorker.workerData ).toHaveProperty( 'packages' ); - expect( firstWorker.workerData.packages ).toBeInstanceOf( Array ); - expect( firstWorker.workerData.packages ).toEqual( [ - '/home/ckeditor/my-packages/package-01', - '/home/ckeditor/my-packages/package-03' - ] ); - - expect( secondWorker.workerData ).toBeInstanceOf( Object ); - expect( secondWorker.workerData ).toHaveProperty( 'packages' ); - expect( secondWorker.workerData.packages ).toBeInstanceOf( Array ); - expect( secondWorker.workerData.packages ).toEqual( [ - '/home/ckeditor/my-packages/package-02', - '/home/ckeditor/my-packages/package-04' - ] ); - - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); - - await promise; - } ); + getExitCallback( firstWorker )( 0 ); + getErrorCallback( secondWorker )( error ); - it( 'should remove the temporary module after execution', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + return promise + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + err => { + expect( err ).toEqual( error ); + } + ); + } ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + it( 'should split packages into threads one by one', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - await promise; + expect( firstWorker.workerData ).toBeInstanceOf( Object ); + expect( firstWorker.workerData ).toHaveProperty( 'packages' ); + expect( firstWorker.workerData.packages ).toBeInstanceOf( Array ); + expect( firstWorker.workerData.packages ).toEqual( [ + '/home/ckeditor/my-packages/package-01', + '/home/ckeditor/my-packages/package-03' + ] ); - expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); - expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); - } ); + expect( secondWorker.workerData ).toBeInstanceOf( Object ); + expect( secondWorker.workerData ).toHaveProperty( 'packages' ); + expect( secondWorker.workerData.packages ).toBeInstanceOf( Array ); + expect( secondWorker.workerData.packages ).toEqual( [ + '/home/ckeditor/my-packages/package-02', + '/home/ckeditor/my-packages/package-04' + ] ); - it( 'should remove the temporary module if the process is aborted', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + await promise; + } ); - abortController.abort( 'SIGINT' ); + it( 'should remove the temporary module after execution', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - // Simulate the "Worker#terminate()" behavior. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - await promise; + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); - expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); - } ); + await promise; - it( 'should remove the temporary module if the promise rejected', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); + } ); - const [ firstWorker ] = stubs.WorkerMock.instances; - const error = new Error( 'Example error from a worker.' ); + it( 'should remove the temporary module if the process is aborted', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - getErrorCallback( firstWorker )( error ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - return promise - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - () => { - expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); - expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); - } - ); - } ); + abortController.abort( 'SIGINT' ); - it( 'should terminate threads if the process is aborted', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + // Simulate the "Worker#terminate()" behavior. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + await promise; - abortController.abort( 'SIGINT' ); + expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); + } ); - // Simulate the "Worker#terminate()" behavior. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + it( 'should remove the temporary module if the promise rejected', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - await promise; + const [ firstWorker ] = stubs.WorkerMock.instances; + const error = new Error( 'Example error from a worker.' ); - expect( firstWorker.terminate ).toHaveBeenCalledTimes( 1 ); - expect( secondWorker.terminate ).toHaveBeenCalledTimes( 1 ); - } ); + getErrorCallback( firstWorker )( error ); - it( 'should attach listener to a worker that executes a callback once per worker', async () => { - const signalEvent = vi.spyOn( abortController.signal, 'addEventListener' ); - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + return promise + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + () => { + expect( fs.unlink ).toHaveBeenCalledTimes( 1 ); + expect( fs.unlink ).toHaveBeenCalledWith( '/home/ckeditor/uuid-4.mjs' ); + } + ); + } ); - expect( registerAbortController ).toHaveBeenCalledTimes( 0 ); + it( 'should terminate threads if the process is aborted', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - abortController.abort( 'SIGINT' ); + abortController.abort( 'SIGINT' ); - // Simulate the "Worker#terminate()" behavior. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + // Simulate the "Worker#terminate()" behavior. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - await promise; + await promise; - expect( signalEvent ).toHaveBeenCalledTimes( 2 ); - expect( signalEvent ).toHaveBeenNthCalledWith( - 1, - 'abort', - expect.any( Function ), - expect.objectContaining( { - once: true - } ) - ); - expect( signalEvent ).toHaveBeenNthCalledWith( - 2, - 'abort', - expect.any( Function ), - expect.objectContaining( { - once: true - } ) - ); + expect( firstWorker.terminate ).toHaveBeenCalledTimes( 1 ); + expect( secondWorker.terminate ).toHaveBeenCalledTimes( 1 ); + } ); - expect( deregisterAbortController ).toHaveBeenCalledTimes( 0 ); - } ); + it( 'should attach listener to a worker that executes a callback once per worker', async () => { + const signalEvent = vi.spyOn( abortController.signal, 'addEventListener' ); + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); + + expect( registerAbortController ).toHaveBeenCalledTimes( 0 ); + + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + + abortController.abort( 'SIGINT' ); + + // Simulate the "Worker#terminate()" behavior. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); + + await promise; + + expect( signalEvent ).toHaveBeenCalledTimes( 2 ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 1, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 2, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); + + expect( deregisterAbortController ).toHaveBeenCalledTimes( 0 ); + } ); - it( 'should register and deregister default abort controller if signal is not provided', async () => { - const abortController = new AbortController(); - const signalEvent = vi.spyOn( abortController.signal, 'addEventListener' ); + it( 'should register and deregister default abort controller if signal is not provided', async () => { + const abortController = new AbortController(); + const signalEvent = vi.spyOn( abortController.signal, 'addEventListener' ); - registerAbortController.mockReturnValue( abortController ); + registerAbortController.mockReturnValue( abortController ); - const options = Object.assign( {}, defaultOptions ); - delete options.signal; + const options = Object.assign( {}, defaultOptions ); + delete options.signal; - const promise = executeInParallel( options ); - await delay( 0 ); + const promise = executeInParallel( options ); + await delay( 0 ); - expect( registerAbortController ).toHaveBeenCalledTimes( 1 ); + expect( registerAbortController ).toHaveBeenCalledTimes( 1 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - abortController.abort( 'SIGINT' ); + abortController.abort( 'SIGINT' ); - // Simulate the "Worker#terminate()" behavior. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + // Simulate the "Worker#terminate()" behavior. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - await promise; + await promise; - expect( signalEvent ).toHaveBeenCalledTimes( 2 ); - expect( signalEvent ).toHaveBeenNthCalledWith( - 1, - 'abort', - expect.any( Function ), - expect.objectContaining( { - once: true - } ) - ); - expect( signalEvent ).toHaveBeenNthCalledWith( - 2, - 'abort', - expect.any( Function ), - expect.objectContaining( { - once: true - } ) - ); + expect( signalEvent ).toHaveBeenCalledTimes( 2 ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 1, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); + expect( signalEvent ).toHaveBeenNthCalledWith( + 2, + 'abort', + expect.any( Function ), + expect.objectContaining( { + once: true + } ) + ); - expect( deregisterAbortController ).toHaveBeenCalledTimes( 1 ); - } ); + expect( deregisterAbortController ).toHaveBeenCalledTimes( 1 ); + } ); - it( 'should update the progress when a package finished executing the callback', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); - - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - - const firstWorkerPackageDone = getMessageCallback( firstWorker ); - const secondWorkerPackageDone = getMessageCallback( secondWorker ); - - expect( outputHistory ).toHaveLength( 0 ); - firstWorkerPackageDone( 'done:package' ); - expect( outputHistory ).toContain( 'Status: 1/4.' ); - expect( outputHistory ).toHaveLength( 1 ); - secondWorkerPackageDone( 'done:package' ); - expect( outputHistory ).toContain( 'Status: 2/4.' ); - expect( outputHistory ).toHaveLength( 2 ); - secondWorkerPackageDone( 'done:package' ); - expect( outputHistory ).toHaveLength( 3 ); - expect( outputHistory ).toContain( 'Status: 3/4.' ); - firstWorkerPackageDone( 'done:package' ); - expect( outputHistory ).toHaveLength( 4 ); - expect( outputHistory ).toContain( 'Status: 4/4.' ); - - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); - - await promise; - } ); + it( 'should update the progress when a package finished executing the callback', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); + + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + + const firstWorkerPackageDone = getMessageCallback( firstWorker ); + const secondWorkerPackageDone = getMessageCallback( secondWorker ); + + expect( outputHistory ).toHaveLength( 0 ); + firstWorkerPackageDone( 'done:package' ); + expect( outputHistory ).toContain( 'Status: 1/4.' ); + expect( outputHistory ).toHaveLength( 1 ); + secondWorkerPackageDone( 'done:package' ); + expect( outputHistory ).toContain( 'Status: 2/4.' ); + expect( outputHistory ).toHaveLength( 2 ); + secondWorkerPackageDone( 'done:package' ); + expect( outputHistory ).toHaveLength( 3 ); + expect( outputHistory ).toContain( 'Status: 3/4.' ); + firstWorkerPackageDone( 'done:package' ); + expect( outputHistory ).toHaveLength( 4 ); + expect( outputHistory ).toContain( 'Status: 4/4.' ); + + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); + + await promise; + } ); - it( 'should ignore messages from threads unrelated to the progress', async () => { - const promise = executeInParallel( defaultOptions ); - await delay( 0 ); + it( 'should ignore messages from threads unrelated to the progress', async () => { + const promise = executeInParallel( defaultOptions ); + await delay( 0 ); - const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; - const firstWorkerPackageDone = getMessageCallback( firstWorker ); + const [ firstWorker, secondWorker ] = stubs.WorkerMock.instances; + const firstWorkerPackageDone = getMessageCallback( firstWorker ); - expect( outputHistory ).toHaveLength( 0 ); - firstWorkerPackageDone( 'foo' ); - expect( outputHistory ).toHaveLength( 0 ); + expect( outputHistory ).toHaveLength( 0 ); + firstWorkerPackageDone( 'foo' ); + expect( outputHistory ).toHaveLength( 0 ); - // Workers did not emit an error. - getExitCallback( firstWorker )( 0 ); - getExitCallback( secondWorker )( 0 ); + // Workers did not emit an error. + getExitCallback( firstWorker )( 0 ); + getExitCallback( secondWorker )( 0 ); - await promise; - } ); + await promise; } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js index a471a8350..02ac72518 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js @@ -8,389 +8,520 @@ import compareFunc from 'compare-func'; import getWriterOptions from '../../lib/utils/getwriteroptions.js'; import generateChangelog from '../../lib/utils/generatechangelog.js'; -describe( 'dev-release-tools/utils', () => { +describe( 'generateChangelog()', () => { const url = 'https://github.com/ckeditor/ckeditor5-package'; /** * Type of commits must be equal to values returned by `transformcommitutils.getCommitType()` function. * Since we're creating all commits manually, we need to "transform" those to proper structures. */ - describe( 'generateChangelog()', () => { - describe( 'initial changelog (without "previousTag")', () => { - it( 'generates "Features" correctly', () => { - const commits = [ - { - type: 'Features', - header: 'Feature: The first an amazing feature.', - subject: 'The first an amazing feature.', - hash: 'x'.repeat( 40 ), - notes: [] - }, - { - type: 'Features', - header: 'Feature: The second an amazing feature.', - subject: 'The second an amazing feature.', - hash: 'z'.repeat( 40 ), - notes: [] - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'commit' - }; + describe( 'initial changelog (without "previousTag")', () => { + it( 'generates "Features" correctly', () => { + const commits = [ + { + type: 'Features', + header: 'Feature: The first an amazing feature.', + subject: 'The first an amazing feature.', + hash: 'x'.repeat( 40 ), + notes: [] + }, + { + type: 'Features', + header: 'Feature: The second an amazing feature.', + subject: 'The second an amazing feature.', + hash: 'z'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'commit' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) + } ); - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Features' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* The first an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' + ); + expect( changesAsArray[ 3 ] ).toEqual( + '* The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' + ); } ); + } ); - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Features' - ); - expect( changesAsArray[ 2 ] ).toEqual( - '* The first an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' - ); - expect( changesAsArray[ 3 ] ).toEqual( - '* The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' - ); - } ); + it( 'generates "Bug fixes" correctly', () => { + const commits = [ + { + type: 'Bug fixes', + header: 'Fix: The first an amazing bug fix.', + subject: 'The first an amazing bug fix.', + hash: 'x'.repeat( 40 ), + notes: [] + }, + { + type: 'Bug fixes', + header: 'Fix: The second an amazing bug fix.', + subject: 'The second an amazing bug fix.', + hash: 'z'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'commit' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) } ); - it( 'generates "Bug fixes" correctly', () => { - const commits = [ - { - type: 'Bug fixes', - header: 'Fix: The first an amazing bug fix.', - subject: 'The first an amazing bug fix.', - hash: 'x'.repeat( 40 ), - notes: [] - }, - { - type: 'Bug fixes', - header: 'Fix: The second an amazing bug fix.', - subject: 'The second an amazing bug fix.', - hash: 'z'.repeat( 40 ), - notes: [] - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'commit' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Bug fixes' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* The first an amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' + ); + expect( changesAsArray[ 3 ] ).toEqual( + '* The second an amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' + ); } ); + } ); - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Bug fixes' - ); - expect( changesAsArray[ 2 ] ).toEqual( - '* The first an amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' - ); - expect( changesAsArray[ 3 ] ).toEqual( - '* The second an amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' - ); - } ); + it( 'generates "Other changes" correctly', () => { + const commits = [ + { + type: 'Other changes', + header: 'Other: The first an amazing commit.', + subject: 'The first an amazing commit.', + hash: 'x'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: The second an amazing commit.', + subject: 'The second an amazing commit.', + hash: 'z'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'commit' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) } ); - it( 'generates "Other changes" correctly', () => { - const commits = [ - { - type: 'Other changes', - header: 'Other: The first an amazing commit.', - subject: 'The first an amazing commit.', - hash: 'x'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: The second an amazing commit.', - subject: 'The second an amazing commit.', - hash: 'z'.repeat( 40 ), - notes: [] - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'commit' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Other changes' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* The first an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' + ); + expect( changesAsArray[ 3 ] ).toEqual( + '* The second an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' + ); } ); + } ); - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Other changes' - ); - expect( changesAsArray[ 2 ] ).toEqual( - '* The first an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' - ); - expect( changesAsArray[ 3 ] ).toEqual( - '* The second an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' - ); - } ); + it( 'generates all groups correctly', () => { + const commits = [ + { + type: 'Features', + header: 'Feature: An amazing feature.', + subject: 'An amazing feature.', + hash: 'x'.repeat( 40 ), + notes: [] + }, + { + type: 'Bug fixes', + header: 'Fix: An amazing bug fix.', + subject: 'An amazing bug fix.', + hash: 'z'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: An amazing commit.', + subject: 'An amazing commit.', + hash: 'y'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'commit' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) } ); - it( 'generates all groups correctly', () => { - const commits = [ - { - type: 'Features', - header: 'Feature: An amazing feature.', - subject: 'An amazing feature.', - hash: 'x'.repeat( 40 ), - notes: [] - }, - { - type: 'Bug fixes', - header: 'Fix: An amazing bug fix.', - subject: 'An amazing bug fix.', - hash: 'z'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: An amazing commit.', - subject: 'An amazing commit.', - hash: 'y'.repeat( 40 ), - notes: [] - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'commit' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Features' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* An amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' + ); + expect( changesAsArray[ 3 ] ).toEqual( + '### Bug fixes' + ); + expect( changesAsArray[ 4 ] ).toEqual( + '* An amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' + ); + expect( changesAsArray[ 5 ] ).toEqual( + '### Other changes' + ); + expect( changesAsArray[ 6 ] ).toEqual( + '* An amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/yyyyyyy))' + ); } ); + } ); - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); + it( 'removes URLs to commits (context.skipCommitsLink=true)', () => { + const commits = [ + { + type: 'Features', + header: 'Feature: The first an amazing feature.', + subject: 'The first an amazing feature.', + hash: 'x'.repeat( 40 ), + notes: [] + }, + { + type: 'Features', + header: 'Feature: The second an amazing feature.', + subject: 'The second an amazing feature.', + hash: 'z'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + skipCommitsLink: true + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) + } ); - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Features' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* The first an amazing feature.' + ); + expect( changesAsArray[ 3 ] ).toEqual( + '* The second an amazing feature.' + ); + } ); + } ); - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Features' - ); - expect( changesAsArray[ 2 ] ).toEqual( - '* An amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))' - ); - expect( changesAsArray[ 3 ] ).toEqual( - '### Bug fixes' - ); - expect( changesAsArray[ 4 ] ).toEqual( - '* An amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' - ); - expect( changesAsArray[ 5 ] ).toEqual( - '### Other changes' - ); - expect( changesAsArray[ 6 ] ).toEqual( - '* An amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/yyyyyyy))' - ); - } ); + it( 'removes compare link from the header (context.skipCompareLink=true)', () => { + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'commit', + skipCompareLink: true + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) } ); - it( 'removes URLs to commits (context.skipCommitsLink=true)', () => { - const commits = [ - { - type: 'Features', - header: 'Feature: The first an amazing feature.', - subject: 'The first an amazing feature.', - hash: 'x'.repeat( 40 ), - notes: [] - }, - { - type: 'Features', - header: 'Feature: The second an amazing feature.', - subject: 'The second an amazing feature.', - hash: 'z'.repeat( 40 ), - notes: [] - } - ]; + return generateChangelog( [], context, options ) + .then( changes => { + changes = replaceDates( changes ); - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - skipCommitsLink: true - }; + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + expect( changesAsArray[ 0 ] ).toEqual( + '## 1.0.0 (0000-00-00)' + ); } ); + } ); - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Features' - ); - expect( changesAsArray[ 2 ] ).toEqual( - '* The first an amazing feature.' - ); - expect( changesAsArray[ 3 ] ).toEqual( - '* The second an amazing feature.' - ); - } ); + it( 'generates additional commit message below the subject', () => { + const commits = [ + { + type: 'Other changes', + header: 'Other: The first an amazing commit.', + subject: 'The first an amazing commit.', + body: [ + ' First line: Lorem Ipsum (1).', + ' Second line: Lorem Ipsum (2).' + ].join( '\n' ), + hash: 'x'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: The second an amazing commit.', + subject: 'The second an amazing commit.', + body: [ + ' First line: Lorem Ipsum (1).', + ' Second line: Lorem Ipsum (2).', + ' Third line: Lorem Ipsum (3).' + ].join( '\n' ), + hash: 'z'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: The third an amazing commit.', + subject: 'The third an amazing commit.', + hash: 'y'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'commit' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) } ); - it( 'removes compare link from the header (context.skipCompareLink=true)', () => { - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'commit', - skipCompareLink: true - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changelog = [ + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)', + '', + '### Other changes', + '', + '* The first an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))', + '', + ' First line: Lorem Ipsum (1).', + ' Second line: Lorem Ipsum (2).', + '* The second an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))', + '', + ' First line: Lorem Ipsum (1).', + ' Second line: Lorem Ipsum (2).', + ' Third line: Lorem Ipsum (3).', + '* The third an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/yyyyyyy))' + ].join( '\n' ); + + expect( changes.trim() ).toEqual( changelog ); } ); + } ); - return generateChangelog( [], context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## 1.0.0 (0000-00-00)' - ); - } ); + it( 'groups "Updated translations." commits as the single entry (merged links)', () => { + const commits = [ + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'a'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'b'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'c'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'd'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'c' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 2 ) } ); - it( 'generates additional commit message below the subject', () => { - const commits = [ - { - type: 'Other changes', - header: 'Other: The first an amazing commit.', - subject: 'The first an amazing commit.', - body: [ - ' First line: Lorem Ipsum (1).', - ' Second line: Lorem Ipsum (2).' - ].join( '\n' ), - hash: 'x'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: The second an amazing commit.', - subject: 'The second an amazing commit.', - body: [ - ' First line: Lorem Ipsum (1).', - ' Second line: Lorem Ipsum (2).', - ' Third line: Lorem Ipsum (3).' - ].join( '\n' ), - hash: 'z'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: The third an amazing commit.', - subject: 'The third an amazing commit.', - hash: 'y'.repeat( 40 ), - notes: [] - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'commit' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Other changes' + ); + /* eslint-disable max-len */ + expect( changesAsArray[ 2 ] ).toEqual( + '* Updated translations. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa), [commit](https://github.com/ckeditor/ckeditor5-package/c/bb), [commit](https://github.com/ckeditor/ckeditor5-package/c/cc), [commit](https://github.com/ckeditor/ckeditor5-package/c/dd))' + ); + /* eslint-enable max-len */ } ); + } ); - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changelog = [ - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)', - '', - '### Other changes', - '', - '* The first an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/xxxxxxx))', - '', - ' First line: Lorem Ipsum (1).', - ' Second line: Lorem Ipsum (2).', - '* The second an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))', - '', - ' First line: Lorem Ipsum (1).', - ' Second line: Lorem Ipsum (2).', - ' Third line: Lorem Ipsum (3).', - '* The third an amazing commit. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/yyyyyyy))' - ].join( '\n' ); - - expect( changes.trim() ).toEqual( changelog ); - } ); + it( 'groups "Updated translations." commits as the single entry (removed links, context.skipCommitsLink=true)', () => { + const commits = [ + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'a'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'b'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'c'.repeat( 40 ), + notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'd'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + skipCommitsLink: true + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 2 ) } ); - it( 'groups "Updated translations." commits as the single entry (merged links)', () => { + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Other changes' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* Updated translations.' + ); + } ); + } ); + + // See: https://github.com/ckeditor/ckeditor5/issues/10445. + it( + 'groups "Updated translations." commits as the single entry (merged links) even if a commit specified "skipLinks=true ' + + '(a private commit is in the middle of the collection)', + () => { const commits = [ { type: 'Other changes', @@ -399,6 +530,14 @@ describe( 'dev-release-tools/utils', () => { hash: 'a'.repeat( 40 ), notes: [] }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'x'.repeat( 40 ), + notes: [], + skipCommitsLink: true + }, { type: 'Other changes', header: 'Other: Updated translations.', @@ -419,6 +558,14 @@ describe( 'dev-release-tools/utils', () => { subject: 'Updated translations.', hash: 'd'.repeat( 40 ), notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'z'.repeat( 40 ), + notes: [], + skipCommitsLink: true } ]; @@ -455,8 +602,20 @@ describe( 'dev-release-tools/utils', () => { } ); } ); - it( 'groups "Updated translations." commits as the single entry (removed links, context.skipCommitsLink=true)', () => { + // See: https://github.com/ckeditor/ckeditor5/issues/10445. + it( + 'groups "Updated translations." commits as the single entry (merged links) even if a commit specified "skipLinks=true ' + + '(a private commit is at the beginning of the collection)', + () => { const commits = [ + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'x'.repeat( 40 ), + notes: [], + skipCommitsLink: true + }, { type: 'Other changes', header: 'Other: Updated translations.', @@ -484,6 +643,14 @@ describe( 'dev-release-tools/utils', () => { subject: 'Updated translations.', hash: 'd'.repeat( 40 ), notes: [] + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'z'.repeat( 40 ), + notes: [], + skipCommitsLink: true } ]; @@ -491,7 +658,7 @@ describe( 'dev-release-tools/utils', () => { version: '1.0.0', repoUrl: url, currentTag: 'v1.0.0', - skipCommitsLink: true + commit: 'c' }; const options = getWriterOptions( { @@ -512,277 +679,49 @@ describe( 'dev-release-tools/utils', () => { expect( changesAsArray[ 1 ] ).toEqual( '### Other changes' ); + /* eslint-disable max-len */ expect( changesAsArray[ 2 ] ).toEqual( - '* Updated translations.' + '* Updated translations. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa), [commit](https://github.com/ckeditor/ckeditor5-package/c/bb), [commit](https://github.com/ckeditor/ckeditor5-package/c/cc), [commit](https://github.com/ckeditor/ckeditor5-package/c/dd))' ); + /* eslint-enable max-len */ } ); } ); - // See: https://github.com/ckeditor/ckeditor5/issues/10445. - it( - 'groups "Updated translations." commits as the single entry (merged links) even if a commit specified "skipLinks=true ' + - '(a private commit is in the middle of the collection)', - () => { - const commits = [ - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'a'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'x'.repeat( 40 ), - notes: [], - skipCommitsLink: true - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'b'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'c'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'd'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'z'.repeat( 40 ), - notes: [], - skipCommitsLink: true - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'c' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); - - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Other changes' - ); - /* eslint-disable max-len */ - expect( changesAsArray[ 2 ] ).toEqual( - '* Updated translations. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa), [commit](https://github.com/ckeditor/ckeditor5-package/c/bb), [commit](https://github.com/ckeditor/ckeditor5-package/c/cc), [commit](https://github.com/ckeditor/ckeditor5-package/c/dd))' - ); - /* eslint-enable max-len */ - } ); - } ); - - // See: https://github.com/ckeditor/ckeditor5/issues/10445. - it( - 'groups "Updated translations." commits as the single entry (merged links) even if a commit specified "skipLinks=true ' + - '(a private commit is at the beginning of the collection)', - () => { - const commits = [ - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'x'.repeat( 40 ), - notes: [], - skipCommitsLink: true - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'a'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'b'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'c'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'd'.repeat( 40 ), - notes: [] - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'z'.repeat( 40 ), - notes: [], - skipCommitsLink: true - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'c' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); - - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Other changes' - ); - /* eslint-disable max-len */ - expect( changesAsArray[ 2 ] ).toEqual( - '* Updated translations. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa), [commit](https://github.com/ckeditor/ckeditor5-package/c/bb), [commit](https://github.com/ckeditor/ckeditor5-package/c/cc), [commit](https://github.com/ckeditor/ckeditor5-package/c/dd))' - ); - /* eslint-enable max-len */ - } ); - } ); - - // See: https://github.com/ckeditor/ckeditor5/issues/10445. - it( - 'groups "Updated translations." commits as the single entry (merged links) even if a commit specified "skipLinks=true ' + - '(all commits come from the private repository)', - () => { - const commits = [ - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'a'.repeat( 40 ), - notes: [], - skipCommitsLink: true - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'b'.repeat( 40 ), - notes: [], - skipCommitsLink: true - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'c'.repeat( 40 ), - notes: [], - skipCommitsLink: true - }, - { - type: 'Other changes', - header: 'Other: Updated translations.', - subject: 'Updated translations.', - hash: 'd'.repeat( 40 ), - notes: [], - skipCommitsLink: true - } - ]; - - const context = { - version: '1.0.0', - repoUrl: url, - currentTag: 'v1.0.0', - commit: 'c' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); - - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### Other changes' - ); - /* eslint-disable max-len */ - expect( changesAsArray[ 2 ] ).toEqual( - '* Updated translations.' - ); - /* eslint-enable max-len */ - } ); - } ); - - it( 'allows removing a URL to commit per commit', () => { + // See: https://github.com/ckeditor/ckeditor5/issues/10445. + it( + 'groups "Updated translations." commits as the single entry (merged links) even if a commit specified "skipLinks=true ' + + '(all commits come from the private repository)', + () => { const commits = [ { - type: 'Features', - header: 'Feature: (a) The first an amazing feature.', - subject: '(a) The first an amazing feature.', - hash: 'x'.repeat( 40 ), + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'a'.repeat( 40 ), notes: [], skipCommitsLink: true }, { - type: 'Features', - header: 'Feature: (b) The second an amazing feature.', - subject: '(b) The second an amazing feature.', - hash: 'z'.repeat( 40 ), - notes: [] + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'b'.repeat( 40 ), + notes: [], + skipCommitsLink: true + }, + { + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'c'.repeat( 40 ), + notes: [], + skipCommitsLink: true }, { - type: 'Features', - header: 'Feature: (c) The last one an amazing feature.', - subject: '(c) The last one an amazing feature.', - hash: 'y'.repeat( 40 ), + type: 'Other changes', + header: 'Other: Updated translations.', + subject: 'Updated translations.', + hash: 'd'.repeat( 40 ), notes: [], skipCommitsLink: true } @@ -792,11 +731,11 @@ describe( 'dev-release-tools/utils', () => { version: '1.0.0', repoUrl: url, currentTag: 'v1.0.0', - commit: 'commit' + commit: 'c' }; const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + hash: hash => hash.slice( 0, 2 ) } ); return generateChangelog( commits, context, options ) @@ -811,266 +750,325 @@ describe( 'dev-release-tools/utils', () => { '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' ); expect( changesAsArray[ 1 ] ).toEqual( - '### Features' + '### Other changes' ); + /* eslint-disable max-len */ expect( changesAsArray[ 2 ] ).toEqual( - '* (a) The first an amazing feature.' - ); - expect( changesAsArray[ 3 ] ).toEqual( - '* (b) The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' - ); - expect( changesAsArray[ 4 ] ).toEqual( - '* (c) The last one an amazing feature.' + '* Updated translations.' ); + /* eslint-enable max-len */ } ); } ); - } ); - describe( 'non-initial changelog (with "previousTag")', () => { - it( 'allows generating "internal release" (set by option, ignored all commits)', () => { - const commits = [ - { - type: 'Other changes', - header: 'Other: The first an amazing commit.', - subject: 'The first an amazing commit.', - hash: 'x'.repeat( 40 ), - notes: [] - } - ]; - - const context = { - version: '1.1.0', - repoUrl: url, - currentTag: 'v1.1.0', - previousTag: 'v1.0.0', - commit: 'commit', - isInternalRelease: true - }; + it( 'allows removing a URL to commit per commit', () => { + const commits = [ + { + type: 'Features', + header: 'Feature: (a) The first an amazing feature.', + subject: '(a) The first an amazing feature.', + hash: 'x'.repeat( 40 ), + notes: [], + skipCommitsLink: true + }, + { + type: 'Features', + header: 'Feature: (b) The second an amazing feature.', + subject: '(b) The second an amazing feature.', + hash: 'z'.repeat( 40 ), + notes: [] + }, + { + type: 'Features', + header: 'Feature: (c) The last one an amazing feature.', + subject: '(c) The last one an amazing feature.', + hash: 'y'.repeat( 40 ), + notes: [], + skipCommitsLink: true + } + ]; + + const context = { + version: '1.0.0', + repoUrl: url, + currentTag: 'v1.0.0', + commit: 'commit' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) + } ); - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### Features' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* (a) The first an amazing feature.' + ); + expect( changesAsArray[ 3 ] ).toEqual( + '* (b) The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))' + ); + expect( changesAsArray[ 4 ] ).toEqual( + '* (c) The last one an amazing feature.' + ); } ); + } ); + } ); - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); - - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); - - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - 'Internal changes only (updated dependencies, documentation, etc.).' - ); - } ); + describe( 'non-initial changelog (with "previousTag")', () => { + it( 'allows generating "internal release" (set by option, ignored all commits)', () => { + const commits = [ + { + type: 'Other changes', + header: 'Other: The first an amazing commit.', + subject: 'The first an amazing commit.', + hash: 'x'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.1.0', + repoUrl: url, + currentTag: 'v1.1.0', + previousTag: 'v1.0.0', + commit: 'commit', + isInternalRelease: true + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) } ); - it( 'allows generating "internal release" (passed an empty array of commits)', () => { - const context = { - version: '1.1.0', - repoUrl: url, - currentTag: 'v1.1.0', - previousTag: 'v1.0.0', - commit: 'commit' - }; + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); - - return generateChangelog( [], context, options ) - .then( changes => { - changes = replaceDates( changes ); + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + 'Internal changes only (updated dependencies, documentation, etc.).' + ); + } ); + } ); - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - 'Internal changes only (updated dependencies, documentation, etc.).' - ); - } ); + it( 'allows generating "internal release" (passed an empty array of commits)', () => { + const context = { + version: '1.1.0', + repoUrl: url, + currentTag: 'v1.1.0', + previousTag: 'v1.0.0', + commit: 'commit' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) } ); - it( 'generates complex changelog', () => { - const commits = [ - { - type: 'Features', - header: 'Feature (engine): The first an amazing feature.', - subject: 'The first an amazing feature.', - scope: [ 'engine' ], - hash: 'x'.repeat( 40 ), - notes: [ - { - title: 'MINOR BREAKING CHANGES', - text: 'Nothing but I would like to use the note - engine.', - scope: [ 'engine' ] - } - ] - }, - { - type: 'Features', - header: 'Feature: The second an amazing feature.', - subject: 'The second an amazing feature.', - hash: 'z'.repeat( 40 ), - notes: [] - }, - { - type: 'Bug fixes', - header: 'Fix (ui): The first amazing bug fix.', - subject: 'The first amazing bug fix.', - scope: [ 'ui' ], - hash: 'y'.repeat( 40 ), - notes: [ - { - title: 'MINOR BREAKING CHANGES', - text: 'Nothing but I would like to use the note - ui.', - scope: [ 'ui' ] - } - ] - }, - { - type: 'Other changes', - header: 'Other: Use the newest version of Node.js on CI.', - subject: 'Use the newest version of Node.js on CI.', - hash: 'a'.repeat( 40 ), - notes: [ - { - title: 'MAJOR BREAKING CHANGES', - text: 'This change should be scoped too but the script should work if the scope is being missed.', - scope: [] - } - ] - }, - { - type: 'Features', - header: 'Feature (autoformat): It just works.', - subject: 'It just works.', - scope: [ - 'autoformat', - // The tool supports multi-scoped changes but only the first one will be printed in the changelog. - 'engine' - ], - hash: 'b'.repeat( 40 ), - notes: [] - } - ]; - - const context = { - version: '1.1.0', - repoUrl: url, - currentTag: 'v1.1.0', - previousTag: 'v1.0.0', - commit: 'c' - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); + return generateChangelog( [], context, options ) + .then( changes => { + changes = replaceDates( changes ); - const sortFunction = compareFunc( item => { - if ( Array.isArray( item.scope ) ) { - return item.scope[ 0 ]; - } + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); - // A hack that allows moving all non-scoped commits or breaking changes notes at the end of the list. - return 'z'.repeat( 15 ); + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + 'Internal changes only (updated dependencies, documentation, etc.).' + ); } ); + } ); - options.commitsSort = sortFunction; - options.notesSort = sortFunction; - - return generateChangelog( commits, context, options ) - .then( changes => { - changes = replaceDates( changes ); + it( 'generates complex changelog', () => { + const commits = [ + { + type: 'Features', + header: 'Feature (engine): The first an amazing feature.', + subject: 'The first an amazing feature.', + scope: [ 'engine' ], + hash: 'x'.repeat( 40 ), + notes: [ + { + title: 'MINOR BREAKING CHANGES', + text: 'Nothing but I would like to use the note - engine.', + scope: [ 'engine' ] + } + ] + }, + { + type: 'Features', + header: 'Feature: The second an amazing feature.', + subject: 'The second an amazing feature.', + hash: 'z'.repeat( 40 ), + notes: [] + }, + { + type: 'Bug fixes', + header: 'Fix (ui): The first amazing bug fix.', + subject: 'The first amazing bug fix.', + scope: [ 'ui' ], + hash: 'y'.repeat( 40 ), + notes: [ + { + title: 'MINOR BREAKING CHANGES', + text: 'Nothing but I would like to use the note - ui.', + scope: [ 'ui' ] + } + ] + }, + { + type: 'Other changes', + header: 'Other: Use the newest version of Node.js on CI.', + subject: 'Use the newest version of Node.js on CI.', + hash: 'a'.repeat( 40 ), + notes: [ + { + title: 'MAJOR BREAKING CHANGES', + text: 'This change should be scoped too but the script should work if the scope is being missed.', + scope: [] + } + ] + }, + { + type: 'Features', + header: 'Feature (autoformat): It just works.', + subject: 'It just works.', + scope: [ + 'autoformat', + // The tool supports multi-scoped changes but only the first one will be printed in the changelog. + 'engine' + ], + hash: 'b'.repeat( 40 ), + notes: [] + } + ]; + + const context = { + version: '1.1.0', + repoUrl: url, + currentTag: 'v1.1.0', + previousTag: 'v1.0.0', + commit: 'c' + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 2 ) + } ); - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); + const sortFunction = compareFunc( item => { + if ( Array.isArray( item.scope ) ) { + return item.scope[ 0 ]; + } - expect( changesAsArray[ 0 ] ).toEqual( - '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' - ); - expect( changesAsArray[ 1 ] ).toEqual( - '### MAJOR BREAKING CHANGES' - ); - expect( changesAsArray[ 2 ] ).toEqual( - '* This change should be scoped too but the script should work if the scope is being missed.' - ); - expect( changesAsArray[ 3 ] ).toEqual( - '### MINOR BREAKING CHANGES' - ); - expect( changesAsArray[ 4 ] ).toEqual( - '* **engine**: Nothing but I would like to use the note - engine.' - ); - expect( changesAsArray[ 5 ] ).toEqual( - '* **ui**: Nothing but I would like to use the note - ui.' - ); - expect( changesAsArray[ 6 ] ).toEqual( - '### Features' - ); - expect( changesAsArray[ 7 ] ).toEqual( - '* **autoformat**: It just works. ([commit](https://github.com/ckeditor/ckeditor5-package/c/bb))' - ); - expect( changesAsArray[ 8 ] ).toEqual( - '* **engine**: The first an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/c/xx))' - ); - expect( changesAsArray[ 9 ] ).toEqual( - '* The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/c/zz))' - ); - expect( changesAsArray[ 10 ] ).toEqual( - '### Bug fixes' - ); - expect( changesAsArray[ 11 ] ).toEqual( - '* **ui**: The first amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/c/yy))' - ); - expect( changesAsArray[ 12 ] ).toEqual( - '### Other changes' - ); - expect( changesAsArray[ 13 ] ).toEqual( - '* Use the newest version of Node.js on CI. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa))' - ); - } ); + // A hack that allows moving all non-scoped commits or breaking changes notes at the end of the list. + return 'z'.repeat( 15 ); } ); - it( 'removes compare link from the header (context.skipCompareLink=true)', () => { - const context = { - version: '1.1.0', - repoUrl: url, - currentTag: 'v1.1.0', - previousTag: 'v1.0.0', - skipCompareLink: true - }; - - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) + options.commitsSort = sortFunction; + options.notesSort = sortFunction; + + return generateChangelog( commits, context, options ) + .then( changes => { + changes = replaceDates( changes ); + + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## [1.1.0](https://github.com/ckeditor/ckeditor5-package/compare/v1.0.0...v1.1.0) (0000-00-00)' + ); + expect( changesAsArray[ 1 ] ).toEqual( + '### MAJOR BREAKING CHANGES' + ); + expect( changesAsArray[ 2 ] ).toEqual( + '* This change should be scoped too but the script should work if the scope is being missed.' + ); + expect( changesAsArray[ 3 ] ).toEqual( + '### MINOR BREAKING CHANGES' + ); + expect( changesAsArray[ 4 ] ).toEqual( + '* **engine**: Nothing but I would like to use the note - engine.' + ); + expect( changesAsArray[ 5 ] ).toEqual( + '* **ui**: Nothing but I would like to use the note - ui.' + ); + expect( changesAsArray[ 6 ] ).toEqual( + '### Features' + ); + expect( changesAsArray[ 7 ] ).toEqual( + '* **autoformat**: It just works. ([commit](https://github.com/ckeditor/ckeditor5-package/c/bb))' + ); + expect( changesAsArray[ 8 ] ).toEqual( + '* **engine**: The first an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/c/xx))' + ); + expect( changesAsArray[ 9 ] ).toEqual( + '* The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/c/zz))' + ); + expect( changesAsArray[ 10 ] ).toEqual( + '### Bug fixes' + ); + expect( changesAsArray[ 11 ] ).toEqual( + '* **ui**: The first amazing bug fix. ([commit](https://github.com/ckeditor/ckeditor5-package/c/yy))' + ); + expect( changesAsArray[ 12 ] ).toEqual( + '### Other changes' + ); + expect( changesAsArray[ 13 ] ).toEqual( + '* Use the newest version of Node.js on CI. ([commit](https://github.com/ckeditor/ckeditor5-package/c/aa))' + ); } ); + } ); - return generateChangelog( [], context, options ) - .then( changes => { - changes = replaceDates( changes ); + it( 'removes compare link from the header (context.skipCompareLink=true)', () => { + const context = { + version: '1.1.0', + repoUrl: url, + currentTag: 'v1.1.0', + previousTag: 'v1.0.0', + skipCompareLink: true + }; + + const options = getWriterOptions( { + hash: hash => hash.slice( 0, 7 ) + } ); - const changesAsArray = changes.split( '\n' ) - .map( line => line.trim() ) - .filter( line => line.length ); + return generateChangelog( [], context, options ) + .then( changes => { + changes = replaceDates( changes ); - expect( changesAsArray[ 0 ] ).toEqual( - '## 1.1.0 (0000-00-00)' - ); - } ); - } ); + const changesAsArray = changes.split( '\n' ) + .map( line => line.trim() ) + .filter( line => line.length ); + + expect( changesAsArray[ 0 ] ).toEqual( + '## 1.1.0 (0000-00-00)' + ); + } ); } ); } ); - - // Replaces dates to known string. It allows comparing changelog entries to strings - // which don't depend on the date. - function replaceDates( changelog ) { - return changelog.replace( /\d{4}-\d{2}-\d{2}/g, '0000-00-00' ); - } } ); + +// Replaces dates to known string. It allows comparing changelog entries to strings +// which don't depend on the date. +function replaceDates( changelog ) { + return changelog.replace( /\d{4}-\d{2}-\d{2}/g, '0000-00-00' ); +} diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js index 8eb100753..376b8c755 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js @@ -9,153 +9,151 @@ import path from 'path'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; import getChangedFilesForCommit from '../../lib/utils/getchangedfilesforcommit.js'; -describe( 'dev-release-tools/utils', () => { +describe( 'getChangedFilesForCommit()', { timeout: 15000 }, function() { let tmpCwd, cwd; - describe( 'getChangedFilesForCommit()', { timeout: 15000 }, function() { - beforeAll( () => { - cwd = process.cwd(); - tmpCwd = fs.mkdtempSync( path.join( __dirname, '..', 'test-fixtures' ) + path.sep ); - process.chdir( tmpCwd ); - } ); - - afterAll( () => { - process.chdir( cwd ); - fs.rmdirSync( tmpCwd ); - } ); - - beforeEach( () => { - exec( 'git init' ); - - if ( process.env.CI ) { - exec( 'git config user.email "ckeditor5@ckeditor.com"' ); - exec( 'git config user.name "CKEditor5 CI"' ); - } - } ); - - afterEach( () => { - fs.rmSync( path.join( tmpCwd, '.git' ), { recursive: true } ); - fs.readdirSync( tmpCwd ).forEach( file => fs.unlinkSync( file ) ); - } ); - - it( 'returns files for initial commit', () => { - fs.writeFileSync( '1.txt', '' ); - fs.writeFileSync( '2.txt', '' ); - fs.writeFileSync( '3.txt', '' ); - fs.writeFileSync( '4.txt', '' ); - fs.writeFileSync( '5.txt', '' ); - exec( 'git add *.txt' ); - exec( 'git commit -m "Initial commit."' ); - - const files = getChangedFilesForCommit( getLastCommit() ); - - expect( files ).toEqual( [ - '1.txt', - '2.txt', - '3.txt', - '4.txt', - '5.txt' - ] ); - } ); - - it( 'returns files for next commit after initial', () => { - fs.writeFileSync( '1.txt', '' ); - fs.writeFileSync( '2.txt', '' ); - fs.writeFileSync( '3.txt', '' ); - fs.writeFileSync( '4.txt', '' ); - fs.writeFileSync( '5.txt', '' ); - exec( 'git add *.txt' ); - exec( 'git commit -m "Initial commit."' ); - - fs.writeFileSync( '2.js', '' ); - fs.writeFileSync( '3.js', '' ); - fs.writeFileSync( '4.js', '' ); - exec( 'git add *.js' ); - exec( 'git commit -m "Next commit after initial."' ); - - const files = getChangedFilesForCommit( getLastCommit() ); - - expect( files ).toEqual( [ - '2.js', - '3.js', - '4.js' - ] ); - } ); - - it( 'returns files for commit on new branch', () => { - fs.writeFileSync( '1.txt', '' ); - fs.writeFileSync( '2.txt', '' ); - fs.writeFileSync( '3.txt', '' ); - fs.writeFileSync( '4.txt', '' ); - fs.writeFileSync( '5.txt', '' ); - exec( 'git add *.txt' ); - exec( 'git commit -m "Initial commit."' ); - - fs.writeFileSync( '2.js', '' ); - fs.writeFileSync( '3.js', '' ); - fs.writeFileSync( '4.js', '' ); - exec( 'git add *.js' ); - exec( 'git commit -m "Next commit after initial."' ); - - exec( 'git checkout -b develop' ); - fs.writeFileSync( '5.json', '' ); - fs.writeFileSync( '6.json', '' ); - exec( 'git add *.json' ); - exec( 'git commit -m "New commit on branch develop."' ); - - const files = getChangedFilesForCommit( getLastCommit() ); - - expect( files ).toEqual( [ - '5.json', - '6.json' - ] ); - } ); - - it( 'returns files for merge commit', () => { - fs.writeFileSync( '1.txt', '' ); - fs.writeFileSync( '2.txt', '' ); - fs.writeFileSync( '3.txt', '' ); - fs.writeFileSync( '4.txt', '' ); - fs.writeFileSync( '5.txt', '' ); - exec( 'git add *.txt' ); - exec( 'git commit -m "Initial commit."' ); - - fs.writeFileSync( '2.js', '' ); - fs.writeFileSync( '3.js', '' ); - fs.writeFileSync( '4.js', '' ); - exec( 'git add *.js' ); - exec( 'git commit -m "Next commit after initial."' ); - - exec( 'git checkout -b develop' ); - fs.writeFileSync( '5.json', '' ); - fs.writeFileSync( '6.json', '' ); - exec( 'git add *.json' ); - exec( 'git commit -m "New commit on branch develop."' ); - - exec( 'git checkout master' ); - fs.writeFileSync( '10.sh', '' ); - fs.writeFileSync( '11.sh', '' ); - fs.writeFileSync( '12.sh', '' ); - exec( 'git add *.sh' ); - exec( 'git commit -m "New commit on branch master."' ); - - exec( 'git merge develop' ); - exec( 'git branch -d develop' ); - - const files = getChangedFilesForCommit( getLastCommit() ); - - expect( files ).toEqual( [ - '5.json', - '6.json' - ] ); - } ); + beforeAll( () => { + cwd = process.cwd(); + tmpCwd = fs.mkdtempSync( path.join( __dirname, '..', 'test-fixtures' ) + path.sep ); + process.chdir( tmpCwd ); } ); - function exec( command ) { - return tools.shExec( command, { verbosity: 'error' } ); - } + afterAll( () => { + process.chdir( cwd ); + fs.rmdirSync( tmpCwd ); + } ); + + beforeEach( () => { + exec( 'git init' ); + + if ( process.env.CI ) { + exec( 'git config user.email "ckeditor5@ckeditor.com"' ); + exec( 'git config user.name "CKEditor5 CI"' ); + } + } ); + + afterEach( () => { + fs.rmSync( path.join( tmpCwd, '.git' ), { recursive: true } ); + fs.readdirSync( tmpCwd ).forEach( file => fs.unlinkSync( file ) ); + } ); + + it( 'returns files for initial commit', () => { + fs.writeFileSync( '1.txt', '' ); + fs.writeFileSync( '2.txt', '' ); + fs.writeFileSync( '3.txt', '' ); + fs.writeFileSync( '4.txt', '' ); + fs.writeFileSync( '5.txt', '' ); + exec( 'git add *.txt' ); + exec( 'git commit -m "Initial commit."' ); + + const files = getChangedFilesForCommit( getLastCommit() ); + + expect( files ).toEqual( [ + '1.txt', + '2.txt', + '3.txt', + '4.txt', + '5.txt' + ] ); + } ); - function getLastCommit() { - return exec( 'git log -n 1 --pretty=format:"%H"' ).trim(); - } + it( 'returns files for next commit after initial', () => { + fs.writeFileSync( '1.txt', '' ); + fs.writeFileSync( '2.txt', '' ); + fs.writeFileSync( '3.txt', '' ); + fs.writeFileSync( '4.txt', '' ); + fs.writeFileSync( '5.txt', '' ); + exec( 'git add *.txt' ); + exec( 'git commit -m "Initial commit."' ); + + fs.writeFileSync( '2.js', '' ); + fs.writeFileSync( '3.js', '' ); + fs.writeFileSync( '4.js', '' ); + exec( 'git add *.js' ); + exec( 'git commit -m "Next commit after initial."' ); + + const files = getChangedFilesForCommit( getLastCommit() ); + + expect( files ).toEqual( [ + '2.js', + '3.js', + '4.js' + ] ); + } ); + + it( 'returns files for commit on new branch', () => { + fs.writeFileSync( '1.txt', '' ); + fs.writeFileSync( '2.txt', '' ); + fs.writeFileSync( '3.txt', '' ); + fs.writeFileSync( '4.txt', '' ); + fs.writeFileSync( '5.txt', '' ); + exec( 'git add *.txt' ); + exec( 'git commit -m "Initial commit."' ); + + fs.writeFileSync( '2.js', '' ); + fs.writeFileSync( '3.js', '' ); + fs.writeFileSync( '4.js', '' ); + exec( 'git add *.js' ); + exec( 'git commit -m "Next commit after initial."' ); + + exec( 'git checkout -b develop' ); + fs.writeFileSync( '5.json', '' ); + fs.writeFileSync( '6.json', '' ); + exec( 'git add *.json' ); + exec( 'git commit -m "New commit on branch develop."' ); + + const files = getChangedFilesForCommit( getLastCommit() ); + + expect( files ).toEqual( [ + '5.json', + '6.json' + ] ); + } ); + + it( 'returns files for merge commit', () => { + fs.writeFileSync( '1.txt', '' ); + fs.writeFileSync( '2.txt', '' ); + fs.writeFileSync( '3.txt', '' ); + fs.writeFileSync( '4.txt', '' ); + fs.writeFileSync( '5.txt', '' ); + exec( 'git add *.txt' ); + exec( 'git commit -m "Initial commit."' ); + + fs.writeFileSync( '2.js', '' ); + fs.writeFileSync( '3.js', '' ); + fs.writeFileSync( '4.js', '' ); + exec( 'git add *.js' ); + exec( 'git commit -m "Next commit after initial."' ); + + exec( 'git checkout -b develop' ); + fs.writeFileSync( '5.json', '' ); + fs.writeFileSync( '6.json', '' ); + exec( 'git add *.json' ); + exec( 'git commit -m "New commit on branch develop."' ); + + exec( 'git checkout master' ); + fs.writeFileSync( '10.sh', '' ); + fs.writeFileSync( '11.sh', '' ); + fs.writeFileSync( '12.sh', '' ); + exec( 'git add *.sh' ); + exec( 'git commit -m "New commit on branch master."' ); + + exec( 'git merge develop' ); + exec( 'git branch -d develop' ); + + const files = getChangedFilesForCommit( getLastCommit() ); + + expect( files ).toEqual( [ + '5.json', + '6.json' + ] ); + } ); } ); + +function exec( command ) { + return tools.shExec( command, { verbosity: 'error' } ); +} + +function getLastCommit() { + return exec( 'git log -n 1 --pretty=format:"%H"' ).trim(); +} diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 11471f436..9c66af702 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -9,289 +9,287 @@ import path from 'path'; import gitRawCommits from 'git-raw-commits'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; -describe( 'dev-release-tools/utils', () => { +describe( 'getCommits()', () => { let tmpCwd, cwd, getCommits, stubs; - describe( 'getCommits()', () => { - beforeAll( () => { - cwd = process.cwd(); - tmpCwd = fs.mkdtempSync( path.join( __dirname, '..', 'test-fixtures' ) + path.sep ); - } ); + beforeAll( () => { + cwd = process.cwd(); + tmpCwd = fs.mkdtempSync( path.join( __dirname, '..', 'test-fixtures' ) + path.sep ); + } ); - afterAll( () => { - fs.rmdirSync( tmpCwd ); - } ); + afterAll( () => { + fs.rmdirSync( tmpCwd ); + } ); - beforeEach( async () => { - process.chdir( tmpCwd ); + beforeEach( async () => { + process.chdir( tmpCwd ); - exec( 'git init' ); + exec( 'git init' ); - if ( process.env.CI ) { - exec( 'git config user.email "ckeditor5@ckeditor.com"' ); - exec( 'git config user.name "CKEditor5 CI"' ); - } + if ( process.env.CI ) { + exec( 'git config user.email "ckeditor5@ckeditor.com"' ); + exec( 'git config user.name "CKEditor5 CI"' ); + } - vi.doMock( 'git-raw-commits', () => ( { - default: vi.fn( gitRawCommits ) - } ) ); + vi.doMock( 'git-raw-commits', () => ( { + default: vi.fn( gitRawCommits ) + } ) ); - vi.doMock( '@ckeditor/ckeditor5-dev-utils' ); + vi.doMock( '@ckeditor/ckeditor5-dev-utils' ); - stubs = { - gitRawCommits: ( await import( 'git-raw-commits' ) ).default, - devTools: ( await import( '@ckeditor/ckeditor5-dev-utils' ) ).tools - }; + stubs = { + gitRawCommits: ( await import( 'git-raw-commits' ) ).default, + devTools: ( await import( '@ckeditor/ckeditor5-dev-utils' ) ).tools + }; + + getCommits = ( await import( '../../lib/utils/getcommits.js' ) ).default; + } ); + + afterEach( () => { + process.chdir( cwd ); + fs.rmSync( path.join( tmpCwd, '.git' ), { recursive: true } ); + + vi.resetModules(); + } ); - getCommits = ( await import( '../../lib/utils/getcommits.js' ) ).default; + describe( 'branch for releasing is the same as the main branch', () => { + beforeEach( async () => { + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'master\n' ); } ); - afterEach( () => { - process.chdir( cwd ); - fs.rmSync( path.join( tmpCwd, '.git' ), { recursive: true } ); + it( 'throws an error when the specified release branch is not equal to the current checked out branch', () => { + return getCommits( transformCommit, { releaseBranch: 'release' } ) + .then( + () => { + throw new Error( 'Supposed to be rejected.' ); + }, + err => { + expect( err.message ).toEqual( + 'Expected to be checked out on the release branch ("release") instead of "master". Aborting.' + ); + } + ); + } ); - vi.resetModules(); + it( 'throws an error when the default release branch is not equal to the current checked out branch', () => { + vi.mocked( stubs.devTools.shExec ).mockReset(); + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'release\n' ); + + return getCommits( transformCommit ) + .then( + () => { + throw new Error( 'Supposed to be rejected.' ); + }, + err => { + expect( err.message ).toEqual( + 'Expected to be checked out on the release branch ("master") instead of "release". Aborting.' + ); + } + ); } ); - describe( 'branch for releasing is the same as the main branch', () => { - beforeEach( async () => { - vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'master\n' ); - } ); - - it( 'throws an error when the specified release branch is not equal to the current checked out branch', () => { - return getCommits( transformCommit, { releaseBranch: 'release' } ) - .then( - () => { - throw new Error( 'Supposed to be rejected.' ); - }, - err => { - expect( err.message ).toEqual( - 'Expected to be checked out on the release branch ("release") instead of "master". Aborting.' - ); - } - ); - } ); - - it( 'throws an error when the default release branch is not equal to the current checked out branch', () => { - vi.mocked( stubs.devTools.shExec ).mockReset(); - vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'release\n' ); - - return getCommits( transformCommit ) - .then( - () => { - throw new Error( 'Supposed to be rejected.' ); - }, - err => { - expect( err.message ).toEqual( - 'Expected to be checked out on the release branch ("master") instead of "release". Aborting.' - ); - } - ); - } ); - - it( 'throws an error when repository is empty', () => { - return getCommits( transformCommit ) - .then( - () => { - throw new Error( 'Supposed to be rejected.' ); - }, - err => { - expect( err.message ).toEqual( 'Given repository is empty.' ); - } - ); - } ); - - it( 'throws an error when there is no tag or commit with specified name in given repository', () => { - return getCommits( transformCommit, { from: 'foobar' } ) - .then( - () => { - throw new Error( 'Supposed to be rejected.' ); - }, - err => { - expect( err.message ).toEqual( 'Cannot find tag or commit "foobar" in given repository.' ); - } - ); - } ); - - it( 'returns an array of commits after "git init"', () => { - exec( 'git commit --allow-empty --message "First."' ); - exec( 'git commit --allow-empty --message "Second."' ); - - return getCommits( transformCommit ) - .then( commits => { - expect( commits.length ).toEqual( 2 ); - expect( commits[ 0 ].header ).toEqual( 'Second.' ); - expect( commits[ 1 ].header ).toEqual( 'First.' ); - } ); - } ); + it( 'throws an error when repository is empty', () => { + return getCommits( transformCommit ) + .then( + () => { + throw new Error( 'Supposed to be rejected.' ); + }, + err => { + expect( err.message ).toEqual( 'Given repository is empty.' ); + } + ); + } ); - it( 'returns an array of commits after "git init" (main branch is not equal to "master")', () => { - vi.mocked( stubs.devTools.shExec ).mockReset(); - vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'main-branch\n' ); + it( 'throws an error when there is no tag or commit with specified name in given repository', () => { + return getCommits( transformCommit, { from: 'foobar' } ) + .then( + () => { + throw new Error( 'Supposed to be rejected.' ); + }, + err => { + expect( err.message ).toEqual( 'Cannot find tag or commit "foobar" in given repository.' ); + } + ); + } ); - exec( 'git checkout -b main-branch' ); - exec( 'git commit --allow-empty --message "First."' ); - exec( 'git commit --allow-empty --message "Second."' ); + it( 'returns an array of commits after "git init"', () => { + exec( 'git commit --allow-empty --message "First."' ); + exec( 'git commit --allow-empty --message "Second."' ); - return getCommits( transformCommit, { mainBranch: 'main-branch', releaseBranch: 'main-branch' } ) - .then( commits => { - expect( commits.length ).toEqual( 2 ); - expect( commits[ 0 ].header ).toEqual( 'Second.' ); - expect( commits[ 1 ].header ).toEqual( 'First.' ); - } ); - } ); - - it( 'returns an array of commits after "git init" if `options.from` is not specified', () => { - exec( 'git commit --allow-empty --message "First."' ); - exec( 'git commit --allow-empty --message "Second."' ); - exec( 'git tag v1.0.0' ); - exec( 'git commit --allow-empty --message "Third."' ); - exec( 'git commit --allow-empty --message "Fourth."' ); - - return getCommits( transformCommit ) - .then( commits => { - expect( commits.length ).toEqual( 4 ); - expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); - expect( commits[ 1 ].header ).toEqual( 'Third.' ); - expect( commits[ 2 ].header ).toEqual( 'Second.' ); - expect( commits[ 3 ].header ).toEqual( 'First.' ); - } ); - } ); - - it( 'returns an array of commits since last tag (`options.from` is specified)', () => { - exec( 'git commit --allow-empty --message "First."' ); - exec( 'git commit --allow-empty --message "Second."' ); - exec( 'git tag v1.0.0' ); - exec( 'git commit --allow-empty --message "Third."' ); - exec( 'git commit --allow-empty --message "Fourth."' ); - - return getCommits( transformCommit, { from: 'v1.0.0' } ) - .then( commits => { - expect( commits.length ).toEqual( 2 ); - expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); - expect( commits[ 1 ].header ).toEqual( 'Third.' ); - } ); - } ); + return getCommits( transformCommit ) + .then( commits => { + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'Second.' ); + expect( commits[ 1 ].header ).toEqual( 'First.' ); + } ); + } ); - it( 'returns an array of commits since specified commit (`options.from` is specified)', () => { - exec( 'git commit --allow-empty --message "First."' ); - exec( 'git commit --allow-empty --message "Second."' ); - exec( 'git tag v1.0.0' ); - exec( 'git commit --allow-empty --message "Third."' ); + it( 'returns an array of commits after "git init" (main branch is not equal to "master")', () => { + vi.mocked( stubs.devTools.shExec ).mockReset(); + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'main-branch\n' ); - const commitId = exec( 'git rev-parse HEAD' ).trim(); + exec( 'git checkout -b main-branch' ); + exec( 'git commit --allow-empty --message "First."' ); + exec( 'git commit --allow-empty --message "Second."' ); - exec( 'git commit --allow-empty --message "Fourth."' ); + return getCommits( transformCommit, { mainBranch: 'main-branch', releaseBranch: 'main-branch' } ) + .then( commits => { + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'Second.' ); + expect( commits[ 1 ].header ).toEqual( 'First.' ); + } ); + } ); - return getCommits( transformCommit, { from: commitId } ) - .then( commits => { - expect( commits.length ).toEqual( 1 ); - expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); - } ); - } ); + it( 'returns an array of commits after "git init" if `options.from` is not specified', () => { + exec( 'git commit --allow-empty --message "First."' ); + exec( 'git commit --allow-empty --message "Second."' ); + exec( 'git tag v1.0.0' ); + exec( 'git commit --allow-empty --message "Third."' ); + exec( 'git commit --allow-empty --message "Fourth."' ); + + return getCommits( transformCommit ) + .then( commits => { + expect( commits.length ).toEqual( 4 ); + expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); + expect( commits[ 1 ].header ).toEqual( 'Third.' ); + expect( commits[ 2 ].header ).toEqual( 'Second.' ); + expect( commits[ 3 ].header ).toEqual( 'First.' ); + } ); + } ); - it( 'ignores false values returned by the "transformCommit" mapper', () => { - const transformCommit = vi.fn() - .mockImplementationOnce( commit => commit ) - .mockImplementationOnce( () => null ); + it( 'returns an array of commits since last tag (`options.from` is specified)', () => { + exec( 'git commit --allow-empty --message "First."' ); + exec( 'git commit --allow-empty --message "Second."' ); + exec( 'git tag v1.0.0' ); + exec( 'git commit --allow-empty --message "Third."' ); + exec( 'git commit --allow-empty --message "Fourth."' ); + + return getCommits( transformCommit, { from: 'v1.0.0' } ) + .then( commits => { + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); + expect( commits[ 1 ].header ).toEqual( 'Third.' ); + } ); + } ); - exec( 'git commit --allow-empty --message "First."' ); - exec( 'git commit --allow-empty --message "Second."' ); + it( 'returns an array of commits since specified commit (`options.from` is specified)', () => { + exec( 'git commit --allow-empty --message "First."' ); + exec( 'git commit --allow-empty --message "Second."' ); + exec( 'git tag v1.0.0' ); + exec( 'git commit --allow-empty --message "Third."' ); - return getCommits( transformCommit ) - .then( commits => { - expect( commits.length ).toEqual( 1 ); - expect( commits[ 0 ].header ).toEqual( 'Second.' ); - } ); - } ); + const commitId = exec( 'git rev-parse HEAD' ).trim(); - it( 'handles arrays returned by the "transformCommit" mapper', () => { - const transformCommit = vi.fn() - .mockImplementationOnce( commit => { - return [ commit, commit ]; - } ); + exec( 'git commit --allow-empty --message "Fourth."' ); - exec( 'git commit --allow-empty --message "First."' ); + return getCommits( transformCommit, { from: commitId } ) + .then( commits => { + expect( commits.length ).toEqual( 1 ); + expect( commits[ 0 ].header ).toEqual( 'Fourth.' ); + } ); + } ); - return getCommits( transformCommit ) - .then( commits => { - expect( commits.length ).toEqual( 2 ); - expect( commits[ 0 ].header ).toEqual( 'First.' ); - expect( commits[ 1 ].header ).toEqual( 'First.' ); - } ); - } ); + it( 'ignores false values returned by the "transformCommit" mapper', () => { + const transformCommit = vi.fn() + .mockImplementationOnce( commit => commit ) + .mockImplementationOnce( () => null ); + + exec( 'git commit --allow-empty --message "First."' ); + exec( 'git commit --allow-empty --message "Second."' ); + + return getCommits( transformCommit ) + .then( commits => { + expect( commits.length ).toEqual( 1 ); + expect( commits[ 0 ].header ).toEqual( 'Second.' ); + } ); } ); - describe( 'branch for releasing is other than the main branch', () => { - it( 'collects commits from the main branch and the release branch', () => { - vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'release\n' ); - vi.mocked( stubs.devTools.shExec ).mockImplementationOnce( exec ); - - exec( 'git commit --allow-empty --message "Type: master: 1."' ); - exec( 'git tag v1.0.0' ); - - // Commits on master and release branches will be parsed. - exec( 'git commit --allow-empty --message "Type: master: 2."' ); - exec( 'git commit --allow-empty --message "Type: master: 3."' ); - exec( 'git commit --allow-empty --message "Type: master: 4."' ); - - exec( 'git checkout -b i/100' ); - exec( 'git commit --allow-empty --message "Type: i/100: 1."' ); - exec( 'git commit --allow-empty --message "Type: i/100: 2."' ); - exec( 'git checkout master' ); - exec( 'git merge i/100 --no-ff --message "Type: Merge i/100. master: 5"' ); - - exec( 'git checkout -b i/200' ); - exec( 'git commit --allow-empty --message "Type: i/200: 1."' ); - exec( 'git commit --allow-empty --message "Type: i/200: 2."' ); - exec( 'git commit --allow-empty --message "Type: i/200: 3."' ); - exec( 'git checkout master' ); - exec( 'git merge i/200 --no-ff --message "Type: Merge i/200. master: 6"' ); - - const baseCommit = exec( 'git rev-parse HEAD' ).trim(); - - exec( 'git checkout -b release' ); - exec( 'git commit --allow-empty --message "Type: release: 1, master 7."' ); - - exec( 'git checkout -b i/300' ); - exec( 'git commit --allow-empty --message "Type: i/300: 1."' ); - exec( 'git commit --allow-empty --message "Type: i/300: 2."' ); - exec( 'git checkout release' ); - exec( 'git merge i/300 --no-ff --message "Type: Merge i/300. release: 2, master: 8"' ); - - exec( 'git commit --allow-empty --message "Type: release: 3, master 9."' ); - exec( 'git branch -D i/100 i/200 i/300' ); - - return getCommits( transformCommit, { from: 'v1.0.0', releaseBranch: 'release' } ) - .then( commits => { - expect( commits.length ).toEqual( 8 ); - - expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 1, { - from: 'v1.0.0', - to: baseCommit, - format: '%B%n-hash-%n%H', - merges: undefined, - firstParent: true - } ); - - expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 2, { - to: 'HEAD', - from: baseCommit, - format: '%B%n-hash-%n%H', - merges: undefined, - firstParent: true - } ); - } ); - } ); + it( 'handles arrays returned by the "transformCommit" mapper', () => { + const transformCommit = vi.fn() + .mockImplementationOnce( commit => { + return [ commit, commit ]; + } ); + + exec( 'git commit --allow-empty --message "First."' ); + + return getCommits( transformCommit ) + .then( commits => { + expect( commits.length ).toEqual( 2 ); + expect( commits[ 0 ].header ).toEqual( 'First.' ); + expect( commits[ 1 ].header ).toEqual( 'First.' ); + } ); } ); } ); - function exec( command ) { - return tools.shExec( command, { verbosity: 'error' } ); - } + describe( 'branch for releasing is other than the main branch', () => { + it( 'collects commits from the main branch and the release branch', () => { + vi.mocked( stubs.devTools.shExec ).mockReturnValueOnce( 'release\n' ); + vi.mocked( stubs.devTools.shExec ).mockImplementationOnce( exec ); + + exec( 'git commit --allow-empty --message "Type: master: 1."' ); + exec( 'git tag v1.0.0' ); + + // Commits on master and release branches will be parsed. + exec( 'git commit --allow-empty --message "Type: master: 2."' ); + exec( 'git commit --allow-empty --message "Type: master: 3."' ); + exec( 'git commit --allow-empty --message "Type: master: 4."' ); + + exec( 'git checkout -b i/100' ); + exec( 'git commit --allow-empty --message "Type: i/100: 1."' ); + exec( 'git commit --allow-empty --message "Type: i/100: 2."' ); + exec( 'git checkout master' ); + exec( 'git merge i/100 --no-ff --message "Type: Merge i/100. master: 5"' ); + + exec( 'git checkout -b i/200' ); + exec( 'git commit --allow-empty --message "Type: i/200: 1."' ); + exec( 'git commit --allow-empty --message "Type: i/200: 2."' ); + exec( 'git commit --allow-empty --message "Type: i/200: 3."' ); + exec( 'git checkout master' ); + exec( 'git merge i/200 --no-ff --message "Type: Merge i/200. master: 6"' ); + + const baseCommit = exec( 'git rev-parse HEAD' ).trim(); + + exec( 'git checkout -b release' ); + exec( 'git commit --allow-empty --message "Type: release: 1, master 7."' ); + + exec( 'git checkout -b i/300' ); + exec( 'git commit --allow-empty --message "Type: i/300: 1."' ); + exec( 'git commit --allow-empty --message "Type: i/300: 2."' ); + exec( 'git checkout release' ); + exec( 'git merge i/300 --no-ff --message "Type: Merge i/300. release: 2, master: 8"' ); + + exec( 'git commit --allow-empty --message "Type: release: 3, master 9."' ); + exec( 'git branch -D i/100 i/200 i/300' ); + + return getCommits( transformCommit, { from: 'v1.0.0', releaseBranch: 'release' } ) + .then( commits => { + expect( commits.length ).toEqual( 8 ); + + expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 1, { + from: 'v1.0.0', + to: baseCommit, + format: '%B%n-hash-%n%H', + merges: undefined, + firstParent: true + } ); - // Do not modify the commit. - function transformCommit( commit ) { - return commit; - } + expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 2, { + to: 'HEAD', + from: baseCommit, + format: '%B%n-hash-%n%H', + merges: undefined, + firstParent: true + } ); + } ); + } ); + } ); } ); + +function exec( command ) { + return tools.shExec( command, { verbosity: 'error' } ); +} + +// Do not modify the commit. +function transformCommit( commit ) { + return commit; +} diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js b/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js index b257ce66f..4f2303395 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getnewversiontype.js @@ -6,112 +6,110 @@ import { describe, it, expect } from 'vitest'; import getNewVersionType from '../../lib/utils/getnewversiontype.js'; -describe( 'dev-release-tools/utils', () => { - describe( 'getSubPackagesPaths()', () => { - it( 'returns "skip" when passing an empty array of commits', () => { - expect( getNewVersionType( [] ) ).toEqual( 'skip' ); - } ); +describe( 'getSubPackagesPaths()', () => { + it( 'returns "skip" when passing an empty array of commits', () => { + expect( getNewVersionType( [] ) ).toEqual( 'skip' ); + } ); - it( 'returns "internal" when passing non-public commits', () => { - expect( getNewVersionType( [ { isPublicCommit: false } ] ) ).toEqual( 'internal' ); - } ); + it( 'returns "internal" when passing non-public commits', () => { + expect( getNewVersionType( [ { isPublicCommit: false } ] ) ).toEqual( 'internal' ); + } ); - it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Other" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Other' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'major' ); - } ); + it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Other" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Other' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'major' ); + } ); - it( 'returns "major" if BREAKING CHANGES was introduced in "type:Other" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Other' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'major' ); - } ); + it( 'returns "major" if BREAKING CHANGES was introduced in "type:Other" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Other' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'major' ); + } ); - it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Fix" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Fix' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'major' ); - } ); + it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Fix" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Fix' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'major' ); + } ); - it( 'returns "major" if BREAKING CHANGES was introduced in "type:Fix" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Fix' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'major' ); - } ); + it( 'returns "major" if BREAKING CHANGES was introduced in "type:Fix" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Fix' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'major' ); + } ); - it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Feature" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Feature' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'major' ); - } ); + it( 'returns "major" if MAJOR BREAKING CHANGES was introduced in "type:Feature" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'MAJOR BREAKING CHANGES' } ], rawType: 'Feature' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'major' ); + } ); - it( 'returns "major" if BREAKING CHANGES was introduced in "type:Feature" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Feature' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'major' ); - } ); + it( 'returns "major" if BREAKING CHANGES was introduced in "type:Feature" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'BREAKING CHANGES' } ], rawType: 'Feature' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'major' ); + } ); - it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Other" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Other' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'minor' ); - } ); + it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Other" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Other' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); + } ); - it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Fix" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Fix' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'minor' ); - } ); + it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Fix" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Fix' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); + } ); - it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Feature" commit', () => { - const commits = [ - { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Feature' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'minor' ); - } ); + it( 'returns "minor" if MINOR BREAKING CHANGES was introduced in "type:Feature" commit', () => { + const commits = [ + { isPublicCommit: true, notes: [ { title: 'MINOR BREAKING CHANGES' } ], rawType: 'Feature' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); + } ); - it( 'returns "minor" if found "type:Feature" commit in the collection', () => { - const commits = [ - { isPublicCommit: true, notes: [], rawType: 'Fix' }, - { isPublicCommit: true, notes: [], rawType: 'Other' }, - { isPublicCommit: false, notes: [], rawType: 'Docs' }, - { isPublicCommit: true, notes: [], rawType: 'Feature' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'minor' ); - } ); + it( 'returns "minor" if found "type:Feature" commit in the collection', () => { + const commits = [ + { isPublicCommit: true, notes: [], rawType: 'Fix' }, + { isPublicCommit: true, notes: [], rawType: 'Other' }, + { isPublicCommit: false, notes: [], rawType: 'Docs' }, + { isPublicCommit: true, notes: [], rawType: 'Feature' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'minor' ); + } ); - it( 'returns "major" if found "MAJOR BREAKING CHANGES" commit in the collection', () => { - const commits = [ - { isPublicCommit: true, notes: [], rawType: 'Fix' }, - { isPublicCommit: true, notes: [], rawType: 'Other' }, - { isPublicCommit: false, notes: [], rawType: 'Docs' }, - { - isPublicCommit: true, - notes: [ - { title: 'MINOR BREAKING CHANGES' }, - { title: 'MAJOR BREAKING CHANGES' } - ], - rawType: 'Feature' - } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'major' ); - } ); + it( 'returns "major" if found "MAJOR BREAKING CHANGES" commit in the collection', () => { + const commits = [ + { isPublicCommit: true, notes: [], rawType: 'Fix' }, + { isPublicCommit: true, notes: [], rawType: 'Other' }, + { isPublicCommit: false, notes: [], rawType: 'Docs' }, + { + isPublicCommit: true, + notes: [ + { title: 'MINOR BREAKING CHANGES' }, + { title: 'MAJOR BREAKING CHANGES' } + ], + rawType: 'Feature' + } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'major' ); + } ); - it( 'returns "patch" if no breaking changes or features commits were made', () => { - const commits = [ - { isPublicCommit: true, notes: [], rawType: 'Fix' }, - { isPublicCommit: true, notes: [], rawType: 'Other' } - ]; - expect( getNewVersionType( commits ) ).toEqual( 'patch' ); - } ); + it( 'returns "patch" if no breaking changes or features commits were made', () => { + const commits = [ + { isPublicCommit: true, notes: [], rawType: 'Fix' }, + { isPublicCommit: true, notes: [], rawType: 'Other' } + ]; + expect( getNewVersionType( commits ) ).toEqual( 'patch' ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js b/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js index 4f37d59dd..1a457e85b 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getnpmtagfromversion.js @@ -10,7 +10,7 @@ import getNpmTagFromVersion from '../../lib/utils/getnpmtagfromversion.js'; vi.mock( 'semver' ); -describe( 'dev-release-tools/getNpmTagFromVersion', () => { +describe( 'getNpmTagFromVersion()', () => { it( 'should return "latest" when processing a X.Y.Z version', () => { expect( getNpmTagFromVersion( '1.0.0' ) ).to.equal( 'latest' ); expect( getNpmTagFromVersion( '2.1.0' ) ).to.equal( 'latest' ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js index 30a87387d..baeb5a958 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js @@ -16,253 +16,251 @@ vi.mock( 'path', () => ( { vi.mock( '@ckeditor/ckeditor5-dev-utils' ); vi.mock( '../../lib/utils/getpackagejson.js' ); -describe( 'dev-release-tools/utils', () => { - describe( 'getPackagesPaths()', () => { - it( 'returns all found packages', () => { - vi.mocked( tools.getDirectories ).mockReturnValue( [ - 'ckeditor5-core', - 'ckeditor5-engine', - 'ckeditor5-utils' - ] ); - - vi.mocked( getPackageJson ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); - - const options = { - cwd: '/tmp', - packages: 'packages', - skipPackages: [], - skipMainRepository: true - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 3 ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 1 ); - expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); - } ); - - it( 'allows ignoring specified packages (specified as array)', () => { - vi.mocked( tools.getDirectories ).mockReturnValue( [ - 'ckeditor5-core', - 'ckeditor5-engine', - 'ckeditor5-utils' - ] ); - - vi.mocked( getPackageJson ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); - - const options = { - cwd: '/tmp', - packages: 'packages', - skipPackages: [ - '@ckeditor/ckeditor5-utils' - ], - skipMainRepository: true - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 2 ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 2 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); - } ); - - it( 'allows ignoring specified packages (specified as string)', () => { - vi.mocked( tools.getDirectories ).mockReturnValue( [ - 'ckeditor5-core', - 'ckeditor5-engine', - 'ckeditor5-utils' - ] ); - - vi.mocked( getPackageJson ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); - - const options = { - cwd: '/tmp', - packages: 'packages', - skipPackages: '@ckeditor/ckeditor5-u*', - skipMainRepository: true - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 2 ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 2 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); - } ); - - it( 'allows restricting the scope for packages', () => { - vi.mocked( tools.getDirectories ).mockReturnValue( [ - 'ckeditor5-core', - 'ckeditor5-engine', - 'ckeditor5-utils', - 'ckeditor5-build-classic', - 'ckeditor5-build-inline' - ] ); - - vi.mocked( getPackageJson ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); - - const options = { - cwd: '/tmp', - packages: 'packages', - scope: '@ckeditor/ckeditor5-build-*', - skipPackages: [], - skipMainRepository: true - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 2 ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 4 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); - } ); - - it( 'allows restricting the scope for packages and works fine with "skipPackages" option', () => { - vi.mocked( tools.getDirectories ).mockReturnValue( [ - 'ckeditor5-core', - 'ckeditor5-engine', - 'ckeditor5-utils', - 'ckeditor5-build-classic', - 'ckeditor5-build-inline' - ] ); - - vi.mocked( getPackageJson ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); - - const options = { - cwd: '/tmp', - packages: 'packages', - scope: '@ckeditor/ckeditor5-build-*', - skipPackages: [ - '@ckeditor/ckeditor5-build-inline' - ], - skipMainRepository: true - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 1 ); - expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 5 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); - } ); - - it( 'allows returning the main repository', () => { - vi.mocked( tools.getDirectories ).mockReturnValue( [ - 'ckeditor5-core', - 'ckeditor5-engine', - 'ckeditor5-utils', - 'ckeditor5-build-classic', - 'ckeditor5-build-inline' - ] ); - - vi.mocked( getPackageJson ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) - .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); - - const options = { - cwd: '/tmp', - packages: 'packages', - skipPackages: [ - '@ckeditor/ckeditor5-*' - ], - skipMainRepository: false - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 1 ); - expect( pathsCollection.matched.has( '/tmp' ) ).toEqual( true ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 5 ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); - expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); - } ); - - it( 'allows returning the main repository only (skipMainRepository=false)', () => { - const options = { - cwd: '/tmp', - packages: null - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 1 ); - expect( pathsCollection.matched.has( '/tmp' ) ).toEqual( true ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 0 ); - } ); - - it( 'allows returning the main repository only (skipMainRepository=true)', () => { - const options = { - cwd: '/tmp', - packages: null, - skipMainRepository: true - }; - - const pathsCollection = getPackagesPaths( options ); - - expect( pathsCollection.matched ).toBeInstanceOf( Set ); - expect( pathsCollection.matched.size ).toEqual( 0 ); - - expect( pathsCollection.skipped ).toBeInstanceOf( Set ); - expect( pathsCollection.skipped.size ).toEqual( 1 ); - expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); - } ); +describe( 'getPackagesPaths()', () => { + it( 'returns all found packages', () => { + vi.mocked( tools.getDirectories ).mockReturnValue( [ + 'ckeditor5-core', + 'ckeditor5-engine', + 'ckeditor5-utils' + ] ); + + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); + + const options = { + cwd: '/tmp', + packages: 'packages', + skipPackages: [], + skipMainRepository: true + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 3 ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 1 ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); + } ); + + it( 'allows ignoring specified packages (specified as array)', () => { + vi.mocked( tools.getDirectories ).mockReturnValue( [ + 'ckeditor5-core', + 'ckeditor5-engine', + 'ckeditor5-utils' + ] ); + + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); + + const options = { + cwd: '/tmp', + packages: 'packages', + skipPackages: [ + '@ckeditor/ckeditor5-utils' + ], + skipMainRepository: true + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 2 ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 2 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); + } ); + + it( 'allows ignoring specified packages (specified as string)', () => { + vi.mocked( tools.getDirectories ).mockReturnValue( [ + 'ckeditor5-core', + 'ckeditor5-engine', + 'ckeditor5-utils' + ] ); + + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ); + + const options = { + cwd: '/tmp', + packages: 'packages', + skipPackages: '@ckeditor/ckeditor5-u*', + skipMainRepository: true + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 2 ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 2 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); + } ); + + it( 'allows restricting the scope for packages', () => { + vi.mocked( tools.getDirectories ).mockReturnValue( [ + 'ckeditor5-core', + 'ckeditor5-engine', + 'ckeditor5-utils', + 'ckeditor5-build-classic', + 'ckeditor5-build-inline' + ] ); + + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); + + const options = { + cwd: '/tmp', + packages: 'packages', + scope: '@ckeditor/ckeditor5-build-*', + skipPackages: [], + skipMainRepository: true + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 2 ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 4 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); + } ); + + it( 'allows restricting the scope for packages and works fine with "skipPackages" option', () => { + vi.mocked( tools.getDirectories ).mockReturnValue( [ + 'ckeditor5-core', + 'ckeditor5-engine', + 'ckeditor5-utils', + 'ckeditor5-build-classic', + 'ckeditor5-build-inline' + ] ); + + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); + + const options = { + cwd: '/tmp', + packages: 'packages', + scope: '@ckeditor/ckeditor5-build-*', + skipPackages: [ + '@ckeditor/ckeditor5-build-inline' + ], + skipMainRepository: true + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 1 ); + expect( pathsCollection.matched.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 5 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); + } ); + + it( 'allows returning the main repository', () => { + vi.mocked( tools.getDirectories ).mockReturnValue( [ + 'ckeditor5-core', + 'ckeditor5-engine', + 'ckeditor5-utils', + 'ckeditor5-build-classic', + 'ckeditor5-build-inline' + ] ); + + vi.mocked( getPackageJson ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-core' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-engine' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-utils' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-classic' } ) + .mockReturnValueOnce( { name: '@ckeditor/ckeditor5-build-inline' } ); + + const options = { + cwd: '/tmp', + packages: 'packages', + skipPackages: [ + '@ckeditor/ckeditor5-*' + ], + skipMainRepository: false + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 1 ); + expect( pathsCollection.matched.has( '/tmp' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 5 ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-core' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-engine' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-utils' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-inline' ) ).toEqual( true ); + expect( pathsCollection.skipped.has( '/tmp/packages/ckeditor5-build-classic' ) ).toEqual( true ); + } ); + + it( 'allows returning the main repository only (skipMainRepository=false)', () => { + const options = { + cwd: '/tmp', + packages: null + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 1 ); + expect( pathsCollection.matched.has( '/tmp' ) ).toEqual( true ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 0 ); + } ); + + it( 'allows returning the main repository only (skipMainRepository=true)', () => { + const options = { + cwd: '/tmp', + packages: null, + skipMainRepository: true + }; + + const pathsCollection = getPackagesPaths( options ); + + expect( pathsCollection.matched ).toBeInstanceOf( Set ); + expect( pathsCollection.matched.size ).toEqual( 0 ); + + expect( pathsCollection.skipped ).toBeInstanceOf( Set ); + expect( pathsCollection.skipped.size ).toEqual( 1 ); + expect( pathsCollection.skipped.has( '/tmp' ) ).toEqual( true ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js index d724b791b..422c1605a 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getwriteroptions.js @@ -6,89 +6,87 @@ import { describe, it, expect, vi } from 'vitest'; import getWriterOptions from '../../lib/utils/getwriteroptions.js'; -describe( 'dev-release-tools/utils', () => { - describe( 'getWriterOptions()', () => { - const transformSpy = vi.fn(); - - it( 'returns an object with writer options', () => { - const writerOptions = getWriterOptions( transformSpy ); - - expect( writerOptions ).to.have.property( 'transform', transformSpy ); - expect( writerOptions ).to.have.property( 'groupBy' ); - expect( writerOptions ).to.have.property( 'commitGroupsSort' ); - expect( writerOptions ).to.have.property( 'commitsSort' ); - expect( writerOptions ).to.have.property( 'noteGroupsSort' ); - expect( writerOptions ).to.have.property( 'mainTemplate' ); - expect( writerOptions ).to.have.property( 'headerPartial' ); - expect( writerOptions ).to.have.property( 'footerPartial' ); - - expect( writerOptions.commitsSort ).to.be.a( 'array' ); - expect( writerOptions.commitGroupsSort ).to.be.a( 'function' ); - expect( writerOptions.noteGroupsSort ).to.be.a( 'function' ); - } ); - - it( 'sorts notes properly', () => { - const writerOptions = getWriterOptions( transformSpy ); - - const noteGroups = [ - { title: 'BREAKING CHANGES', notes: [] }, - { title: 'MINOR BREAKING CHANGES', notes: [] }, - { title: 'MAJOR BREAKING CHANGES', notes: [] } - ]; - - expect( noteGroups.sort( writerOptions.noteGroupsSort ) ).to.deep.equal( [ - { title: 'MAJOR BREAKING CHANGES', notes: [] }, - { title: 'MINOR BREAKING CHANGES', notes: [] }, - { title: 'BREAKING CHANGES', notes: [] } - ] ); - } ); - - it( 'sorts notes properly (titles with emojis)', () => { - const writerOptions = getWriterOptions( transformSpy ); - - const noteGroups = [ - { title: 'BREAKING CHANGES [ℹ](url)', notes: [] }, - { title: 'MINOR BREAKING CHANGES [ℹ](url)', notes: [] }, - { title: 'MAJOR BREAKING CHANGES [ℹ](url)', notes: [] } - ]; - - expect( noteGroups.sort( writerOptions.noteGroupsSort ) ).to.deep.equal( [ - { title: 'MAJOR BREAKING CHANGES [ℹ](url)', notes: [] }, - { title: 'MINOR BREAKING CHANGES [ℹ](url)', notes: [] }, - { title: 'BREAKING CHANGES [ℹ](url)', notes: [] } - ] ); - } ); - - it( 'sorts groups properly', () => { - const writerOptions = getWriterOptions( transformSpy ); - - const commitGroups = [ - { title: 'Other changes', commits: [] }, - { title: 'Features', commits: [] }, - { title: 'Bug fixes', commits: [] } - ]; - - expect( commitGroups.sort( writerOptions.commitGroupsSort ) ).to.deep.equal( [ - { title: 'Features', commits: [] }, - { title: 'Bug fixes', commits: [] }, - { title: 'Other changes', commits: [] } - ] ); - } ); - - it( 'sorts groups properly (titles with emojis)', () => { - const writerOptions = getWriterOptions( transformSpy ); - - const commitGroups = [ - { title: 'Other changes [ℹ](url)', commits: [] }, - { title: 'Features [ℹ](url)', commits: [] }, - { title: 'Bug fixes [ℹ](url)', commits: [] } - ]; - - expect( commitGroups.sort( writerOptions.commitGroupsSort ) ).to.deep.equal( [ - { title: 'Features [ℹ](url)', commits: [] }, - { title: 'Bug fixes [ℹ](url)', commits: [] }, - { title: 'Other changes [ℹ](url)', commits: [] } - ] ); - } ); +describe( 'getWriterOptions()', () => { + const transformSpy = vi.fn(); + + it( 'returns an object with writer options', () => { + const writerOptions = getWriterOptions( transformSpy ); + + expect( writerOptions ).to.have.property( 'transform', transformSpy ); + expect( writerOptions ).to.have.property( 'groupBy' ); + expect( writerOptions ).to.have.property( 'commitGroupsSort' ); + expect( writerOptions ).to.have.property( 'commitsSort' ); + expect( writerOptions ).to.have.property( 'noteGroupsSort' ); + expect( writerOptions ).to.have.property( 'mainTemplate' ); + expect( writerOptions ).to.have.property( 'headerPartial' ); + expect( writerOptions ).to.have.property( 'footerPartial' ); + + expect( writerOptions.commitsSort ).to.be.a( 'array' ); + expect( writerOptions.commitGroupsSort ).to.be.a( 'function' ); + expect( writerOptions.noteGroupsSort ).to.be.a( 'function' ); + } ); + + it( 'sorts notes properly', () => { + const writerOptions = getWriterOptions( transformSpy ); + + const noteGroups = [ + { title: 'BREAKING CHANGES', notes: [] }, + { title: 'MINOR BREAKING CHANGES', notes: [] }, + { title: 'MAJOR BREAKING CHANGES', notes: [] } + ]; + + expect( noteGroups.sort( writerOptions.noteGroupsSort ) ).to.deep.equal( [ + { title: 'MAJOR BREAKING CHANGES', notes: [] }, + { title: 'MINOR BREAKING CHANGES', notes: [] }, + { title: 'BREAKING CHANGES', notes: [] } + ] ); + } ); + + it( 'sorts notes properly (titles with emojis)', () => { + const writerOptions = getWriterOptions( transformSpy ); + + const noteGroups = [ + { title: 'BREAKING CHANGES [ℹ](url)', notes: [] }, + { title: 'MINOR BREAKING CHANGES [ℹ](url)', notes: [] }, + { title: 'MAJOR BREAKING CHANGES [ℹ](url)', notes: [] } + ]; + + expect( noteGroups.sort( writerOptions.noteGroupsSort ) ).to.deep.equal( [ + { title: 'MAJOR BREAKING CHANGES [ℹ](url)', notes: [] }, + { title: 'MINOR BREAKING CHANGES [ℹ](url)', notes: [] }, + { title: 'BREAKING CHANGES [ℹ](url)', notes: [] } + ] ); + } ); + + it( 'sorts groups properly', () => { + const writerOptions = getWriterOptions( transformSpy ); + + const commitGroups = [ + { title: 'Other changes', commits: [] }, + { title: 'Features', commits: [] }, + { title: 'Bug fixes', commits: [] } + ]; + + expect( commitGroups.sort( writerOptions.commitGroupsSort ) ).to.deep.equal( [ + { title: 'Features', commits: [] }, + { title: 'Bug fixes', commits: [] }, + { title: 'Other changes', commits: [] } + ] ); + } ); + + it( 'sorts groups properly (titles with emojis)', () => { + const writerOptions = getWriterOptions( transformSpy ); + + const commitGroups = [ + { title: 'Other changes [ℹ](url)', commits: [] }, + { title: 'Features [ℹ](url)', commits: [] }, + { title: 'Bug fixes [ℹ](url)', commits: [] } + ]; + + expect( commitGroups.sort( writerOptions.commitGroupsSort ) ).to.deep.equal( [ + { title: 'Features [ℹ](url)', commits: [] }, + { title: 'Bug fixes [ℹ](url)', commits: [] }, + { title: 'Other changes [ℹ](url)', commits: [] } + ] ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js b/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js index e66e35fba..de70dc13e 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/isversionpublishablefortag.js @@ -14,7 +14,7 @@ vi.mock( '@ckeditor/ckeditor5-dev-utils' ); vi.mock( 'semver' ); vi.mock( 'shell-escape' ); -describe( 'dev-release-tools/isVersionPublishableForTag', () => { +describe( 'isVersionPublishableForTag()', () => { beforeEach( () => { vi.mocked( shellEscape ).mockImplementation( v => v[ 0 ] ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js b/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js index 6557daa96..8e522dbac 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/parseroptions.js @@ -6,10 +6,8 @@ import { describe, it, expect } from 'vitest'; import parserOptions from '../../lib/utils/parseroptions.js'; -describe( 'dev-release-tools/utils', () => { - describe( 'parser-options', () => { - it( 'should not hoist closed tickets', () => { - expect( parserOptions.referenceActions ).to.deep.equal( [] ); - } ); +describe( 'parser-options', () => { + it( 'should not hoist closed tickets', () => { + expect( parserOptions.referenceActions ).to.deep.equal( [] ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js index d38e6f42d..aa49a1a50 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js @@ -11,109 +11,107 @@ import publishPackageOnNpmCallback from '../../lib/utils/publishpackageonnpmcall vi.mock( 'fs-extra' ); vi.mock( '@ckeditor/ckeditor5-dev-utils' ); -describe( 'dev-release-tools/utils', () => { - describe( 'publishPackageOnNpmCallback()', () => { - beforeEach( () => { - vi.mocked( tools.shExec ).mockResolvedValue(); - vi.mocked( fs.remove ).mockResolvedValue(); - } ); - - it( 'should publish package on npm with provided npm tag', () => { - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( () => { - expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); - expect( tools.shExec ).toHaveBeenCalledWith( - 'npm publish --access=public --tag nightly', - expect.objectContaining( { - cwd: packagePath - } ) - ); - } ); - } ); - - it( 'should publish packages on npm asynchronously', () => { - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( () => { - expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); - expect( tools.shExec ).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining( { - async: true - } ) - ); - } ); - } ); - - it( 'should set the verbosity level to "error" during publishing packages', () => { - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( () => { - expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); - expect( tools.shExec ).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining( { - verbosity: 'error' - } ) - ); - } ); - } ); - - it( 'should remove package directory after publishing on npm', () => { - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( () => { - expect( fs.remove ).toHaveBeenCalledTimes( 1 ); - expect( fs.remove ).toHaveBeenCalledWith( packagePath ); - } ); - } ); - - it( 'should throw when publishing on npm failed', () => { - vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); - - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).toBeInstanceOf( Error ); - expect( error.message ).toEqual( 'Unable to publish "ckeditor5-foo" package.' ); - } - ); - } ); +describe( 'publishPackageOnNpmCallback()', () => { + beforeEach( () => { + vi.mocked( tools.shExec ).mockResolvedValue(); + vi.mocked( fs.remove ).mockResolvedValue(); + } ); - it( 'should not remove a package directory when publishing on npm failed', () => { - vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); + it( 'should publish package on npm with provided npm tag', () => { + const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + + return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) + .then( () => { + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( + 'npm publish --access=public --tag nightly', + expect.objectContaining( { + cwd: packagePath + } ) + ); + } ); + } ); - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + it( 'should publish packages on npm asynchronously', () => { + const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + + return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) + .then( () => { + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining( { + async: true + } ) + ); + } ); + } ); - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - () => { - expect( fs.remove ).not.toHaveBeenCalled(); - } + it( 'should set the verbosity level to "error" during publishing packages', () => { + const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + + return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) + .then( () => { + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining( { + verbosity: 'error' + } ) ); - } ); + } ); + } ); + + it( 'should remove package directory after publishing on npm', () => { + const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + + return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) + .then( () => { + expect( fs.remove ).toHaveBeenCalledTimes( 1 ); + expect( fs.remove ).toHaveBeenCalledWith( packagePath ); + } ); + } ); + + it( 'should throw when publishing on npm failed', () => { + vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); + + const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + + return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + error => { + expect( error ).toBeInstanceOf( Error ); + expect( error.message ).toEqual( 'Unable to publish "ckeditor5-foo" package.' ); + } + ); + } ); + + it( 'should not remove a package directory when publishing on npm failed', () => { + vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); + + const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + + return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) + .then( + () => { + throw new Error( 'Expected to be rejected.' ); + }, + () => { + expect( fs.remove ).not.toHaveBeenCalled(); + } + ); + } ); - it( 'should not remove a package directory and not throw error when publishing on npm failed with code 409', async () => { - vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'code E409' ) ); + it( 'should not remove a package directory and not throw error when publishing on npm failed with code 409', async () => { + vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'code E409' ) ); - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; + const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - await publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ); + await publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ); - expect( fs.remove ).not.toHaveBeenCalled(); - } ); + expect( fs.remove ).not.toHaveBeenCalled(); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js index 335fc1009..ef0b185bc 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitfactory.js @@ -9,138 +9,540 @@ import transformCommitFactory from '../../lib/utils/transformcommitfactory.js'; vi.mock( '../../lib/utils/getchangedfilesforcommit.js' ); -describe( 'dev-release-tools/utils', () => { - describe( 'transformCommitFactory()', () => { - it( 'returns a function', () => { - expect( transformCommitFactory() ).to.be.a( 'function' ); +describe( 'transformCommitFactory()', () => { + it( 'returns a function', () => { + expect( transformCommitFactory() ).to.be.a( 'function' ); + } ); + + describe( 'options.treatMajorAsMinorBreakingChange = true', () => { + it( 'treats "MAJOR BREAKING CHANGES" as "MINOR BREAKING CHANGES"', () => { + const transformCommit = transformCommitFactory( { + treatMajorAsMinorBreakingChange: true, + useExplicitBreakingChangeGroups: true + } ); + + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [ + { title: 'BREAKING CHANGE', text: 'Note 1.' }, + { title: 'MAJOR BREAKING CHANGES', text: 'Note 2.' } + ] + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.notes[ 0 ].title ).to.equal( 'MINOR BREAKING CHANGES' ); + expect( commit.notes[ 1 ].title ).to.equal( 'MINOR BREAKING CHANGES' ); } ); + } ); - describe( 'options.treatMajorAsMinorBreakingChange = true', () => { - it( 'treats "MAJOR BREAKING CHANGES" as "MINOR BREAKING CHANGES"', () => { - const transformCommit = transformCommitFactory( { - treatMajorAsMinorBreakingChange: true, - useExplicitBreakingChangeGroups: true - } ); + describe( 'transformCommit()', () => { + let transformCommit; - const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix.', - type: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [ - { title: 'BREAKING CHANGE', text: 'Note 1.' }, - { title: 'MAJOR BREAKING CHANGES', text: 'Note 2.' } - ] - }; + beforeEach( () => { + transformCommit = transformCommitFactory(); + } ); - const commit = transformCommit( rawCommit ); + it( 'returns a new instance of object instead od modifying passed one', () => { + const notes = [ + { title: 'BREAKING CHANGES', text: 'Foo-Text', scope: null }, + { title: 'BREAKING CHANGES', text: 'Bar-Text', scope: null } + ]; + + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes + }; + + const commit = transformCommit( rawCommit ); + + // Notes cannot be the same but they should be equal. + expect( commit.notes ).to.not.equal( rawCommit.notes ); + expect( commit.notes ).to.deep.equal( rawCommit.notes ); + } ); - expect( commit.notes[ 0 ].title ).to.equal( 'MINOR BREAKING CHANGES' ); - expect( commit.notes[ 1 ].title ).to.equal( 'MINOR BREAKING CHANGES' ); - } ); + it( 'returns files that were changed with the commit', () => { + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [] + }; + + const files = [ + 'a/b/y.txt', + 'c/d/z.md' + ]; + + vi.mocked( getChangedFilesForCommit ).mockReturnValue( files ); + + const commit = transformCommit( rawCommit ); + + expect( getChangedFilesForCommit ).toHaveBeenCalledTimes( 1 ); + expect( commit.files ).to.deep.equal( files ); } ); - describe( 'transformCommit()', () => { - let transformCommit; + it( 'returns non-public commit', () => { + const rawCommit = { + hash: '684997d', + header: 'Docs: README.', + type: 'Docs', + subject: 'README.', + body: null, + footer: null, + notes: [] + }; + + const newCommit = transformCommit( rawCommit ); + + expect( newCommit ).to.not.equal( undefined ); + } ); - beforeEach( () => { - transformCommit = transformCommitFactory(); + it( 'groups "BREAKING CHANGES" and "BREAKING CHANGE" as "MAJOR BREAKING CHANGES"', () => { + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [ + { title: 'BREAKING CHANGE', text: 'Note 1.' }, + { title: 'BREAKING CHANGES', text: 'Note 2.' } + ] + }; + + const transformCommit = transformCommitFactory( { + useExplicitBreakingChangeGroups: true } ); + const commit = transformCommit( rawCommit ); + + expect( commit.notes[ 0 ].title ).to.equal( 'MAJOR BREAKING CHANGES' ); + expect( commit.notes[ 1 ].title ).to.equal( 'MAJOR BREAKING CHANGES' ); + } ); + + it( 'makes proper links in the commit subject', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix: Simple fix. See ckeditor/ckeditor5#1. Thanks to @CKEditor. Closes #2.', + type: 'Fix', + subject: 'Simple fix. See ckeditor/ckeditor5#1. Thanks to @CKEditor. Closes #2.', + body: null, + footer: null, + notes: [] + }; + + const commit = transformCommit( rawCommit ); + + const expectedSubject = 'Simple fix. ' + + 'See [ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1). ' + + 'Thanks to [@CKEditor](https://github.com/CKEditor). ' + + 'Closes [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2).'; + + expect( commit.subject ).to.equal( expectedSubject ); + } ); + + it( 'makes proper links in the commit body', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix: Simple fix. Closes #2.', + type: 'Fix', + subject: 'Simple fix. Closes #2', + body: 'See ckeditor/ckeditor5#1. Thanks to @CKEditor. Read more #2.', + footer: null, + notes: [] + }; + + const commit = transformCommit( rawCommit ); + + // Remember about the indent in commit body. + const expectedBody = ' See [ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1). ' + + 'Thanks to [@CKEditor](https://github.com/CKEditor). ' + + 'Read more [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2).'; + + expect( commit.body ).to.equal( expectedBody ); + } ); + + it( 'makes proper links in the commit notes', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix: Simple fix. Closes #2.', + type: 'Fix', + subject: 'Simple fix. Closes #2', + body: null, + footer: null, + notes: [ + { + title: 'BREAKING CHANGES', + text: 'See ckeditor/ckeditor5#1. Thanks to @CKEditor.' + }, + { + title: 'BREAKING CHANGES', + text: 'Read more #2.' + } + ] + }; + + const commit = transformCommit( rawCommit ); + + const expectedFirstNoteText = 'See [ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1). ' + + 'Thanks to [@CKEditor](https://github.com/CKEditor).'; + + const expectedSecondNodeText = 'Read more [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2).'; + + expect( commit.notes[ 0 ].text ).to.equal( expectedFirstNoteText ); + expect( commit.notes[ 1 ].text ).to.equal( expectedSecondNodeText ); + } ); + + it( 'attaches additional commit description with correct indent', () => { + const commitDescription = [ + '* Release task - rebuilt module for collecting dependencies to release.', + '* Used `semver` package for bumping the version (instead of a custom module).' + ]; + + const commitDescriptionWithIndents = [ + ' * Release task - rebuilt module for collecting dependencies to release.', + ' * Used `semver` package for bumping the version (instead of a custom module).' + ].join( '\n' ); + + const rawCommit = { + header: 'Feature: Introduced a brand new release tools with a new set of requirements. See #64.', + hash: 'dea35014ab610be0c2150343c6a8a68620cfe5ad', + body: commitDescription.join( '\n' ), + footer: null, + mentions: [], + type: 'Feature', + subject: 'Introduced a brand new release tools with a new set of requirements. See #64.', + notes: [] + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.type ).to.equal( 'Features' ); + expect( commit.subject ).to.equal( 'Introduced a brand new release tools with a new set of requirements. ' + + 'See [#64](https://github.com/ckeditor/ckeditor5-dev/issues/64).' ); + expect( commit.body ).to.equal( commitDescriptionWithIndents ); + } ); + + it( 'removes references to issues', () => { + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [], + references: [ + { issue: '11' }, + { issue: '12' } + ] + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.references ).to.equal( undefined ); + } ); + + it( 'uses commit\'s footer as a commit\'s body when commit does not have additional notes', () => { + const rawCommit = { + hash: 'dea35014ab610be0c2150343c6a8a68620cfe5ad', + header: 'Feature: Introduced a brand new release tools with a new set of requirements.', + type: 'Feature', + subject: 'Introduced a brand new release tools with a new set of requirements.', + body: null, + footer: 'Additional description has been parsed as a footer but it should be a body.', + notes: [] + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.body ).to.equal( + ' Additional description has been parsed as a footer but it should be a body.' + ); + expect( commit.footer ).to.equal( null ); + } ); + + it( 'removes [skip ci] from the commit message', () => { + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: README. [skip ci]', + type: 'Fix', + subject: 'README. [skip ci]', + body: null, + footer: null, + notes: [] + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.subject ).to.equal( 'README.' ); + } ); + + it( 'includes "repositoryUrl" where the commit has been done', () => { + const notes = [ + { title: 'Foo', text: 'Foo-Text' }, + { title: 'Bar', text: 'Bar-Text' } + ]; + + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.repositoryUrl ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); + + it( 'treats all "* BREAKING CHANGES" notes as "BREAKING CHANGE"', () => { + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: null, + notes: [ + { title: 'BREAKING CHANGE', text: 'Note 1.' }, + { title: 'BREAKING CHANGES', text: 'Note 2.' }, + { title: 'MAJOR BREAKING CHANGE', text: 'Note 3.' }, + { title: 'MAJOR BREAKING CHANGES', text: 'Note 4.' }, + { title: 'MINOR BREAKING CHANGE', text: 'Note 5.' }, + { title: 'MINOR BREAKING CHANGES', text: 'Note 6.' } + ] + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.notes[ 0 ].title ).to.equal( 'BREAKING CHANGES' ); + expect( commit.notes[ 1 ].title ).to.equal( 'BREAKING CHANGES' ); + expect( commit.notes[ 2 ].title ).to.equal( 'BREAKING CHANGES' ); + expect( commit.notes[ 3 ].title ).to.equal( 'BREAKING CHANGES' ); + expect( commit.notes[ 4 ].title ).to.equal( 'BREAKING CHANGES' ); + expect( commit.notes[ 5 ].title ).to.equal( 'BREAKING CHANGES' ); + } ); + + it( 'removes duplicated notes from the footer', () => { + const notes = [ + { title: 'BREAKING CHANGES', text: 'Foo.', scope: null }, + { title: 'BREAKING CHANGES', text: 'Bar-Text.', scope: null } + ]; + + const rawCommit = { + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix.', + type: 'Fix', + subject: 'Simple fix.', + body: null, + footer: [ + 'BREAKING CHANGES: Foo.', + 'NOTE: Do not remove me.', + 'BREAKING CHANGES: Bar-Text.' + ].join( '\n' ), + notes + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.body ).to.equal( ' NOTE: Do not remove me.' ); + expect( commit.notes ).to.deep.equal( notes ); + } ); + + // See: https://github.com/ckeditor/ckeditor5/issues/7495. + it( 'removes duplicated scoped notes from the footer', () => { + const rawCommit = { + type: 'Other (table)', + subject: 'Extracted `TableMouse` plugin from `TableSelection` plugin. Closes #6757.', + merge: 'Merge pull request #7355 from ckeditor/i/6757', + header: 'Other (table): Extracted `TableMouse` plugin from `TableSelection` plugin. Closes #6757.', + body: null, + footer: 'MINOR BREAKING CHANGE (table): The `TableNavigation` plugin renamed to `TableKeyboard`.', + notes: [ + { + title: 'MINOR BREAKING CHANGE', + text: '(table): The `TableNavigation` plugin renamed to `TableKeyboard`.' + } + ], + references: [], + mentions: [], + revert: null, + hash: '4d2f5f9b9f298601b332f304da66333c52673cb8' + }; + + const commit = transformCommit( rawCommit ); + + expect( commit.footer ).to.equal( null ); + } ); + + // See: https://github.com/ckeditor/ckeditor5/issues/7489. + describe( 'internal merge commits', () => { + const mergeCommitsToIgnore = [ + 'Merge branch \'stable\'', + 'Merge branch \'master\'', + 'Merge branch \'release\'', + 'Merge \'stable\' into \'master\'', + 'Merge \'master\' into \'release\'', + 'Merge \'release\' into \'stable\'', + 'Merge branch \'stable\' into \'master\'', + 'Merge branch \'master\' into \'release\'', + 'Merge branch \'release\' into \'stable\'', + 'Merge branch \'stable\' into master', + 'Merge branch \'master\' into release', + 'Merge branch \'release\' into stable', + 'Merge branch stable into \'master\'', + 'Merge branch master into \'release\'', + 'Merge branch release into \'stable\'', + 'Merge branch stable into master', + 'Merge branch master into release', + 'Merge branch release into stable', + 'Merge remote-tracking branch \'origin/master\' into i/6788-feature-branch', + 'Merge branch \'master\' into i/6788-feature-branch', + 'Merge branch master into i/6788-feature-branch' + ]; + + const validMergeCommits = [ + 'Merge pull request #7485 from ckeditor/i/6788-feature-branch', + 'Merge branch \'i/6788-feature-branch\'', + 'Merge branch i/6788-feature-branch' + ]; + + for ( const commitTitle of mergeCommitsToIgnore ) { + it( `ignores a commit: "${ commitTitle }"`, () => { + const rawCommit = { + merge: commitTitle, + header: '-hash-', + body: '575e00bc8ece48826adefe226c4fb1fe071c73a7', + notes: [] + }; + + expect( transformCommit( rawCommit ) ).to.equal( undefined ); + } ); + } - it( 'returns a new instance of object instead od modifying passed one', () => { - const notes = [ - { title: 'BREAKING CHANGES', text: 'Foo-Text', scope: null }, - { title: 'BREAKING CHANGES', text: 'Bar-Text', scope: null } - ]; + for ( const commitTitle of validMergeCommits ) { + it( `does not ignore a commit: "${ commitTitle }"`, () => { + const rawCommit = { + merge: commitTitle, + header: '-hash-', + body: '575e00bc8ece48826adefe226c4fb1fe071c73a7', + notes: [] + }; + expect( transformCommit( rawCommit ) ).to.not.equal( undefined ); + } ); + } + } ); + + describe( '"Closes" references - merging into single entry', () => { + it( 'works for #id pattern', () => { const rawCommit = { hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix.', + header: 'Fix: Simple fix. Closes #1. Closes #2. Closes #3.', type: 'Fix', - subject: 'Simple fix.', + subject: 'Simple fix. Closes #1. Closes #2. Closes #3.', body: null, footer: null, - notes + notes: [] }; const commit = transformCommit( rawCommit ); - // Notes cannot be the same but they should be equal. - expect( commit.notes ).to.not.equal( rawCommit.notes ); - expect( commit.notes ).to.deep.equal( rawCommit.notes ); + const expectedSubject = 'Simple fix. Closes ' + + '[#1](https://github.com/ckeditor/ckeditor5-dev/issues/1), ' + + '[#2](https://github.com/ckeditor/ckeditor5-dev/issues/2), ' + + '[#3](https://github.com/ckeditor/ckeditor5-dev/issues/3).'; + + expect( commit.subject ).to.equal( expectedSubject ); } ); - it( 'returns files that were changed with the commit', () => { + it( 'works for org/repo#id pattern', () => { const rawCommit = { hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix.', + header: 'Fix: Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes ckeditor/ckeditor5#3.', type: 'Fix', - subject: 'Simple fix.', + subject: 'Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes ckeditor/ckeditor5#3.', body: null, footer: null, notes: [] }; - const files = [ - 'a/b/y.txt', - 'c/d/z.md' - ]; - - vi.mocked( getChangedFilesForCommit ).mockReturnValue( files ); - const commit = transformCommit( rawCommit ); - expect( getChangedFilesForCommit ).toHaveBeenCalledTimes( 1 ); - expect( commit.files ).to.deep.equal( files ); + const expectedSubject = 'Simple fix. Closes ' + + '[ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1), ' + + '[ckeditor/ckeditor5#2](https://github.com/ckeditor/ckeditor5/issues/2), ' + + '[ckeditor/ckeditor5#3](https://github.com/ckeditor/ckeditor5/issues/3).'; + + expect( commit.subject ).to.equal( expectedSubject ); } ); - it( 'returns non-public commit', () => { + it( 'works for mixed #id and org/repo#id patterns, starting with #id', () => { const rawCommit = { - hash: '684997d', - header: 'Docs: README.', - type: 'Docs', - subject: 'README.', + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix. Closes #1. Closes #2. Closes ckeditor/ckeditor5#3.', + type: 'Fix', + subject: 'Simple fix. Closes #1. Closes #2. Closes ckeditor/ckeditor5#3.', body: null, footer: null, notes: [] }; - const newCommit = transformCommit( rawCommit ); + const commit = transformCommit( rawCommit ); + + const expectedSubject = 'Simple fix. Closes ' + + '[#1](https://github.com/ckeditor/ckeditor5-dev/issues/1), ' + + '[#2](https://github.com/ckeditor/ckeditor5-dev/issues/2), ' + + '[ckeditor/ckeditor5#3](https://github.com/ckeditor/ckeditor5/issues/3).'; - expect( newCommit ).to.not.equal( undefined ); + expect( commit.subject ).to.equal( expectedSubject ); } ); - it( 'groups "BREAKING CHANGES" and "BREAKING CHANGE" as "MAJOR BREAKING CHANGES"', () => { + it( 'works for mixed #id and org/repo#id patterns, starting with org/repo#id', () => { const rawCommit = { hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix.', + header: 'Fix: Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes #3.', type: 'Fix', - subject: 'Simple fix.', + subject: 'Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes #3.', body: null, footer: null, - notes: [ - { title: 'BREAKING CHANGE', text: 'Note 1.' }, - { title: 'BREAKING CHANGES', text: 'Note 2.' } - ] + notes: [] }; - const transformCommit = transformCommitFactory( { - useExplicitBreakingChangeGroups: true - } ); const commit = transformCommit( rawCommit ); - expect( commit.notes[ 0 ].title ).to.equal( 'MAJOR BREAKING CHANGES' ); - expect( commit.notes[ 1 ].title ).to.equal( 'MAJOR BREAKING CHANGES' ); + const expectedSubject = 'Simple fix. Closes ' + + '[ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1), ' + + '[ckeditor/ckeditor5#2](https://github.com/ckeditor/ckeditor5/issues/2), ' + + '[#3](https://github.com/ckeditor/ckeditor5-dev/issues/3).'; + + expect( commit.subject ).to.equal( expectedSubject ); } ); - it( 'makes proper links in the commit subject', () => { + it( 'does no touch the "See #" references.', () => { const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix. See ckeditor/ckeditor5#1. Thanks to @CKEditor. Closes #2.', + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix. Closes #1. Closes #2. See #3, #.', type: 'Fix', - subject: 'Simple fix. See ckeditor/ckeditor5#1. Thanks to @CKEditor. Closes #2.', + subject: 'Simple fix. Closes #1. Closes #2. See #3, #4.', body: null, footer: null, notes: [] @@ -148,1387 +550,983 @@ describe( 'dev-release-tools/utils', () => { const commit = transformCommit( rawCommit ); - const expectedSubject = 'Simple fix. ' + - 'See [ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1). ' + - 'Thanks to [@CKEditor](https://github.com/CKEditor). ' + - 'Closes [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2).'; + const expectedSubject = 'Simple fix. Closes ' + + '[#1](https://github.com/ckeditor/ckeditor5-dev/issues/1), ' + + '[#2](https://github.com/ckeditor/ckeditor5-dev/issues/2). ' + + 'See [#3](https://github.com/ckeditor/ckeditor5-dev/issues/3), ' + + '[#4](https://github.com/ckeditor/ckeditor5-dev/issues/4).'; expect( commit.subject ).to.equal( expectedSubject ); } ); - it( 'makes proper links in the commit body', () => { + it( 'does not replace paths with hash as github issue', () => { const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix. Closes #2.', + hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + header: 'Fix: Simple fix. Closes i/am/path5#1.', type: 'Fix', - subject: 'Simple fix. Closes #2', - body: 'See ckeditor/ckeditor5#1. Thanks to @CKEditor. Read more #2.', + subject: 'Fix: Simple fix. Closes i/am/path5#1.', + body: null, footer: null, notes: [] }; const commit = transformCommit( rawCommit ); - // Remember about the indent in commit body. - const expectedBody = ' See [ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1). ' + - 'Thanks to [@CKEditor](https://github.com/CKEditor). ' + - 'Read more [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2).'; - - expect( commit.body ).to.equal( expectedBody ); + expect( commit.subject ).to.equal( 'Fix: Simple fix. Closes i/am/path5#1.' ); } ); + } ); - it( 'makes proper links in the commit notes', () => { + describe( 'scopes', () => { + it( 'returns null if the scope is being missed', () => { const rawCommit = { hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix. Closes #2.', + header: 'Fix: Simple fix.', type: 'Fix', - subject: 'Simple fix. Closes #2', + subject: 'Simple fix.', body: null, footer: null, - notes: [ - { - title: 'BREAKING CHANGES', - text: 'See ckeditor/ckeditor5#1. Thanks to @CKEditor.' - }, - { - title: 'BREAKING CHANGES', - text: 'Read more #2.' - } - ] + notes: [] }; const commit = transformCommit( rawCommit ); - const expectedFirstNoteText = 'See [ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1). ' + - 'Thanks to [@CKEditor](https://github.com/CKEditor).'; - - const expectedSecondNodeText = 'Read more [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2).'; - - expect( commit.notes[ 0 ].text ).to.equal( expectedFirstNoteText ); - expect( commit.notes[ 1 ].text ).to.equal( expectedSecondNodeText ); + expect( commit.scope ).to.be.equal( null ); } ); - it( 'attaches additional commit description with correct indent', () => { - const commitDescription = [ - '* Release task - rebuilt module for collecting dependencies to release.', - '* Used `semver` package for bumping the version (instead of a custom module).' - ]; - - const commitDescriptionWithIndents = [ - ' * Release task - rebuilt module for collecting dependencies to release.', - ' * Used `semver` package for bumping the version (instead of a custom module).' - ].join( '\n' ); - + it( 'extracts the scope from the commit type', () => { const rawCommit = { - header: 'Feature: Introduced a brand new release tools with a new set of requirements. See #64.', - hash: 'dea35014ab610be0c2150343c6a8a68620cfe5ad', - body: commitDescription.join( '\n' ), + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix (package): Simple fix.', + type: 'Fix (package)', + subject: 'Simple fix.', + body: null, footer: null, - mentions: [], - type: 'Feature', - subject: 'Introduced a brand new release tools with a new set of requirements. See #64.', notes: [] }; const commit = transformCommit( rawCommit ); - expect( commit.type ).to.equal( 'Features' ); - expect( commit.subject ).to.equal( 'Introduced a brand new release tools with a new set of requirements. ' + - 'See [#64](https://github.com/ckeditor/ckeditor5-dev/issues/64).' ); - expect( commit.body ).to.equal( commitDescriptionWithIndents ); + expect( commit.scope ).to.be.an( 'Array' ); + expect( commit.scope.length ).to.equal( 1 ); + expect( commit.scope[ 0 ] ).to.equal( 'package' ); + expect( commit.rawType ).to.equal( 'Fix' ); } ); - it( 'removes references to issues', () => { + it( 'works with multi-scoped changes (commit type)', () => { const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix.', - type: 'Fix', + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature (foo, bar): Simple fix.', + type: 'Feature (foo, bar)', subject: 'Simple fix.', body: null, footer: null, - notes: [], - references: [ - { issue: '11' }, - { issue: '12' } - ] + notes: [] }; const commit = transformCommit( rawCommit ); - expect( commit.references ).to.equal( undefined ); + expect( commit ).to.be.an( 'Array' ); + expect( commit.length ).to.equal( 2 ); + + expect( commit[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit[ 0 ].scope.length ).to.equal( 1 ); + expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'bar' ); + expect( commit[ 0 ].rawType ).to.equal( 'Feature' ); + + expect( commit[ 1 ].scope ).to.be.an( 'Array' ); + expect( commit[ 1 ].scope.length ).to.equal( 1 ); + expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'foo' ); + expect( commit[ 1 ].rawType ).to.equal( 'Feature' ); } ); - it( 'uses commit\'s footer as a commit\'s body when commit does not have additional notes', () => { + it( 'works with multi-scoped merge commit', () => { const rawCommit = { - hash: 'dea35014ab610be0c2150343c6a8a68620cfe5ad', - header: 'Feature: Introduced a brand new release tools with a new set of requirements.', - type: 'Feature', - subject: 'Introduced a brand new release tools with a new set of requirements.', + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature (foo, bar): Simple fix.', + type: 'Feature (foo, bar)', + subject: 'Simple fix.', + merge: 'Merge pull request #7355 from ckeditor/i/6757', body: null, - footer: 'Additional description has been parsed as a footer but it should be a body.', + footer: null, notes: [] }; const commit = transformCommit( rawCommit ); - expect( commit.body ).to.equal( - ' Additional description has been parsed as a footer but it should be a body.' - ); - expect( commit.footer ).to.equal( null ); + expect( commit ).to.be.an( 'Array' ); + expect( commit.length ).to.equal( 2 ); + + expect( commit[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit[ 0 ].scope.length ).to.equal( 1 ); + expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'bar' ); + expect( commit[ 0 ].rawType ).to.equal( 'Feature' ); + + expect( commit[ 1 ].scope ).to.be.an( 'Array' ); + expect( commit[ 1 ].scope.length ).to.equal( 1 ); + expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'foo' ); + expect( commit[ 1 ].rawType ).to.equal( 'Feature' ); } ); - it( 'removes [skip ci] from the commit message', () => { + it( 'clones the commit properties for multi-scoped changes', () => { const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: README. [skip ci]', - type: 'Fix', - subject: 'README. [skip ci]', + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature (foo, bar): Simple fix.', + type: 'Feature (foo, bar)', + subject: 'Simple fix.', body: null, footer: null, - notes: [] + notes: [ + { + title: 'BREAKING CHANGES', + text: '(package): Foo.' + } + ] }; const commit = transformCommit( rawCommit ); - expect( commit.subject ).to.equal( 'README.' ); - } ); + expect( commit ).to.be.an( 'Array' ); + expect( commit.length ).to.equal( 2 ); - it( 'includes "repositoryUrl" where the commit has been done', () => { - const notes = [ - { title: 'Foo', text: 'Foo-Text' }, - { title: 'Bar', text: 'Bar-Text' } - ]; + expect( commit[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit[ 1 ].scope ).to.be.an( 'Array' ); + expect( commit[ 0 ].scope ).to.not.equal( commit[ 1 ].scope ); + + expect( commit[ 0 ].files ).to.be.an( 'Array' ); + expect( commit[ 1 ].files ).to.be.an( 'Array' ); + expect( commit[ 0 ].files ).to.not.equal( commit[ 1 ].files ); + + expect( commit[ 0 ].notes ).to.be.an( 'Array' ); + expect( commit[ 1 ].notes ).to.be.an( 'Array' ); + expect( commit[ 0 ].notes ).to.not.equal( commit[ 1 ].notes ); + + expect( commit[ 0 ].notes[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit[ 0 ].notes[ 0 ].scope[ 0 ] ).to.equal( 'package' ); + expect( commit[ 1 ].notes ).to.be.an( 'Array' ); + } ); + it( 'extracts the scope from notes', () => { const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', header: 'Fix: Simple fix.', type: 'Fix', subject: 'Simple fix.', body: null, footer: null, - notes + notes: [ + { + title: 'BREAKING CHANGES', + text: '(package): Foo.' + } + ] }; const commit = transformCommit( rawCommit ); - expect( commit.repositoryUrl ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + expect( commit.notes ).to.be.an( 'Array' ); + expect( commit.notes.length ).to.equal( 1 ); + expect( commit.notes[ 0 ] ).to.be.an( 'Object' ); + expect( commit.notes[ 0 ].text ).to.equal( 'Foo.' ); + expect( commit.notes[ 0 ].title ).to.equal( 'BREAKING CHANGES' ); + expect( commit.notes[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit.notes[ 0 ].scope.length ).to.equal( 1 ); + expect( commit.notes[ 0 ].scope[ 0 ] ).to.equal( 'package' ); } ); - it( 'treats all "* BREAKING CHANGES" notes as "BREAKING CHANGE"', () => { + it( 'works with multi-scoped notes', () => { const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', header: 'Fix: Simple fix.', type: 'Fix', subject: 'Simple fix.', body: null, footer: null, notes: [ - { title: 'BREAKING CHANGE', text: 'Note 1.' }, - { title: 'BREAKING CHANGES', text: 'Note 2.' }, - { title: 'MAJOR BREAKING CHANGE', text: 'Note 3.' }, - { title: 'MAJOR BREAKING CHANGES', text: 'Note 4.' }, - { title: 'MINOR BREAKING CHANGE', text: 'Note 5.' }, - { title: 'MINOR BREAKING CHANGES', text: 'Note 6.' } + { + title: 'BREAKING CHANGES', + text: '(foo, bar): Package.' + } ] }; const commit = transformCommit( rawCommit ); + expect( commit.notes ).to.be.an( 'Array' ); + expect( commit.notes.length ).to.equal( 1 ); + expect( commit.notes[ 0 ] ).to.be.an( 'Object' ); + expect( commit.notes[ 0 ].text ).to.equal( 'Package.' ); expect( commit.notes[ 0 ].title ).to.equal( 'BREAKING CHANGES' ); - expect( commit.notes[ 1 ].title ).to.equal( 'BREAKING CHANGES' ); - expect( commit.notes[ 2 ].title ).to.equal( 'BREAKING CHANGES' ); - expect( commit.notes[ 3 ].title ).to.equal( 'BREAKING CHANGES' ); - expect( commit.notes[ 4 ].title ).to.equal( 'BREAKING CHANGES' ); - expect( commit.notes[ 5 ].title ).to.equal( 'BREAKING CHANGES' ); + expect( commit.notes[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit.notes[ 0 ].scope.length ).to.equal( 2 ); + expect( commit.notes[ 0 ].scope[ 0 ] ).to.equal( 'bar' ); + expect( commit.notes[ 0 ].scope[ 1 ] ).to.equal( 'foo' ); } ); - it( 'removes duplicated notes from the footer', () => { - const notes = [ - { title: 'BREAKING CHANGES', text: 'Foo.', scope: null }, - { title: 'BREAKING CHANGES', text: 'Bar-Text.', scope: null } - ]; - + it( 'does not copy notes when processing multi-scoped commit (single entry)', () => { const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix.', - type: 'Fix', - subject: 'Simple fix.', - body: null, - footer: [ - 'BREAKING CHANGES: Foo.', - 'NOTE: Do not remove me.', - 'BREAKING CHANGES: Bar-Text.' - ].join( '\n' ), - notes + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix (scope1, scope2): Simple feature Closes #1.', + type: 'Fix (scope1, scope2)', + subject: 'Simple feature Closes #1.', + body: '', + footer: null, + notes: [ + { title: 'BREAKING CHANGE', text: 'Note 1.' }, + { title: 'MAJOR BREAKING CHANGES', text: 'Note 2.' } + ] }; const commit = transformCommit( rawCommit ); - expect( commit.body ).to.equal( ' NOTE: Do not remove me.' ); - expect( commit.notes ).to.deep.equal( notes ); + expect( commit.length ).to.equal( 2 ); + + expect( commit[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit[ 0 ].scope.length ).to.equal( 1 ); + expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'scope1' ); + expect( commit[ 0 ].rawType ).to.equal( 'Fix' ); + expect( commit[ 0 ].notes ).to.be.an( 'Array' ); + expect( commit[ 0 ].notes.length ).to.equal( 2 ); + expect( commit[ 0 ].notes[ 0 ].scope ).to.equal( null ); + expect( commit[ 0 ].notes[ 1 ].scope ).to.equal( null ); + + expect( commit[ 1 ].scope ).to.be.an( 'Array' ); + expect( commit[ 1 ].scope.length ).to.equal( 1 ); + expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'scope2' ); + expect( commit[ 1 ].rawType ).to.equal( 'Fix' ); + expect( commit[ 1 ].notes ).to.be.an( 'Array' ); + expect( commit[ 1 ].notes.length ).to.equal( 0 ); } ); - // See: https://github.com/ckeditor/ckeditor5/issues/7495. - it( 'removes duplicated scoped notes from the footer', () => { + it( 'does not copy notes when processing multi-scoped commit (multi entries)', () => { const rawCommit = { - type: 'Other (table)', - subject: 'Extracted `TableMouse` plugin from `TableSelection` plugin. Closes #6757.', - merge: 'Merge pull request #7355 from ckeditor/i/6757', - header: 'Other (table): Extracted `TableMouse` plugin from `TableSelection` plugin. Closes #6757.', - body: null, - footer: 'MINOR BREAKING CHANGE (table): The `TableNavigation` plugin renamed to `TableKeyboard`.', + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix (scope1, scope2): Simple feature Closes #1.', + type: 'Fix (scope1, scope2)', + subject: 'Simple feature Closes #1.', + body: [ + 'Other (scope3, scope4): Simple other change. Closes ckeditor/ckeditor5#3. Closes #3.' + ].join( '\n' ), + footer: null, notes: [ - { - title: 'MINOR BREAKING CHANGE', - text: '(table): The `TableNavigation` plugin renamed to `TableKeyboard`.' - } - ], - references: [], - mentions: [], - revert: null, - hash: '4d2f5f9b9f298601b332f304da66333c52673cb8' + { title: 'BREAKING CHANGE', text: 'Note 1.' }, + { title: 'MAJOR BREAKING CHANGES', text: 'Note 2.' } + ] }; const commit = transformCommit( rawCommit ); - expect( commit.footer ).to.equal( null ); - } ); - - // See: https://github.com/ckeditor/ckeditor5/issues/7489. - describe( 'internal merge commits', () => { - const mergeCommitsToIgnore = [ - 'Merge branch \'stable\'', - 'Merge branch \'master\'', - 'Merge branch \'release\'', - 'Merge \'stable\' into \'master\'', - 'Merge \'master\' into \'release\'', - 'Merge \'release\' into \'stable\'', - 'Merge branch \'stable\' into \'master\'', - 'Merge branch \'master\' into \'release\'', - 'Merge branch \'release\' into \'stable\'', - 'Merge branch \'stable\' into master', - 'Merge branch \'master\' into release', - 'Merge branch \'release\' into stable', - 'Merge branch stable into \'master\'', - 'Merge branch master into \'release\'', - 'Merge branch release into \'stable\'', - 'Merge branch stable into master', - 'Merge branch master into release', - 'Merge branch release into stable', - 'Merge remote-tracking branch \'origin/master\' into i/6788-feature-branch', - 'Merge branch \'master\' into i/6788-feature-branch', - 'Merge branch master into i/6788-feature-branch' - ]; - - const validMergeCommits = [ - 'Merge pull request #7485 from ckeditor/i/6788-feature-branch', - 'Merge branch \'i/6788-feature-branch\'', - 'Merge branch i/6788-feature-branch' - ]; - - for ( const commitTitle of mergeCommitsToIgnore ) { - it( `ignores a commit: "${ commitTitle }"`, () => { - const rawCommit = { - merge: commitTitle, - header: '-hash-', - body: '575e00bc8ece48826adefe226c4fb1fe071c73a7', - notes: [] - }; - - expect( transformCommit( rawCommit ) ).to.equal( undefined ); - } ); - } - - for ( const commitTitle of validMergeCommits ) { - it( `does not ignore a commit: "${ commitTitle }"`, () => { - const rawCommit = { - merge: commitTitle, - header: '-hash-', - body: '575e00bc8ece48826adefe226c4fb1fe071c73a7', - notes: [] - }; - - expect( transformCommit( rawCommit ) ).to.not.equal( undefined ); - } ); - } + expect( commit.length ).to.equal( 4 ); + + expect( commit[ 0 ].scope ).to.be.an( 'Array' ); + expect( commit[ 0 ].scope.length ).to.equal( 1 ); + expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'scope1' ); + expect( commit[ 0 ].rawType ).to.equal( 'Fix' ); + expect( commit[ 0 ].notes ).to.be.an( 'Array' ); + expect( commit[ 0 ].notes.length ).to.equal( 2 ); + + expect( commit[ 1 ].scope ).to.be.an( 'Array' ); + expect( commit[ 1 ].scope.length ).to.equal( 1 ); + expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'scope2' ); + expect( commit[ 1 ].rawType ).to.equal( 'Fix' ); + expect( commit[ 1 ].notes ).to.be.an( 'Array' ); + expect( commit[ 1 ].notes.length ).to.equal( 0 ); + + expect( commit[ 2 ].scope ).to.be.an( 'Array' ); + expect( commit[ 2 ].scope.length ).to.equal( 1 ); + expect( commit[ 2 ].scope[ 0 ] ).to.equal( 'scope3' ); + expect( commit[ 2 ].rawType ).to.equal( 'Other' ); + expect( commit[ 2 ].notes ).to.be.an( 'Array' ); + expect( commit[ 2 ].notes.length ).to.equal( 0 ); + + expect( commit[ 3 ].scope ).to.be.an( 'Array' ); + expect( commit[ 3 ].scope.length ).to.equal( 1 ); + expect( commit[ 3 ].scope[ 0 ] ).to.equal( 'scope4' ); + expect( commit[ 3 ].rawType ).to.equal( 'Other' ); + expect( commit[ 3 ].notes ).to.be.an( 'Array' ); + expect( commit[ 3 ].notes.length ).to.equal( 0 ); } ); + } ); - describe( '"Closes" references - merging into single entry', () => { - it( 'works for #id pattern', () => { - const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix. Closes #1. Closes #2. Closes #3.', - type: 'Fix', - subject: 'Simple fix. Closes #1. Closes #2. Closes #3.', - body: null, - footer: null, - notes: [] - }; + describe( 'multi-entries commit', () => { + it( 'returns an array with all entries', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Feature', + subject: 'Simple feature (1).', + body: [ + 'Fix: Simple fix (2).', + '', + 'Other: Simple other change (3).' + ].join( '\n' ), + footer: null, + notes: [] + }; - const commit = transformCommit( rawCommit ); + const commits = transformCommit( rawCommit ); - const expectedSubject = 'Simple fix. Closes ' + - '[#1](https://github.com/ckeditor/ckeditor5-dev/issues/1), ' + - '[#2](https://github.com/ckeditor/ckeditor5-dev/issues/2), ' + - '[#3](https://github.com/ckeditor/ckeditor5-dev/issues/3).'; + expect( commits ).to.be.an( 'Array' ); + expect( commits.length ).to.equal( 3 ); - expect( commit.subject ).to.equal( expectedSubject ); + expect( commits[ 0 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Features', + subject: 'Simple feature (1).', + body: '', + footer: null, + notes: [], + rawType: 'Feature', + files: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - it( 'works for org/repo#id pattern', () => { - const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes ckeditor/ckeditor5#3.', - type: 'Fix', - subject: 'Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes ckeditor/ckeditor5#3.', - body: null, - footer: null, - notes: [] - }; - - const commit = transformCommit( rawCommit ); - - const expectedSubject = 'Simple fix. Closes ' + - '[ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1), ' + - '[ckeditor/ckeditor5#2](https://github.com/ckeditor/ckeditor5/issues/2), ' + - '[ckeditor/ckeditor5#3](https://github.com/ckeditor/ckeditor5/issues/3).'; - - expect( commit.subject ).to.equal( expectedSubject ); + expect( commits[ 1 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix: Simple fix (2).', + type: 'Bug fixes', + subject: 'Simple fix (2).', + body: '', + revert: null, + merge: null, + footer: null, + notes: [], + rawType: 'Fix', + files: [], + mentions: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - it( 'works for mixed #id and org/repo#id patterns, starting with #id', () => { - const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix. Closes #1. Closes #2. Closes ckeditor/ckeditor5#3.', - type: 'Fix', - subject: 'Simple fix. Closes #1. Closes #2. Closes ckeditor/ckeditor5#3.', - body: null, - footer: null, - notes: [] - }; - - const commit = transformCommit( rawCommit ); - - const expectedSubject = 'Simple fix. Closes ' + - '[#1](https://github.com/ckeditor/ckeditor5-dev/issues/1), ' + - '[#2](https://github.com/ckeditor/ckeditor5-dev/issues/2), ' + - '[ckeditor/ckeditor5#3](https://github.com/ckeditor/ckeditor5/issues/3).'; - - expect( commit.subject ).to.equal( expectedSubject ); + expect( commits[ 2 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Other: Simple other change (3).', + type: 'Other changes', + subject: 'Simple other change (3).', + body: '', + revert: null, + merge: null, + footer: null, + notes: [], + rawType: 'Other', + files: [], + mentions: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); + } ); - it( 'works for mixed #id and org/repo#id patterns, starting with org/repo#id', () => { - const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes #3.', - type: 'Fix', - subject: 'Simple fix. Closes ckeditor/ckeditor5#1. Closes ckeditor/ckeditor5#2. Closes #3.', - body: null, - footer: null, - notes: [] - }; + it( 'preserves the description of the first commit', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Feature', + subject: 'Simple feature (1).', + body: [ + 'Lorem ipsum. #1', + '', + 'Fix: Simple fix (2).', + '', + 'Second lorem ipsum. #2', + '', + 'Other: Other simple change (3).' + ].join( '\n' ), + footer: null, + notes: [] + }; - const commit = transformCommit( rawCommit ); + const commits = transformCommit( rawCommit ); - const expectedSubject = 'Simple fix. Closes ' + - '[ckeditor/ckeditor5#1](https://github.com/ckeditor/ckeditor5/issues/1), ' + - '[ckeditor/ckeditor5#2](https://github.com/ckeditor/ckeditor5/issues/2), ' + - '[#3](https://github.com/ckeditor/ckeditor5-dev/issues/3).'; + expect( commits ).to.be.an( 'Array' ); + expect( commits.length ).to.equal( 3 ); - expect( commit.subject ).to.equal( expectedSubject ); + expect( commits[ 0 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Features', + subject: 'Simple feature (1).', + body: ' Lorem ipsum. [#1](https://github.com/ckeditor/ckeditor5-dev/issues/1)', + footer: null, + notes: [], + rawType: 'Feature', + files: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - it( 'does no touch the "See #" references.', () => { - const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix. Closes #1. Closes #2. See #3, #.', - type: 'Fix', - subject: 'Simple fix. Closes #1. Closes #2. See #3, #4.', - body: null, - footer: null, - notes: [] - }; - - const commit = transformCommit( rawCommit ); - - const expectedSubject = 'Simple fix. Closes ' + - '[#1](https://github.com/ckeditor/ckeditor5-dev/issues/1), ' + - '[#2](https://github.com/ckeditor/ckeditor5-dev/issues/2). ' + - 'See [#3](https://github.com/ckeditor/ckeditor5-dev/issues/3), ' + - '[#4](https://github.com/ckeditor/ckeditor5-dev/issues/4).'; - - expect( commit.subject ).to.equal( expectedSubject ); + expect( commits[ 1 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix: Simple fix (2).', + type: 'Bug fixes', + subject: 'Simple fix (2).', + body: ' Second lorem ipsum. [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2)', + revert: null, + merge: null, + footer: null, + notes: [], + rawType: 'Fix', + files: [], + mentions: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - it( 'does not replace paths with hash as github issue', () => { - const rawCommit = { - hash: '684997d0eb2eca76b9e058fb1c3fa00b50059cdc', - header: 'Fix: Simple fix. Closes i/am/path5#1.', - type: 'Fix', - subject: 'Fix: Simple fix. Closes i/am/path5#1.', - body: null, - footer: null, - notes: [] - }; - - const commit = transformCommit( rawCommit ); - - expect( commit.subject ).to.equal( 'Fix: Simple fix. Closes i/am/path5#1.' ); + expect( commits[ 2 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Other: Other simple change (3).', + type: 'Other changes', + subject: 'Other simple change (3).', + body: '', + revert: null, + merge: null, + footer: null, + notes: [], + rawType: 'Other', + files: [], + mentions: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); } ); - describe( 'scopes', () => { - it( 'returns null if the scope is being missed', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix.', - type: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }; - - const commit = transformCommit( rawCommit ); + it( 'adds a dot at the subject if missing in new commit', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Feature', + subject: 'Simple feature (1).', + body: [ + 'Fix: Simple fix (2)' + ].join( '\n' ), + footer: null, + notes: [] + }; - expect( commit.scope ).to.be.equal( null ); - } ); + const commits = transformCommit( rawCommit ); - it( 'extracts the scope from the commit type', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix (package): Simple fix.', - type: 'Fix (package)', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }; + expect( commits ).to.be.an( 'Array' ); + expect( commits.length ).to.equal( 2 ); - const commit = transformCommit( rawCommit ); + expect( commits[ 1 ].subject ).to.equal( 'Simple fix (2).' ); + } ); - expect( commit.scope ).to.be.an( 'Array' ); - expect( commit.scope.length ).to.equal( 1 ); - expect( commit.scope[ 0 ] ).to.equal( 'package' ); - expect( commit.rawType ).to.equal( 'Fix' ); - } ); + it( 'copies an array with changed files across all commits', () => { + const files = [ 'a', 'b', 'c' ]; - it( 'works with multi-scoped changes (commit type)', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature (foo, bar): Simple fix.', - type: 'Feature (foo, bar)', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [] - }; - - const commit = transformCommit( rawCommit ); + vi.mocked( getChangedFilesForCommit ).mockReturnValue( files ); - expect( commit ).to.be.an( 'Array' ); - expect( commit.length ).to.equal( 2 ); + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Feature', + subject: 'Simple feature (1).', + body: [ + 'Fix: Simple fix (2)', + '', + 'Other: Simple other change (3).' + ].join( '\n' ), + footer: null, + notes: [] + }; - expect( commit[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit[ 0 ].scope.length ).to.equal( 1 ); - expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'bar' ); - expect( commit[ 0 ].rawType ).to.equal( 'Feature' ); + const commits = transformCommit( rawCommit ); - expect( commit[ 1 ].scope ).to.be.an( 'Array' ); - expect( commit[ 1 ].scope.length ).to.equal( 1 ); - expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'foo' ); - expect( commit[ 1 ].rawType ).to.equal( 'Feature' ); - } ); + expect( commits[ 0 ].files ).to.equal( files ); + expect( commits[ 1 ].files ).to.equal( files ); + expect( commits[ 2 ].files ).to.equal( files ); + } ); - it( 'works with multi-scoped merge commit', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature (foo, bar): Simple fix.', - type: 'Feature (foo, bar)', - subject: 'Simple fix.', - merge: 'Merge pull request #7355 from ckeditor/i/6757', - body: null, - footer: null, - notes: [] - }; + it( 'works with non-public commits', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Feature', + subject: 'Simple feature (1).', + body: [ + 'Docs: Simple docs change (2)', + '', + 'Internal: Simple internal change (3).' + ].join( '\n' ), + footer: null, + notes: [] + }; - const commit = transformCommit( rawCommit ); + const commits = transformCommit( rawCommit ); - expect( commit ).to.be.an( 'Array' ); - expect( commit.length ).to.equal( 2 ); + expect( commits ).to.be.an( 'Array' ); + expect( commits.length ).to.equal( 3 ); - expect( commit[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit[ 0 ].scope.length ).to.equal( 1 ); - expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'bar' ); - expect( commit[ 0 ].rawType ).to.equal( 'Feature' ); + expect( commits[ 0 ].isPublicCommit ).to.equal( true ); + expect( commits[ 1 ].isPublicCommit ).to.equal( false ); + expect( commits[ 2 ].isPublicCommit ).to.equal( false ); + } ); - expect( commit[ 1 ].scope ).to.be.an( 'Array' ); - expect( commit[ 1 ].scope.length ).to.equal( 1 ); - expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'foo' ); - expect( commit[ 1 ].rawType ).to.equal( 'Feature' ); - } ); + it( 'handles scoped and non-scoped changes', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature (1).', + type: 'Feature', + subject: 'Simple feature (1).', + body: [ + 'Fix (foo): Simple fix (2).', + '', + 'Other: Simple other change (3).', + '', + 'Feature (foo, bar): Simple other change (4).' + ].join( '\n' ), + footer: null, + notes: [] + }; - it( 'clones the commit properties for multi-scoped changes', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature (foo, bar): Simple fix.', - type: 'Feature (foo, bar)', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [ - { - title: 'BREAKING CHANGES', - text: '(package): Foo.' - } - ] - }; + const commits = transformCommit( rawCommit ); - const commit = transformCommit( rawCommit ); + expect( commits ).to.be.an( 'Array' ); + expect( commits.length ).to.equal( 5 ); - expect( commit ).to.be.an( 'Array' ); - expect( commit.length ).to.equal( 2 ); + expect( commits[ 0 ].scope ).to.equal( null ); + expect( commits[ 1 ].scope ).to.deep.equal( [ 'foo' ] ); + expect( commits[ 2 ].scope ).to.equal( null ); + expect( commits[ 3 ].scope ).to.deep.equal( [ 'bar' ] ); + expect( commits[ 4 ].scope ).to.deep.equal( [ 'foo' ] ); + } ); - expect( commit[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit[ 1 ].scope ).to.be.an( 'Array' ); - expect( commit[ 0 ].scope ).to.not.equal( commit[ 1 ].scope ); + it( 'merges "Closes" references in multi-entries commit', () => { + const rawCommit = { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature Closes #1.', + type: 'Feature', + subject: 'Simple feature Closes #1.', + body: [ + 'Fix: Simple fix. Closes #2. Closes ckeditor/ckeditor5#2. See ckeditor/ckeditor5#1000.', + '', + 'Other: Simple other change. Closes ckeditor/ckeditor5#3. Closes #3.' + ].join( '\n' ), + footer: null, + notes: [] + }; - expect( commit[ 0 ].files ).to.be.an( 'Array' ); - expect( commit[ 1 ].files ).to.be.an( 'Array' ); - expect( commit[ 0 ].files ).to.not.equal( commit[ 1 ].files ); + const commits = transformCommit( rawCommit ); - expect( commit[ 0 ].notes ).to.be.an( 'Array' ); - expect( commit[ 1 ].notes ).to.be.an( 'Array' ); - expect( commit[ 0 ].notes ).to.not.equal( commit[ 1 ].notes ); + expect( commits ).to.be.an( 'Array' ); + expect( commits.length ).to.equal( 3 ); - expect( commit[ 0 ].notes[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit[ 0 ].notes[ 0 ].scope[ 0 ] ).to.equal( 'package' ); - expect( commit[ 1 ].notes ).to.be.an( 'Array' ); + expect( commits[ 0 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Feature: Simple feature Closes #1.', + type: 'Features', + subject: 'Simple feature Closes [#1](https://github.com/ckeditor/ckeditor5-dev/issues/1).', + body: '', + footer: null, + notes: [], + rawType: 'Feature', + files: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - it( 'extracts the scope from notes', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix.', - type: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [ - { - title: 'BREAKING CHANGES', - text: '(package): Foo.' - } - ] - }; - - const commit = transformCommit( rawCommit ); - - expect( commit.notes ).to.be.an( 'Array' ); - expect( commit.notes.length ).to.equal( 1 ); - expect( commit.notes[ 0 ] ).to.be.an( 'Object' ); - expect( commit.notes[ 0 ].text ).to.equal( 'Foo.' ); - expect( commit.notes[ 0 ].title ).to.equal( 'BREAKING CHANGES' ); - expect( commit.notes[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit.notes[ 0 ].scope.length ).to.equal( 1 ); - expect( commit.notes[ 0 ].scope[ 0 ] ).to.equal( 'package' ); + expect( commits[ 1 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Fix: Simple fix. Closes #2. Closes ckeditor/ckeditor5#2. See ckeditor/ckeditor5#1000.', + type: 'Bug fixes', + subject: 'Simple fix. Closes [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2), ' + + '[ckeditor/ckeditor5#2](https://github.com/ckeditor/ckeditor5/issues/2). ' + + 'See [ckeditor/ckeditor5#1000](https://github.com/ckeditor/ckeditor5/issues/1000).', + body: '', + revert: null, + merge: null, + footer: null, + notes: [], + rawType: 'Fix', + files: [], + mentions: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - it( 'works with multi-scoped notes', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix.', - type: 'Fix', - subject: 'Simple fix.', - body: null, - footer: null, - notes: [ - { - title: 'BREAKING CHANGES', - text: '(foo, bar): Package.' - } - ] - }; - - const commit = transformCommit( rawCommit ); - - expect( commit.notes ).to.be.an( 'Array' ); - expect( commit.notes.length ).to.equal( 1 ); - expect( commit.notes[ 0 ] ).to.be.an( 'Object' ); - expect( commit.notes[ 0 ].text ).to.equal( 'Package.' ); - expect( commit.notes[ 0 ].title ).to.equal( 'BREAKING CHANGES' ); - expect( commit.notes[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit.notes[ 0 ].scope.length ).to.equal( 2 ); - expect( commit.notes[ 0 ].scope[ 0 ] ).to.equal( 'bar' ); - expect( commit.notes[ 0 ].scope[ 1 ] ).to.equal( 'foo' ); + expect( commits[ 2 ] ).to.deep.equal( { + hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', + header: 'Other: Simple other change. Closes ckeditor/ckeditor5#3. Closes #3.', + type: 'Other changes', + subject: 'Simple other change. Closes [ckeditor/ckeditor5#3](https://github.com/ckeditor/ckeditor5/issues/3), ' + + '[#3](https://github.com/ckeditor/ckeditor5-dev/issues/3).', + body: '', + revert: null, + merge: null, + footer: null, + notes: [], + rawType: 'Other', + files: [], + mentions: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); + } ); + } ); - it( 'does not copy notes when processing multi-scoped commit (single entry)', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix (scope1, scope2): Simple feature Closes #1.', - type: 'Fix (scope1, scope2)', - subject: 'Simple feature Closes #1.', - body: '', - footer: null, - notes: [ - { title: 'BREAKING CHANGE', text: 'Note 1.' }, - { title: 'MAJOR BREAKING CHANGES', text: 'Note 2.' } - ] - }; + describe( 'squash merge commit', () => { + it( 'removes the squash commit part from results', () => { + const rawCommit = { + type: null, + subject: null, + merge: null, + header: 'A squash pull request change (#111)', + body: 'Fix (scope-1): Description 1.\n' + + '\n' + + 'Other (scope-2): Description 2.\n' + + '\n' + + 'Internal (scope-3): Description 3.', + footer: '', + notes: [], + references: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' + }; - const commit = transformCommit( rawCommit ); - - expect( commit.length ).to.equal( 2 ); - - expect( commit[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit[ 0 ].scope.length ).to.equal( 1 ); - expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'scope1' ); - expect( commit[ 0 ].rawType ).to.equal( 'Fix' ); - expect( commit[ 0 ].notes ).to.be.an( 'Array' ); - expect( commit[ 0 ].notes.length ).to.equal( 2 ); - expect( commit[ 0 ].notes[ 0 ].scope ).to.equal( null ); - expect( commit[ 0 ].notes[ 1 ].scope ).to.equal( null ); - - expect( commit[ 1 ].scope ).to.be.an( 'Array' ); - expect( commit[ 1 ].scope.length ).to.equal( 1 ); - expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'scope2' ); - expect( commit[ 1 ].rawType ).to.equal( 'Fix' ); - expect( commit[ 1 ].notes ).to.be.an( 'Array' ); - expect( commit[ 1 ].notes.length ).to.equal( 0 ); - } ); + const commits = transformCommit( rawCommit ); - it( 'does not copy notes when processing multi-scoped commit (multi entries)', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix (scope1, scope2): Simple feature Closes #1.', - type: 'Fix (scope1, scope2)', - subject: 'Simple feature Closes #1.', - body: [ - 'Other (scope3, scope4): Simple other change. Closes ckeditor/ckeditor5#3. Closes #3.' - ].join( '\n' ), - footer: null, - notes: [ - { title: 'BREAKING CHANGE', text: 'Note 1.' }, - { title: 'MAJOR BREAKING CHANGES', text: 'Note 2.' } - ] - }; + expect( commits ).to.be.an( 'Array' ); + expect( commits ).to.lengthOf( 3 ); - const commit = transformCommit( rawCommit ); - - expect( commit.length ).to.equal( 4 ); - - expect( commit[ 0 ].scope ).to.be.an( 'Array' ); - expect( commit[ 0 ].scope.length ).to.equal( 1 ); - expect( commit[ 0 ].scope[ 0 ] ).to.equal( 'scope1' ); - expect( commit[ 0 ].rawType ).to.equal( 'Fix' ); - expect( commit[ 0 ].notes ).to.be.an( 'Array' ); - expect( commit[ 0 ].notes.length ).to.equal( 2 ); - - expect( commit[ 1 ].scope ).to.be.an( 'Array' ); - expect( commit[ 1 ].scope.length ).to.equal( 1 ); - expect( commit[ 1 ].scope[ 0 ] ).to.equal( 'scope2' ); - expect( commit[ 1 ].rawType ).to.equal( 'Fix' ); - expect( commit[ 1 ].notes ).to.be.an( 'Array' ); - expect( commit[ 1 ].notes.length ).to.equal( 0 ); - - expect( commit[ 2 ].scope ).to.be.an( 'Array' ); - expect( commit[ 2 ].scope.length ).to.equal( 1 ); - expect( commit[ 2 ].scope[ 0 ] ).to.equal( 'scope3' ); - expect( commit[ 2 ].rawType ).to.equal( 'Other' ); - expect( commit[ 2 ].notes ).to.be.an( 'Array' ); - expect( commit[ 2 ].notes.length ).to.equal( 0 ); - - expect( commit[ 3 ].scope ).to.be.an( 'Array' ); - expect( commit[ 3 ].scope.length ).to.equal( 1 ); - expect( commit[ 3 ].scope[ 0 ] ).to.equal( 'scope4' ); - expect( commit[ 3 ].rawType ).to.equal( 'Other' ); - expect( commit[ 3 ].notes ).to.be.an( 'Array' ); - expect( commit[ 3 ].notes.length ).to.equal( 0 ); + expect( commits[ 0 ] ).to.deep.equal( { + revert: null, + merge: 'A squash pull request change (#111)', + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [], + mentions: [], + rawType: 'Fix', + scope: [ 'scope-1' ], + isPublicCommit: true, + type: 'Bug fixes', + header: 'Fix (scope-1): Description 1.', + subject: 'Description 1.', + body: '' } ); - } ); - describe( 'multi-entries commit', () => { - it( 'returns an array with all entries', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Feature', - subject: 'Simple feature (1).', - body: [ - 'Fix: Simple fix (2).', - '', - 'Other: Simple other change (3).' - ].join( '\n' ), - footer: null, - notes: [] - }; - - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits.length ).to.equal( 3 ); - - expect( commits[ 0 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Features', - subject: 'Simple feature (1).', - body: '', - footer: null, - notes: [], - rawType: 'Feature', - files: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - - expect( commits[ 1 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix (2).', - type: 'Bug fixes', - subject: 'Simple fix (2).', - body: '', - revert: null, - merge: null, - footer: null, - notes: [], - rawType: 'Fix', - files: [], - mentions: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - - expect( commits[ 2 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Other: Simple other change (3).', - type: 'Other changes', - subject: 'Simple other change (3).', - body: '', - revert: null, - merge: null, - footer: null, - notes: [], - rawType: 'Other', - files: [], - mentions: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); + expect( commits[ 1 ] ).to.deep.equal( { + revert: null, + merge: null, + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [], + mentions: [], + rawType: 'Other', + scope: [ 'scope-2' ], + isPublicCommit: true, + type: 'Other changes', + header: 'Other (scope-2): Description 2.', + subject: 'Description 2.', + body: '' } ); - it( 'preserves the description of the first commit', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Feature', - subject: 'Simple feature (1).', - body: [ - 'Lorem ipsum. #1', - '', - 'Fix: Simple fix (2).', - '', - 'Second lorem ipsum. #2', - '', - 'Other: Other simple change (3).' - ].join( '\n' ), - footer: null, - notes: [] - }; - - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits.length ).to.equal( 3 ); - - expect( commits[ 0 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Features', - subject: 'Simple feature (1).', - body: ' Lorem ipsum. [#1](https://github.com/ckeditor/ckeditor5-dev/issues/1)', - footer: null, - notes: [], - rawType: 'Feature', - files: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - - expect( commits[ 1 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix (2).', - type: 'Bug fixes', - subject: 'Simple fix (2).', - body: ' Second lorem ipsum. [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2)', - revert: null, - merge: null, - footer: null, - notes: [], - rawType: 'Fix', - files: [], - mentions: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - - expect( commits[ 2 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Other: Other simple change (3).', - type: 'Other changes', - subject: 'Other simple change (3).', - body: '', - revert: null, - merge: null, - footer: null, - notes: [], - rawType: 'Other', - files: [], - mentions: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); + expect( commits[ 2 ] ).to.deep.equal( { + revert: null, + merge: null, + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [], + mentions: [], + rawType: 'Internal', + scope: [ 'scope-3' ], + isPublicCommit: false, + header: 'Internal (scope-3): Description 3.', + subject: 'Description 3.', + body: '' } ); + } ); - it( 'adds a dot at the subject if missing in new commit', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Feature', - subject: 'Simple feature (1).', - body: [ - 'Fix: Simple fix (2)' - ].join( '\n' ), - footer: null, - notes: [] - }; + it( 'processes breaking change notes from the removed squash commit', () => { + const rawCommit = { + type: null, + subject: null, + merge: null, + header: 'A squash pull request change (#111)', + body: 'Fix (scope-1): Description 1.\n' + + '\n' + + 'Other (scope-2): Description 2.\n' + + '\n' + + 'Internal (scope-3): Description 3.', + footer: 'MINOR BREAKING CHANGE (scope-1): BC 1.\n' + + '\n' + + 'MINOR BREAKING CHANGE (scope-2): BC 2.', + notes: [ + { + title: 'MINOR BREAKING CHANGE', + text: '(scope-1): BC 1.' + }, + { + title: 'MINOR BREAKING CHANGE', + text: '(scope-2): BC 2.' + } + ], + references: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' + }; - const commits = transformCommit( rawCommit ); + const commits = transformCommit( rawCommit ); - expect( commits ).to.be.an( 'Array' ); - expect( commits.length ).to.equal( 2 ); + expect( commits ).to.be.an( 'Array' ); + expect( commits ).to.lengthOf( 3 ); - expect( commits[ 1 ].subject ).to.equal( 'Simple fix (2).' ); + expect( commits[ 0 ] ).to.deep.equal( { + revert: null, + merge: 'A squash pull request change (#111)', + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [ + { + scope: [ + 'scope-1' + ], + text: 'BC 1.', + title: 'BREAKING CHANGES' + }, + { + scope: [ + 'scope-2' + ], + text: 'BC 2.', + title: 'BREAKING CHANGES' + } + ], + mentions: [], + rawType: 'Fix', + scope: [ 'scope-1' ], + isPublicCommit: true, + type: 'Bug fixes', + header: 'Fix (scope-1): Description 1.', + subject: 'Description 1.', + body: '' } ); + expect( commits[ 1 ].notes ).to.deep.equal( [] ); + expect( commits[ 2 ].notes ).to.deep.equal( [] ); + } ); - it( 'copies an array with changed files across all commits', () => { - const files = [ 'a', 'b', 'c' ]; - - vi.mocked( getChangedFilesForCommit ).mockReturnValue( files ); + it( 'does not remove the squash commit if all changes are marked as internal', () => { + const rawCommit = { + type: null, + subject: null, + merge: null, + header: 'A squash pull request change (#111)', + body: 'Internal (scope-1): Description 1.\n' + + '\n' + + 'Internal (scope-2): Description 2.\n' + + '\n' + + 'Internal (scope-3): Description 3.', + footer: 'MINOR BREAKING CHANGE (scope-1): BC 1.\n' + + '\n' + + 'MINOR BREAKING CHANGE (scope-2): BC 2.', + notes: [ + { + title: 'MINOR BREAKING CHANGE', + text: '(scope-1): BC 1.' + }, + { + title: 'MINOR BREAKING CHANGE', + text: '(scope-2): BC 2.' + } + ], + references: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' + }; - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Feature', - subject: 'Simple feature (1).', - body: [ - 'Fix: Simple fix (2)', - '', - 'Other: Simple other change (3).' - ].join( '\n' ), - footer: null, - notes: [] - }; + const commits = transformCommit( rawCommit ); - const commits = transformCommit( rawCommit ); + expect( commits ).to.be.an( 'Array' ); + expect( commits ).to.lengthOf( 4 ); - expect( commits[ 0 ].files ).to.equal( files ); - expect( commits[ 1 ].files ).to.equal( files ); - expect( commits[ 2 ].files ).to.equal( files ); + expect( commits[ 0 ] ).to.deep.equal( { + type: null, + subject: null, + merge: null, + header: 'A squash pull request change (#111)', + body: '', + footer: 'MINOR BREAKING CHANGE (scope-1): BC 1.\n' + + '\n' + + 'MINOR BREAKING CHANGE (scope-2): BC 2.', + notes: [ + { + text: '(scope-1): BC 1.', + title: 'MINOR BREAKING CHANGE' + }, + { + text: '(scope-2): BC 2.', + title: 'MINOR BREAKING CHANGE' + } + ], + references: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + rawType: undefined, + files: [], + scope: undefined, + isPublicCommit: false, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - - it( 'works with non-public commits', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Feature', - subject: 'Simple feature (1).', - body: [ - 'Docs: Simple docs change (2)', - '', - 'Internal: Simple internal change (3).' - ].join( '\n' ), - footer: null, - notes: [] - }; - - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits.length ).to.equal( 3 ); - - expect( commits[ 0 ].isPublicCommit ).to.equal( true ); - expect( commits[ 1 ].isPublicCommit ).to.equal( false ); - expect( commits[ 2 ].isPublicCommit ).to.equal( false ); + expect( commits[ 1 ] ).to.deep.equal( { + revert: null, + merge: null, + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [], + mentions: [], + rawType: 'Internal', + scope: [ 'scope-1' ], + isPublicCommit: false, + header: 'Internal (scope-1): Description 1.', + subject: 'Description 1.', + body: '' } ); - - it( 'handles scoped and non-scoped changes', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature (1).', - type: 'Feature', - subject: 'Simple feature (1).', - body: [ - 'Fix (foo): Simple fix (2).', - '', - 'Other: Simple other change (3).', - '', - 'Feature (foo, bar): Simple other change (4).' - ].join( '\n' ), - footer: null, - notes: [] - }; - - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits.length ).to.equal( 5 ); - - expect( commits[ 0 ].scope ).to.equal( null ); - expect( commits[ 1 ].scope ).to.deep.equal( [ 'foo' ] ); - expect( commits[ 2 ].scope ).to.equal( null ); - expect( commits[ 3 ].scope ).to.deep.equal( [ 'bar' ] ); - expect( commits[ 4 ].scope ).to.deep.equal( [ 'foo' ] ); + expect( commits[ 2 ] ).to.deep.equal( { + revert: null, + merge: null, + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [], + mentions: [], + rawType: 'Internal', + scope: [ 'scope-2' ], + isPublicCommit: false, + header: 'Internal (scope-2): Description 2.', + subject: 'Description 2.', + body: '' } ); - - it( 'merges "Closes" references in multi-entries commit', () => { - const rawCommit = { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature Closes #1.', - type: 'Feature', - subject: 'Simple feature Closes #1.', - body: [ - 'Fix: Simple fix. Closes #2. Closes ckeditor/ckeditor5#2. See ckeditor/ckeditor5#1000.', - '', - 'Other: Simple other change. Closes ckeditor/ckeditor5#3. Closes #3.' - ].join( '\n' ), - footer: null, - notes: [] - }; - - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits.length ).to.equal( 3 ); - - expect( commits[ 0 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Feature: Simple feature Closes #1.', - type: 'Features', - subject: 'Simple feature Closes [#1](https://github.com/ckeditor/ckeditor5-dev/issues/1).', - body: '', - footer: null, - notes: [], - rawType: 'Feature', - files: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - - expect( commits[ 1 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Fix: Simple fix. Closes #2. Closes ckeditor/ckeditor5#2. See ckeditor/ckeditor5#1000.', - type: 'Bug fixes', - subject: 'Simple fix. Closes [#2](https://github.com/ckeditor/ckeditor5-dev/issues/2), ' + - '[ckeditor/ckeditor5#2](https://github.com/ckeditor/ckeditor5/issues/2). ' + - 'See [ckeditor/ckeditor5#1000](https://github.com/ckeditor/ckeditor5/issues/1000).', - body: '', - revert: null, - merge: null, - footer: null, - notes: [], - rawType: 'Fix', - files: [], - mentions: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - - expect( commits[ 2 ] ).to.deep.equal( { - hash: '76b9e058fb1c3fa00b50059cdc684997d0eb2eca', - header: 'Other: Simple other change. Closes ckeditor/ckeditor5#3. Closes #3.', - type: 'Other changes', - subject: 'Simple other change. Closes [ckeditor/ckeditor5#3](https://github.com/ckeditor/ckeditor5/issues/3), ' + - '[#3](https://github.com/ckeditor/ckeditor5-dev/issues/3).', - body: '', - revert: null, - merge: null, - footer: null, - notes: [], - rawType: 'Other', - files: [], - mentions: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); + expect( commits[ 3 ] ).to.deep.equal( { + revert: null, + merge: null, + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [], + mentions: [], + rawType: 'Internal', + scope: [ 'scope-3' ], + isPublicCommit: false, + header: 'Internal (scope-3): Description 3.', + subject: 'Description 3.', + body: '' } ); } ); - describe( 'squash merge commit', () => { - it( 'removes the squash commit part from results', () => { - const rawCommit = { - type: null, - subject: null, - merge: null, - header: 'A squash pull request change (#111)', - body: 'Fix (scope-1): Description 1.\n' + - '\n' + - 'Other (scope-2): Description 2.\n' + - '\n' + - 'Internal (scope-3): Description 3.', - footer: '', - notes: [], - references: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' - }; + it( 'does not remove the squash commit if it is a valid message', () => { + const rawCommit = { + type: 'Fix', + subject: 'A squash pull request change (#111)', + merge: null, + header: 'Fix: A squash pull request change (#111)', + body: 'Internal (scope-1): Description 1.', + footer: '', + notes: [], + references: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' + }; - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits ).to.lengthOf( 3 ); - - expect( commits[ 0 ] ).to.deep.equal( { - revert: null, - merge: 'A squash pull request change (#111)', - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [], - mentions: [], - rawType: 'Fix', - scope: [ 'scope-1' ], - isPublicCommit: true, - type: 'Bug fixes', - header: 'Fix (scope-1): Description 1.', - subject: 'Description 1.', - body: '' - } ); - - expect( commits[ 1 ] ).to.deep.equal( { - revert: null, - merge: null, - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [], - mentions: [], - rawType: 'Other', - scope: [ 'scope-2' ], - isPublicCommit: true, - type: 'Other changes', - header: 'Other (scope-2): Description 2.', - subject: 'Description 2.', - body: '' - } ); - - expect( commits[ 2 ] ).to.deep.equal( { - revert: null, - merge: null, - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [], - mentions: [], - rawType: 'Internal', - scope: [ 'scope-3' ], - isPublicCommit: false, - header: 'Internal (scope-3): Description 3.', - subject: 'Description 3.', - body: '' - } ); - } ); + const commits = transformCommit( rawCommit ); - it( 'processes breaking change notes from the removed squash commit', () => { - const rawCommit = { - type: null, - subject: null, - merge: null, - header: 'A squash pull request change (#111)', - body: 'Fix (scope-1): Description 1.\n' + - '\n' + - 'Other (scope-2): Description 2.\n' + - '\n' + - 'Internal (scope-3): Description 3.', - footer: 'MINOR BREAKING CHANGE (scope-1): BC 1.\n' + - '\n' + - 'MINOR BREAKING CHANGE (scope-2): BC 2.', - notes: [ - { - title: 'MINOR BREAKING CHANGE', - text: '(scope-1): BC 1.' - }, - { - title: 'MINOR BREAKING CHANGE', - text: '(scope-2): BC 2.' - } - ], - references: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' - }; + expect( commits ).to.be.an( 'Array' ); + expect( commits ).to.lengthOf( 2 ); - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits ).to.lengthOf( 3 ); - - expect( commits[ 0 ] ).to.deep.equal( { - revert: null, - merge: 'A squash pull request change (#111)', - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [ - { - scope: [ - 'scope-1' - ], - text: 'BC 1.', - title: 'BREAKING CHANGES' - }, - { - scope: [ - 'scope-2' - ], - text: 'BC 2.', - title: 'BREAKING CHANGES' - } - ], - mentions: [], - rawType: 'Fix', - scope: [ 'scope-1' ], - isPublicCommit: true, - type: 'Bug fixes', - header: 'Fix (scope-1): Description 1.', - subject: 'Description 1.', - body: '' - } ); - expect( commits[ 1 ].notes ).to.deep.equal( [] ); - expect( commits[ 2 ].notes ).to.deep.equal( [] ); + expect( commits[ 0 ] ).to.deep.equal( { + type: 'Bug fixes', + rawType: 'Fix', + subject: 'A squash pull request change ([#111](https://github.com/ckeditor/ckeditor5-dev/issues/111)).', + merge: null, + header: 'Fix: A squash pull request change (#111)', + body: '', + footer: '', + notes: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + scope: null, + isPublicCommit: true, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); - - it( 'does not remove the squash commit if all changes are marked as internal', () => { - const rawCommit = { - type: null, - subject: null, - merge: null, - header: 'A squash pull request change (#111)', - body: 'Internal (scope-1): Description 1.\n' + - '\n' + - 'Internal (scope-2): Description 2.\n' + - '\n' + - 'Internal (scope-3): Description 3.', - footer: 'MINOR BREAKING CHANGE (scope-1): BC 1.\n' + - '\n' + - 'MINOR BREAKING CHANGE (scope-2): BC 2.', - notes: [ - { - title: 'MINOR BREAKING CHANGE', - text: '(scope-1): BC 1.' - }, - { - title: 'MINOR BREAKING CHANGE', - text: '(scope-2): BC 2.' - } - ], - references: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' - }; - - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits ).to.lengthOf( 4 ); - - expect( commits[ 0 ] ).to.deep.equal( { - type: null, - subject: null, - merge: null, - header: 'A squash pull request change (#111)', - body: '', - footer: 'MINOR BREAKING CHANGE (scope-1): BC 1.\n' + - '\n' + - 'MINOR BREAKING CHANGE (scope-2): BC 2.', - notes: [ - { - text: '(scope-1): BC 1.', - title: 'MINOR BREAKING CHANGE' - }, - { - text: '(scope-2): BC 2.', - title: 'MINOR BREAKING CHANGE' - } - ], - references: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - rawType: undefined, - files: [], - scope: undefined, - isPublicCommit: false, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - expect( commits[ 1 ] ).to.deep.equal( { - revert: null, - merge: null, - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [], - mentions: [], - rawType: 'Internal', - scope: [ 'scope-1' ], - isPublicCommit: false, - header: 'Internal (scope-1): Description 1.', - subject: 'Description 1.', - body: '' - } ); - expect( commits[ 2 ] ).to.deep.equal( { - revert: null, - merge: null, - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [], - mentions: [], - rawType: 'Internal', - scope: [ 'scope-2' ], - isPublicCommit: false, - header: 'Internal (scope-2): Description 2.', - subject: 'Description 2.', - body: '' - } ); - expect( commits[ 3 ] ).to.deep.equal( { - revert: null, - merge: null, - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [], - mentions: [], - rawType: 'Internal', - scope: [ 'scope-3' ], - isPublicCommit: false, - header: 'Internal (scope-3): Description 3.', - subject: 'Description 3.', - body: '' - } ); + expect( commits[ 1 ] ).to.deep.equal( { + revert: null, + merge: null, + footer: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + files: [], + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', + notes: [], + mentions: [], + rawType: 'Internal', + scope: [ 'scope-1' ], + isPublicCommit: false, + header: 'Internal (scope-1): Description 1.', + subject: 'Description 1.', + body: '' } ); + } ); - it( 'does not remove the squash commit if it is a valid message', () => { - const rawCommit = { - type: 'Fix', - subject: 'A squash pull request change (#111)', - merge: null, - header: 'Fix: A squash pull request change (#111)', - body: 'Internal (scope-1): Description 1.', - footer: '', - notes: [], - references: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' - }; - - const commits = transformCommit( rawCommit ); - - expect( commits ).to.be.an( 'Array' ); - expect( commits ).to.lengthOf( 2 ); - - expect( commits[ 0 ] ).to.deep.equal( { - type: 'Bug fixes', - rawType: 'Fix', - subject: 'A squash pull request change ([#111](https://github.com/ckeditor/ckeditor5-dev/issues/111)).', - merge: null, - header: 'Fix: A squash pull request change (#111)', - body: '', - footer: '', - notes: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - scope: null, - isPublicCommit: true, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); - expect( commits[ 1 ] ).to.deep.equal( { - revert: null, - merge: null, - footer: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - files: [], - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev', - notes: [], - mentions: [], - rawType: 'Internal', - scope: [ 'scope-1' ], - isPublicCommit: false, - header: 'Internal (scope-1): Description 1.', - subject: 'Description 1.', - body: '' - } ); - } ); + it( 'processes a title including various non-letter symbols', () => { + const rawCommit = { + type: null, + subject: null, + merge: null, + header: 'A squash pull (#12) request change! (#111)', + body: 'Just details.', + footer: '', + notes: [], + references: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' + }; - it( 'processes a title including various non-letter symbols', () => { - const rawCommit = { - type: null, - subject: null, - merge: null, - header: 'A squash pull (#12) request change! (#111)', - body: 'Just details.', - footer: '', - notes: [], - references: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee' - }; + const commit = transformCommit( rawCommit ); - const commit = transformCommit( rawCommit ); - - expect( commit ).to.be.an( 'Object' ); - expect( commit ).to.deep.equal( { - type: null, - subject: null, - merge: null, - header: 'A squash pull (#12) request change! (#111)', - body: 'Just details.', - footer: '', - notes: [], - references: [], - mentions: [], - revert: null, - hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', - rawType: undefined, - files: [], - scope: undefined, - isPublicCommit: false, - repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' - } ); + expect( commit ).to.be.an( 'Object' ); + expect( commit ).to.deep.equal( { + type: null, + subject: null, + merge: null, + header: 'A squash pull (#12) request change! (#111)', + body: 'Just details.', + footer: '', + notes: [], + references: [], + mentions: [], + revert: null, + hash: 'bb24d87e46a9f4675eabfa97e247ee7f58debeee', + rawType: undefined, + files: [], + scope: undefined, + isPublicCommit: false, + repositoryUrl: 'https://github.com/ckeditor/ckeditor5-dev' } ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js index e597b447c..b33b44b69 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/transformcommitutils.js @@ -10,245 +10,243 @@ import * as transformCommitUtils from '../../lib/utils/transformcommitutils.js'; vi.mock( '../../lib/utils/getpackagejson.js' ); -describe( 'dev-release-tools/utils', () => { - describe( 'transformCommitUtils', () => { - describe( 'availableTypes', () => { - it( 'should be defined', () => { - expect( transformCommitUtils.availableCommitTypes ).to.be.a( 'Map' ); - } ); +describe( 'transformCommitUtils', () => { + describe( 'availableTypes', () => { + it( 'should be defined', () => { + expect( transformCommitUtils.availableCommitTypes ).to.be.a( 'Map' ); } ); + } ); - describe( 'typesOrder', () => { - it( 'should be defined', () => { - expect( transformCommitUtils.typesOrder ).to.be.a( 'Object' ); - } ); + describe( 'typesOrder', () => { + it( 'should be defined', () => { + expect( transformCommitUtils.typesOrder ).to.be.a( 'Object' ); } ); + } ); - describe( 'MULTI_ENTRIES_COMMIT_REGEXP', () => { - it( 'should be defined', () => { - expect( transformCommitUtils.MULTI_ENTRIES_COMMIT_REGEXP ).to.be.a( 'RegExp' ); - } ); + describe( 'MULTI_ENTRIES_COMMIT_REGEXP', () => { + it( 'should be defined', () => { + expect( transformCommitUtils.MULTI_ENTRIES_COMMIT_REGEXP ).to.be.a( 'RegExp' ); } ); + } ); - describe( 'getTypeOrder()', () => { - it( 'returns proper values for commit groups', () => { - expect( transformCommitUtils.getTypeOrder( 'Features' ) ).to.equal( 1 ); - expect( transformCommitUtils.getTypeOrder( 'Bug fixes' ) ).to.equal( 2 ); - expect( transformCommitUtils.getTypeOrder( 'Other changes' ) ).to.equal( 3 ); - } ); - - it( 'returns proper values for commit groups when they ends with additional link', () => { - expect( transformCommitUtils.getTypeOrder( 'Features [ℹ](url)' ) ).to.equal( 1 ); - expect( transformCommitUtils.getTypeOrder( 'Bug fixes [ℹ](url)' ) ).to.equal( 2 ); - expect( transformCommitUtils.getTypeOrder( 'Other changes [ℹ](url)' ) ).to.equal( 3 ); - } ); - - it( 'returns proper values for note groups', () => { - expect( transformCommitUtils.getTypeOrder( 'MAJOR BREAKING CHANGES' ) ).to.equal( 1 ); - expect( transformCommitUtils.getTypeOrder( 'MINOR BREAKING CHANGES' ) ).to.equal( 2 ); - expect( transformCommitUtils.getTypeOrder( 'BREAKING CHANGES' ) ).to.equal( 3 ); - } ); + describe( 'getTypeOrder()', () => { + it( 'returns proper values for commit groups', () => { + expect( transformCommitUtils.getTypeOrder( 'Features' ) ).to.equal( 1 ); + expect( transformCommitUtils.getTypeOrder( 'Bug fixes' ) ).to.equal( 2 ); + expect( transformCommitUtils.getTypeOrder( 'Other changes' ) ).to.equal( 3 ); + } ); - it( 'returns proper values for note groups when they ends with additional link', () => { - expect( transformCommitUtils.getTypeOrder( 'MAJOR BREAKING CHANGES [ℹ](url)' ) ).to.equal( 1 ); - expect( transformCommitUtils.getTypeOrder( 'MINOR BREAKING CHANGES [ℹ](url)' ) ).to.equal( 2 ); - expect( transformCommitUtils.getTypeOrder( 'BREAKING CHANGES [ℹ](url)' ) ).to.equal( 3 ); - } ); + it( 'returns proper values for commit groups when they ends with additional link', () => { + expect( transformCommitUtils.getTypeOrder( 'Features [ℹ](url)' ) ).to.equal( 1 ); + expect( transformCommitUtils.getTypeOrder( 'Bug fixes [ℹ](url)' ) ).to.equal( 2 ); + expect( transformCommitUtils.getTypeOrder( 'Other changes [ℹ](url)' ) ).to.equal( 3 ); + } ); - it( 'returns default value for unknown type', () => { - expect( transformCommitUtils.getTypeOrder( 'Foo' ) ).to.equal( 10 ); - expect( transformCommitUtils.getTypeOrder( 'Bar' ) ).to.equal( 10 ); + it( 'returns proper values for note groups', () => { + expect( transformCommitUtils.getTypeOrder( 'MAJOR BREAKING CHANGES' ) ).to.equal( 1 ); + expect( transformCommitUtils.getTypeOrder( 'MINOR BREAKING CHANGES' ) ).to.equal( 2 ); + expect( transformCommitUtils.getTypeOrder( 'BREAKING CHANGES' ) ).to.equal( 3 ); + } ); - expect( transformCommitUtils.getTypeOrder( 'Foo ℹ' ) ).to.equal( 10 ); - expect( transformCommitUtils.getTypeOrder( 'Bar ℹ' ) ).to.equal( 10 ); - } ); + it( 'returns proper values for note groups when they ends with additional link', () => { + expect( transformCommitUtils.getTypeOrder( 'MAJOR BREAKING CHANGES [ℹ](url)' ) ).to.equal( 1 ); + expect( transformCommitUtils.getTypeOrder( 'MINOR BREAKING CHANGES [ℹ](url)' ) ).to.equal( 2 ); + expect( transformCommitUtils.getTypeOrder( 'BREAKING CHANGES [ℹ](url)' ) ).to.equal( 3 ); } ); - describe( 'linkToGithubUser()', () => { - it( 'makes a link to GitHub profile if a user was mentioned in a comment', () => { - expect( transformCommitUtils.linkToGithubUser( 'Foo @CKSource Bar' ) ) - .to.equal( 'Foo [@CKSource](https://github.com/CKSource) Bar' ); - } ); + it( 'returns default value for unknown type', () => { + expect( transformCommitUtils.getTypeOrder( 'Foo' ) ).to.equal( 10 ); + expect( transformCommitUtils.getTypeOrder( 'Bar' ) ).to.equal( 10 ); - it( 'makes a link to GitHub profile if a user was mentioned in a comment at the beginning', () => { - expect( transformCommitUtils.linkToGithubUser( '@CKSource Bar' ) ) - .to.equal( '[@CKSource](https://github.com/CKSource) Bar' ); - } ); + expect( transformCommitUtils.getTypeOrder( 'Foo ℹ' ) ).to.equal( 10 ); + expect( transformCommitUtils.getTypeOrder( 'Bar ℹ' ) ).to.equal( 10 ); + } ); + } ); - it( 'makes a link to GitHub profile if a user was mentioned at the beginning of a line', () => { - expect( transformCommitUtils.linkToGithubUser( 'Foo\n@CKSource Bar' ) ) - .to.equal( 'Foo\n[@CKSource](https://github.com/CKSource) Bar' ); - } ); + describe( 'linkToGithubUser()', () => { + it( 'makes a link to GitHub profile if a user was mentioned in a comment', () => { + expect( transformCommitUtils.linkToGithubUser( 'Foo @CKSource Bar' ) ) + .to.equal( 'Foo [@CKSource](https://github.com/CKSource) Bar' ); + } ); - it( 'makes a link to GitHub profile if a user was mentioned in a comment at the ending', () => { - expect( transformCommitUtils.linkToGithubUser( 'Bar @CKSource' ) ) - .to.equal( 'Bar [@CKSource](https://github.com/CKSource)' ); - } ); + it( 'makes a link to GitHub profile if a user was mentioned in a comment at the beginning', () => { + expect( transformCommitUtils.linkToGithubUser( '@CKSource Bar' ) ) + .to.equal( '[@CKSource](https://github.com/CKSource) Bar' ); + } ); - it( 'makes a link to GitHub profile if a user was mentioned in a bracket', () => { - expect( transformCommitUtils.linkToGithubUser( 'Bar (@CKSource)' ) ) - .to.equal( 'Bar ([@CKSource](https://github.com/CKSource))' ); - } ); + it( 'makes a link to GitHub profile if a user was mentioned at the beginning of a line', () => { + expect( transformCommitUtils.linkToGithubUser( 'Foo\n@CKSource Bar' ) ) + .to.equal( 'Foo\n[@CKSource](https://github.com/CKSource) Bar' ); + } ); - it( 'does nothing if a comment contains scoped package name', () => { - expect( transformCommitUtils.linkToGithubUser( 'Foo @ckeditor/ckeditor5-foo Bar' ) ) - .to.equal( 'Foo @ckeditor/ckeditor5-foo Bar' ); - } ); + it( 'makes a link to GitHub profile if a user was mentioned in a comment at the ending', () => { + expect( transformCommitUtils.linkToGithubUser( 'Bar @CKSource' ) ) + .to.equal( 'Bar [@CKSource](https://github.com/CKSource)' ); + } ); - it( 'does nothing if an email is inside the comment', () => { - expect( transformCommitUtils.linkToGithubUser( 'Foo foo@bar.com Bar' ) ) - .to.equal( 'Foo foo@bar.com Bar' ); - } ); + it( 'makes a link to GitHub profile if a user was mentioned in a bracket', () => { + expect( transformCommitUtils.linkToGithubUser( 'Bar (@CKSource)' ) ) + .to.equal( 'Bar ([@CKSource](https://github.com/CKSource))' ); + } ); - it( 'does nothing if a user is already linked', () => { - expect( transformCommitUtils.linkToGithubUser( 'Foo [@bar](https://github.com/bar) Bar' ) ) - .to.equal( 'Foo [@bar](https://github.com/bar) Bar' ); - } ); + it( 'does nothing if a comment contains scoped package name', () => { + expect( transformCommitUtils.linkToGithubUser( 'Foo @ckeditor/ckeditor5-foo Bar' ) ) + .to.equal( 'Foo @ckeditor/ckeditor5-foo Bar' ); } ); - describe( 'linkToGithubIssue()', () => { - it( 'replaces "#ID" with a link to GitHub issue (packageJson.repository as a string)', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package', - repository: 'https://github.com/ckeditor/ckeditor5-dev' - } ); + it( 'does nothing if an email is inside the comment', () => { + expect( transformCommitUtils.linkToGithubUser( 'Foo foo@bar.com Bar' ) ) + .to.equal( 'Foo foo@bar.com Bar' ); + } ); - expect( transformCommitUtils.linkToGithubIssue( 'Some issue #1.' ) ) - .to.equal( 'Some issue [#1](https://github.com/ckeditor/ckeditor5-dev/issues/1).' ); - } ); + it( 'does nothing if a user is already linked', () => { + expect( transformCommitUtils.linkToGithubUser( 'Foo [@bar](https://github.com/bar) Bar' ) ) + .to.equal( 'Foo [@bar](https://github.com/bar) Bar' ); + } ); + } ); - it( 'replaces "organization/repository#id" with a link to the issue in specified repository', () => { - expect( transformCommitUtils.linkToGithubIssue( 'ckeditor/ckeditor5-dev#1' ) ) - .to.equal( '[ckeditor/ckeditor5-dev#1](https://github.com/ckeditor/ckeditor5-dev/issues/1)' ); + describe( 'linkToGithubIssue()', () => { + it( 'replaces "#ID" with a link to GitHub issue (packageJson.repository as a string)', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package', + repository: 'https://github.com/ckeditor/ckeditor5-dev' } ); - it( 'does not make a link from a comment which is a path', () => { - expect( transformCommitUtils.linkToGithubIssue( 'i/am/a/path#1' ) ) - .to.equal( 'i/am/a/path#1' ); - } ); + expect( transformCommitUtils.linkToGithubIssue( 'Some issue #1.' ) ) + .to.equal( 'Some issue [#1](https://github.com/ckeditor/ckeditor5-dev/issues/1).' ); + } ); - it( 'does not make a link if a comment does not match to "organization/repository"', () => { - expect( transformCommitUtils.linkToGithubIssue( 'ckeditor/ckeditor5-dev/' ) ) - .to.equal( 'ckeditor/ckeditor5-dev/' ); - } ); + it( 'replaces "organization/repository#id" with a link to the issue in specified repository', () => { + expect( transformCommitUtils.linkToGithubIssue( 'ckeditor/ckeditor5-dev#1' ) ) + .to.equal( '[ckeditor/ckeditor5-dev#1](https://github.com/ckeditor/ckeditor5-dev/issues/1)' ); + } ); - it( 'does not make a link from a comment which does not contain the issue id', () => { - expect( transformCommitUtils.linkToGithubIssue( 'ckeditor/ckeditor5-dev#' ) ) - .to.equal( 'ckeditor/ckeditor5-dev#' ); - } ); + it( 'does not make a link from a comment which is a path', () => { + expect( transformCommitUtils.linkToGithubIssue( 'i/am/a/path#1' ) ) + .to.equal( 'i/am/a/path#1' ); + } ); - it( 'does not make a link from a comment which contains color hex code with letters and numbers', () => { - expect( transformCommitUtils.linkToGithubIssue( 'Colors: first: `#8da47e`, second: `#f7ce76`.' ) ) - .to.equal( 'Colors: first: `#8da47e`, second: `#f7ce76`.' ); - } ); + it( 'does not make a link if a comment does not match to "organization/repository"', () => { + expect( transformCommitUtils.linkToGithubIssue( 'ckeditor/ckeditor5-dev/' ) ) + .to.equal( 'ckeditor/ckeditor5-dev/' ); + } ); - it( 'does not make a link from a comment which contains color hex code with letters or numbers only', () => { - expect( transformCommitUtils.linkToGithubIssue( 'Colors: first: `#000000`, second: `#ffffff`.' ) ) - .to.equal( 'Colors: first: `#000000`, second: `#ffffff`.' ); - } ); + it( 'does not make a link from a comment which does not contain the issue id', () => { + expect( transformCommitUtils.linkToGithubIssue( 'ckeditor/ckeditor5-dev#' ) ) + .to.equal( 'ckeditor/ckeditor5-dev#' ); } ); - describe( 'getCommitType()', () => { - it( 'throws an error when passed unsupported commit type', () => { - expect( () => transformCommitUtils.getCommitType( 'invalid' ) ) - .to.throw( Error, 'Given invalid type of commit ("invalid").' ); - } ); + it( 'does not make a link from a comment which contains color hex code with letters and numbers', () => { + expect( transformCommitUtils.linkToGithubIssue( 'Colors: first: `#8da47e`, second: `#f7ce76`.' ) ) + .to.equal( 'Colors: first: `#8da47e`, second: `#f7ce76`.' ); + } ); - it( 'changes a singular type of commit to plural', () => { - expect( transformCommitUtils.getCommitType( 'Feature' ) ).to.equal( 'Features' ); - expect( transformCommitUtils.getCommitType( 'Fix' ) ).to.equal( 'Bug fixes' ); - expect( transformCommitUtils.getCommitType( 'Other' ) ).to.equal( 'Other changes' ); - } ); + it( 'does not make a link from a comment which contains color hex code with letters or numbers only', () => { + expect( transformCommitUtils.linkToGithubIssue( 'Colors: first: `#000000`, second: `#ffffff`.' ) ) + .to.equal( 'Colors: first: `#000000`, second: `#ffffff`.' ); } ); + } ); - describe( 'truncate()', () => { - it( 'does not modify too short sentence', () => { - const sentence = 'This is a short sentence.'; + describe( 'getCommitType()', () => { + it( 'throws an error when passed unsupported commit type', () => { + expect( () => transformCommitUtils.getCommitType( 'invalid' ) ) + .to.throw( Error, 'Given invalid type of commit ("invalid").' ); + } ); - expect( transformCommitUtils.truncate( sentence, 25 ) ).to.equal( sentence ); - } ); + it( 'changes a singular type of commit to plural', () => { + expect( transformCommitUtils.getCommitType( 'Feature' ) ).to.equal( 'Features' ); + expect( transformCommitUtils.getCommitType( 'Fix' ) ).to.equal( 'Bug fixes' ); + expect( transformCommitUtils.getCommitType( 'Other' ) ).to.equal( 'Other changes' ); + } ); + } ); - it( 'truncates too long sentence', () => { - const sentence = 'This is a short sentence.'; + describe( 'truncate()', () => { + it( 'does not modify too short sentence', () => { + const sentence = 'This is a short sentence.'; - expect( transformCommitUtils.truncate( sentence, 13 ) ).to.equal( 'This is a...' ); - } ); + expect( transformCommitUtils.truncate( sentence, 25 ) ).to.equal( sentence ); } ); - describe( 'getRepositoryUrl()', () => { - it( 'throws an error if package.json does not contain the "repository" property', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package' - } ); + it( 'truncates too long sentence', () => { + const sentence = 'This is a short sentence.'; - expect( () => transformCommitUtils.getRepositoryUrl() ) - .to.throw( Error, 'The package.json for "test-package" must contain the "repository" property.' ); - } ); + expect( transformCommitUtils.truncate( sentence, 13 ) ).to.equal( 'This is a...' ); + } ); + } ); - it( 'passes specified `cwd` to `getPackageJson()` util', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package', - repository: 'https://github.com/ckeditor/ckeditor5-dev/issues' - } ); + describe( 'getRepositoryUrl()', () => { + it( 'throws an error if package.json does not contain the "repository" property', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package' + } ); - transformCommitUtils.getRepositoryUrl( 'foo' ); + expect( () => transformCommitUtils.getRepositoryUrl() ) + .to.throw( Error, 'The package.json for "test-package" must contain the "repository" property.' ); + } ); - expect( getPackageJson ).toHaveBeenCalledTimes( 1 ); - expect( getPackageJson ).toHaveBeenCalledWith( 'foo' ); + it( 'passes specified `cwd` to `getPackageJson()` util', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package', + repository: 'https://github.com/ckeditor/ckeditor5-dev/issues' } ); - it( 'returns the repository URL (packageJson.repository as a string, contains "/issues")', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package', - repository: 'https://github.com/ckeditor/ckeditor5-dev/issues' - } ); + transformCommitUtils.getRepositoryUrl( 'foo' ); + + expect( getPackageJson ).toHaveBeenCalledTimes( 1 ); + expect( getPackageJson ).toHaveBeenCalledWith( 'foo' ); + } ); - expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + it( 'returns the repository URL (packageJson.repository as a string, contains "/issues")', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package', + repository: 'https://github.com/ckeditor/ckeditor5-dev/issues' } ); - it( 'returns the repository URL (packageJson.repository as a string, ends with ".git")', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package', - repository: 'https://github.com/ckeditor/ckeditor5-dev.git' - } ); + expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); - expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + it( 'returns the repository URL (packageJson.repository as a string, ends with ".git")', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package', + repository: 'https://github.com/ckeditor/ckeditor5-dev.git' } ); - it( 'returns the repository URL (packageJson.repository as an object)', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package', - repository: { - url: 'https://github.com/ckeditor/ckeditor5-dev' - } - } ); + expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); - expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + it( 'returns the repository URL (packageJson.repository as an object)', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package', + repository: { + url: 'https://github.com/ckeditor/ckeditor5-dev' + } } ); - it( 'returns the repository URL (packageJson.repository as an object, contains "/issues")', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package', - repository: { - url: 'https://github.com/ckeditor/ckeditor5-dev/issues', - type: 'git' - } - } ); + expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); - expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + it( 'returns the repository URL (packageJson.repository as an object, contains "/issues")', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package', + repository: { + url: 'https://github.com/ckeditor/ckeditor5-dev/issues', + type: 'git' + } } ); - it( 'returns the repository URL (packageJson.repository as an object, ends with ".git")', () => { - vi.mocked( getPackageJson ).mockReturnValue( { - name: 'test-package', - repository: { - url: 'https://github.com/ckeditor/ckeditor5-dev.git', - type: 'git' - } - } ); + expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + } ); - expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); + it( 'returns the repository URL (packageJson.repository as an object, ends with ".git")', () => { + vi.mocked( getPackageJson ).mockReturnValue( { + name: 'test-package', + repository: { + url: 'https://github.com/ckeditor/ckeditor5-dev.git', + type: 'git' + } } ); + + expect( transformCommitUtils.getRepositoryUrl() ).to.equal( 'https://github.com/ckeditor/ckeditor5-dev' ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js b/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js index c62f654c9..88725ca1d 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/validaterepositorytorelease.js @@ -10,117 +10,115 @@ import validateRepositoryToRelease from '../../lib/utils/validaterepositorytorel vi.mock( '@ckeditor/ckeditor5-dev-utils' ); -describe( 'dev-release-tools/utils', () => { - describe( 'validateRepositoryToRelease()', () => { - it( 'resolves an empty array if validation passes (remote branch exists)', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); +describe( 'validateRepositoryToRelease()', () => { + it( 'resolves an empty array if validation passes (remote branch exists)', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); - expect( errors ).to.be.an( 'Array' ); - expect( errors.length ).to.equal( 0 ); - } ); - - it( 'resolves an empty array if validation passes (missing remote branch)', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## master' ); + expect( errors ).to.be.an( 'Array' ); + expect( errors.length ).to.equal( 0 ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); + it( 'resolves an empty array if validation passes (missing remote branch)', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## master' ); - expect( errors ).to.be.an( 'Array' ); - expect( errors.length ).to.equal( 0 ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); - it( 'resolves an array with errors if the release changes are not defined', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); + expect( errors ).to.be.an( 'Array' ); + expect( errors.length ).to.equal( 0 ); + } ); - const errors = await validateRepositoryToRelease( { changes: null, version: '1.0.0' } ); + it( 'resolves an array with errors if the release changes are not defined', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); - expect( errors.length ).to.equal( 1 ); - expect( errors[ 0 ] ).to.equal( 'Cannot find changelog entries for version "1.0.0".' ); - } ); + const errors = await validateRepositoryToRelease( { changes: null, version: '1.0.0' } ); - it( 'resolves an array with errors if the specified version is not a string', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); + expect( errors.length ).to.equal( 1 ); + expect( errors[ 0 ] ).to.equal( 'Cannot find changelog entries for version "1.0.0".' ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: null } ); + it( 'resolves an array with errors if the specified version is not a string', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); - expect( errors.length ).to.equal( 1 ); - expect( errors[ 0 ] ).to.equal( 'Passed an invalid version ("null").' ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: null } ); - it( 'resolves an array with errors if the specified version is empty string', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); + expect( errors.length ).to.equal( 1 ); + expect( errors[ 0 ] ).to.equal( 'Passed an invalid version ("null").' ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '' } ); + it( 'resolves an array with errors if the specified version is empty string', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master' ); - expect( errors.length ).to.equal( 1 ); - expect( errors[ 0 ] ).to.equal( 'Passed an invalid version ("").' ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '' } ); - it( 'resolves an array with errors if current branch is not "master" (remote branch exists)', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); + expect( errors.length ).to.equal( 1 ); + expect( errors[ 0 ] ).to.equal( 'Passed an invalid version ("").' ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); + it( 'resolves an array with errors if current branch is not "master" (remote branch exists)', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); - expect( errors.length ).to.equal( 1 ); - expect( errors[ 0 ] ).to.equal( 'Not on the "#master" branch.' ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); - it( 'resolves an array with errors if current branch is not "master" (missing remote branch)', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## develop' ); + expect( errors.length ).to.equal( 1 ); + expect( errors[ 0 ] ).to.equal( 'Not on the "#master" branch.' ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); + it( 'resolves an array with errors if current branch is not "master" (missing remote branch)', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## develop' ); - expect( errors.length ).to.equal( 1 ); - expect( errors[ 0 ] ).to.equal( 'Not on the "#master" branch.' ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); - it( 'resolves an array with errors if master is behind with origin (remote branch exists)', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master [behind 2]' ); + expect( errors.length ).to.equal( 1 ); + expect( errors[ 0 ] ).to.equal( 'Not on the "#master" branch.' ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); + it( 'resolves an array with errors if master is behind with origin (remote branch exists)', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## master...origin/master [behind 2]' ); - expect( errors.length ).to.equal( 1 ); - expect( errors[ 0 ] ).to.equal( 'The branch is behind with the remote.' ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); - it( 'resolves an array with errors if master is behind with origin (missing remote branch)', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## master [behind 2]' ); + expect( errors.length ).to.equal( 1 ); + expect( errors[ 0 ] ).to.equal( 'The branch is behind with the remote.' ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); + it( 'resolves an array with errors if master is behind with origin (missing remote branch)', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## master [behind 2]' ); - expect( errors.length ).to.equal( 1 ); - expect( errors[ 0 ] ).to.equal( 'The branch is behind with the remote.' ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0' } ); - it( 'allows skipping the branch check', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); + expect( errors.length ).to.equal( 1 ); + expect( errors[ 0 ] ).to.equal( 'The branch is behind with the remote.' ); + } ); - const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0', ignoreBranchCheck: true } ); + it( 'allows skipping the branch check', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); - expect( errors.length ).to.equal( 0 ); - } ); + const errors = await validateRepositoryToRelease( { changes: 'Some changes.', version: '1.0.0', ignoreBranchCheck: true } ); - it( 'uses non-master branch for releasing if specified', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## release...origin/release' ); + expect( errors.length ).to.equal( 0 ); + } ); - const errors = await validateRepositoryToRelease( { branch: 'release', changes: 'Some changes.', version: '1.0.0' } ); + it( 'uses non-master branch for releasing if specified', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## release...origin/release' ); - expect( errors ).to.be.an( 'Array' ); - expect( errors.length ).to.equal( 0 ); - } ); + const errors = await validateRepositoryToRelease( { branch: 'release', changes: 'Some changes.', version: '1.0.0' } ); - it( 'allows skipping the branch check (even if specified)', async () => { - vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); + expect( errors ).to.be.an( 'Array' ); + expect( errors.length ).to.equal( 0 ); + } ); - const errors = await validateRepositoryToRelease( { - branch: 'release', - changes: 'Some changes.', - version: '1.0.0', - ignoreBranchCheck: true - } ); + it( 'allows skipping the branch check (even if specified)', async () => { + vi.mocked( tools.shExec ).mockResolvedValue( '## develop...origin/develop' ); - expect( errors.length ).to.equal( 0 ); + const errors = await validateRepositoryToRelease( { + branch: 'release', + changes: 'Some changes.', + version: '1.0.0', + ignoreBranchCheck: true } ); + + expect( errors.length ).to.equal( 0 ); } ); } ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js index 655c91788..c9a47a280 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/versions.js @@ -22,322 +22,320 @@ vi.mock( '@ckeditor/ckeditor5-dev-utils' ); vi.mock( '../../lib/utils/getchangelog.js' ); vi.mock( '../../lib/utils/getpackagejson.js' ); -describe( 'dev-release-tools/utils', () => { - describe( 'versions', () => { - describe( 'getLastFromChangelog()', () => { - it( 'returns null if the changelog is invalid', () => { - vi.mocked( getChangelog ).mockReturnValue( 'Example changelog.' ); +describe( 'versions', () => { + describe( 'getLastFromChangelog()', () => { + it( 'returns null if the changelog is invalid', () => { + vi.mocked( getChangelog ).mockReturnValue( 'Example changelog.' ); - expect( getLastFromChangelog() ).to.equal( null ); - } ); + expect( getLastFromChangelog() ).to.equal( null ); + } ); + + it( 'returns version from changelog #1', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + + expect( getLastFromChangelog() ).to.equal( '1.0.0' ); + } ); + + it( 'returns version from changelog #2', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0 (2017-04-05)\nSome changelog entry.' ); + + expect( getLastFromChangelog() ).to.equal( '1.0.0' ); + } ); + + it( 'returns version from changelog #3', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-alpha](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); + } ); + + it( 'returns version from changelog #4', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-alpha (2017-04-05)\nSome changelog entry.' ); + + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); + } ); + + it( 'returns version from changelog #5', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-alpha+001](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); + } ); + + it( 'returns version from changelog #6', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-alpha+001 (2017-04-05)\nSome changelog entry.' ); + + expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); + } ); - it( 'returns version from changelog #1', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + it( 'returns version from changelog #7', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-beta.2](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); - expect( getLastFromChangelog() ).to.equal( '1.0.0' ); - } ); + expect( getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); + } ); + + it( 'returns version from changelog #8', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-beta.2 (2017-04-05)\nSome changelog entry.' ); + + expect( getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); + } ); + + it( 'returns version from changelog #9', () => { + vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0\nSome changelog entry.' ); - it( 'returns version from changelog #2', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0 (2017-04-05)\nSome changelog entry.' ); + expect( getLastFromChangelog() ).to.equal( '1.0.0' ); + } ); - expect( getLastFromChangelog() ).to.equal( '1.0.0' ); - } ); + it( 'returns null for empty changelog', () => { + vi.mocked( getChangelog ).mockReturnValue( '' ); - it( 'returns version from changelog #3', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-alpha](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + expect( getLastFromChangelog() ).to.equal( null ); + } ); - expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); - } ); + it( 'returns null if changelog does not exist', () => { + vi.mocked( getChangelog ).mockReturnValue( null ); - it( 'returns version from changelog #4', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-alpha (2017-04-05)\nSome changelog entry.' ); + expect( getLastFromChangelog() ).to.equal( null ); + } ); + } ); - expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha' ); - } ); + describe( 'getLastPreRelease()', () => { + beforeEach( () => { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); + } ); - it( 'returns version from changelog #5', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-alpha+001](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); + it( 'asks npm for all versions of a package', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [] ) ); + + return getLastPreRelease( '42.0.0-alpha' ) + .then( () => { + expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); + expect( tools.shExec ).toHaveBeenCalledWith( 'npm view ckeditor5 versions --json', expect.anything() ); + } ); + } ); + + it( 'returns null if there is no version for a package', () => { + vi.mocked( tools.shExec ).mockRejectedValue(); + + return getLastPreRelease( '42.0.0-alpha' ) + .then( result => { + expect( result ).to.equal( null ); + } ); + } ); + + it( 'returns null if there is no pre-release version matching the release identifier', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '37.0.0-alpha.1', + '41.0.0', + '42.0.0' + ] ) ); + + return getLastPreRelease( '42.0.0-alpha' ) + .then( result => { + expect( result ).to.equal( null ); + } ); + } ); + + it( 'returns last pre-release version matching the release identifier', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '37.0.0-alpha.1', + '41.0.0', + '42.0.0' + ] ) ); + + return getLastPreRelease( '37.0.0-alpha' ) + .then( result => { + expect( result ).to.equal( '37.0.0-alpha.1' ); + } ); + } ); - expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); - } ); + it( 'returns last pre-release version matching the release identifier (non-chronological versions order)', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '37.0.0-alpha.2', + '41.0.0', + '42.0.0', + '37.0.0-alpha.1' + ] ) ); + + return getLastPreRelease( '37.0.0-alpha' ) + .then( result => { + expect( result ).to.equal( '37.0.0-alpha.2' ); + } ); + } ); + + it( 'returns last pre-release version matching the release identifier (sequence numbers greater than 10)', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.1', + '37.0.0-alpha.2', + '37.0.0-alpha.3', + '41.0.0', + '37.0.0-alpha.10', + '37.0.0-alpha.11' + ] ) ); + + return getLastPreRelease( '37.0.0-alpha' ) + .then( result => { + expect( result ).to.equal( '37.0.0-alpha.11' ); + } ); + } ); + + it( 'returns last nightly version', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230614.0', + '0.0.0-nightly-20230615.0', + '0.0.0-nightly-20230615.1', + '0.0.0-nightly-20230615.2', + '0.0.0-nightly-20230616.0', + '37.0.0-alpha.0', + '37.0.0-alpha.2', + '41.0.0', + '42.0.0' + ] ) ); + + return getLastPreRelease( '0.0.0-nightly' ) + .then( result => { + expect( result ).to.equal( '0.0.0-nightly-20230616.0' ); + } ); + } ); + + it( 'returns last nightly version from a specified day', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230614.0', + '0.0.0-nightly-20230615.0', + '0.0.0-nightly-20230615.1', + '0.0.0-nightly-20230615.2', + '0.0.0-nightly-20230616.0', + '37.0.0-alpha.0', + '37.0.0-alpha.2', + '41.0.0', + '42.0.0' + ] ) ); + + return getLastPreRelease( '0.0.0-nightly-20230615' ) + .then( result => { + expect( result ).to.equal( '0.0.0-nightly-20230615.2' ); + } ); + } ); + } ); + + describe( 'getLastNightly()', () => { + beforeEach( async () => { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); + } ); + + it( 'returns last nightly pre-release version', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230613.0', + '0.0.0-nightly-20230614.0', + '0.0.0-nightly-20230614.1', + '0.0.0-nightly-20230614.2', + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); + + return getLastNightly() + .then( result => { + expect( result ).to.equal( '0.0.0-nightly-20230615.0' ); + } ); + } ); + } ); + + describe( 'getNextPreRelease()', () => { + beforeEach( async () => { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); + } ); + + it( 'returns pre-release version with id = 0 if pre-release version was never published for the package yet', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); + + return getNextPreRelease( '42.0.0-alpha' ) + .then( result => { + expect( result ).to.equal( '42.0.0-alpha.0' ); + } ); + } ); + + it( 'returns pre-release version with incremented id if older pre-release version was already published', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0-alpha.5' + ] ) ); + + return getNextPreRelease( '42.0.0-alpha' ) + .then( result => { + expect( result ).to.equal( '42.0.0-alpha.6' ); + } ); + } ); + + it( 'returns nightly version with incremented id if older nightly version was already published', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.5', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); + + return getNextPreRelease( '0.0.0-nightly' ) + .then( result => { + expect( result ).to.equal( '0.0.0-nightly-20230615.6' ); + } ); + } ); + } ); + + describe( 'getNextNightly()', () => { + beforeEach( () => { + vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); + + vi.useFakeTimers(); + vi.setSystemTime( new Date( '2023-06-15 12:00:00' ) ); + } ); + + afterEach( () => { + vi.useRealTimers(); + } ); + + it( 'asks for a last nightly pre-release version', () => { + vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ + '0.0.0-nightly-20230615.0', + '37.0.0-alpha.0', + '42.0.0' + ] ) ); + + return getNextNightly() + .then( result => { + expect( result ).to.equal( '0.0.0-nightly-20230615.1' ); + } ); + } ); + } ); + + describe( 'getLastTagFromGit()', () => { + it( 'returns last tag if exists', () => { + vi.mocked( tools.shExec ).mockReturnValue( 'v1.0.0' ); + + expect( getLastTagFromGit() ).to.equal( '1.0.0' ); + } ); + + it( 'returns null if tags do not exist', () => { + vi.mocked( tools.shExec ).mockReturnValue( '' ); + + expect( getLastTagFromGit() ).to.equal( null ); + } ); + } ); - it( 'returns version from changelog #6', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-alpha+001 (2017-04-05)\nSome changelog entry.' ); + describe( 'getCurrent()', () => { + it( 'returns current version from "package.json"', () => { + vi.mocked( getPackageJson ).mockReturnValue( { version: '0.1.2' } ); - expect( getLastFromChangelog() ).to.equal( '1.0.0-alpha+001' ); - } ); - - it( 'returns version from changelog #7', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## [1.0.0-beta.2](...) (2017-04-05)\nSome changelog entry.\n\n## 0.0.1' ); - - expect( getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); - } ); - - it( 'returns version from changelog #8', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0-beta.2 (2017-04-05)\nSome changelog entry.' ); - - expect( getLastFromChangelog() ).to.equal( '1.0.0-beta.2' ); - } ); - - it( 'returns version from changelog #9', () => { - vi.mocked( getChangelog ).mockReturnValue( '\n## 1.0.0\nSome changelog entry.' ); - - expect( getLastFromChangelog() ).to.equal( '1.0.0' ); - } ); - - it( 'returns null for empty changelog', () => { - vi.mocked( getChangelog ).mockReturnValue( '' ); - - expect( getLastFromChangelog() ).to.equal( null ); - } ); - - it( 'returns null if changelog does not exist', () => { - vi.mocked( getChangelog ).mockReturnValue( null ); - - expect( getLastFromChangelog() ).to.equal( null ); - } ); - } ); - - describe( 'getLastPreRelease()', () => { - beforeEach( () => { - vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); - } ); - - it( 'asks npm for all versions of a package', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [] ) ); - - return getLastPreRelease( '42.0.0-alpha' ) - .then( () => { - expect( tools.shExec ).toHaveBeenCalledTimes( 1 ); - expect( tools.shExec ).toHaveBeenCalledWith( 'npm view ckeditor5 versions --json', expect.anything() ); - } ); - } ); - - it( 'returns null if there is no version for a package', () => { - vi.mocked( tools.shExec ).mockRejectedValue(); - - return getLastPreRelease( '42.0.0-alpha' ) - .then( result => { - expect( result ).to.equal( null ); - } ); - } ); - - it( 'returns null if there is no pre-release version matching the release identifier', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.0', - '37.0.0-alpha.1', - '41.0.0', - '42.0.0' - ] ) ); - - return getLastPreRelease( '42.0.0-alpha' ) - .then( result => { - expect( result ).to.equal( null ); - } ); - } ); - - it( 'returns last pre-release version matching the release identifier', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.0', - '37.0.0-alpha.1', - '41.0.0', - '42.0.0' - ] ) ); - - return getLastPreRelease( '37.0.0-alpha' ) - .then( result => { - expect( result ).to.equal( '37.0.0-alpha.1' ); - } ); - } ); - - it( 'returns last pre-release version matching the release identifier (non-chronological versions order)', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.0', - '37.0.0-alpha.2', - '41.0.0', - '42.0.0', - '37.0.0-alpha.1' - ] ) ); - - return getLastPreRelease( '37.0.0-alpha' ) - .then( result => { - expect( result ).to.equal( '37.0.0-alpha.2' ); - } ); - } ); - - it( 'returns last pre-release version matching the release identifier (sequence numbers greater than 10)', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.1', - '37.0.0-alpha.2', - '37.0.0-alpha.3', - '41.0.0', - '37.0.0-alpha.10', - '37.0.0-alpha.11' - ] ) ); - - return getLastPreRelease( '37.0.0-alpha' ) - .then( result => { - expect( result ).to.equal( '37.0.0-alpha.11' ); - } ); - } ); - - it( 'returns last nightly version', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230614.0', - '0.0.0-nightly-20230615.0', - '0.0.0-nightly-20230615.1', - '0.0.0-nightly-20230615.2', - '0.0.0-nightly-20230616.0', - '37.0.0-alpha.0', - '37.0.0-alpha.2', - '41.0.0', - '42.0.0' - ] ) ); - - return getLastPreRelease( '0.0.0-nightly' ) - .then( result => { - expect( result ).to.equal( '0.0.0-nightly-20230616.0' ); - } ); - } ); - - it( 'returns last nightly version from a specified day', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230614.0', - '0.0.0-nightly-20230615.0', - '0.0.0-nightly-20230615.1', - '0.0.0-nightly-20230615.2', - '0.0.0-nightly-20230616.0', - '37.0.0-alpha.0', - '37.0.0-alpha.2', - '41.0.0', - '42.0.0' - ] ) ); - - return getLastPreRelease( '0.0.0-nightly-20230615' ) - .then( result => { - expect( result ).to.equal( '0.0.0-nightly-20230615.2' ); - } ); - } ); - } ); - - describe( 'getLastNightly()', () => { - beforeEach( async () => { - vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); - } ); - - it( 'returns last nightly pre-release version', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230613.0', - '0.0.0-nightly-20230614.0', - '0.0.0-nightly-20230614.1', - '0.0.0-nightly-20230614.2', - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.0', - '42.0.0' - ] ) ); - - return getLastNightly() - .then( result => { - expect( result ).to.equal( '0.0.0-nightly-20230615.0' ); - } ); - } ); - } ); - - describe( 'getNextPreRelease()', () => { - beforeEach( async () => { - vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); - } ); - - it( 'returns pre-release version with id = 0 if pre-release version was never published for the package yet', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.0', - '42.0.0' - ] ) ); - - return getNextPreRelease( '42.0.0-alpha' ) - .then( result => { - expect( result ).to.equal( '42.0.0-alpha.0' ); - } ); - } ); - - it( 'returns pre-release version with incremented id if older pre-release version was already published', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.0', - '42.0.0-alpha.5' - ] ) ); - - return getNextPreRelease( '42.0.0-alpha' ) - .then( result => { - expect( result ).to.equal( '42.0.0-alpha.6' ); - } ); - } ); - - it( 'returns nightly version with incremented id if older nightly version was already published', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.5', - '37.0.0-alpha.0', - '42.0.0' - ] ) ); - - return getNextPreRelease( '0.0.0-nightly' ) - .then( result => { - expect( result ).to.equal( '0.0.0-nightly-20230615.6' ); - } ); - } ); - } ); - - describe( 'getNextNightly()', () => { - beforeEach( () => { - vi.mocked( getPackageJson ).mockReturnValue( { name: 'ckeditor5' } ); - - vi.useFakeTimers(); - vi.setSystemTime( new Date( '2023-06-15 12:00:00' ) ); - } ); - - afterEach( () => { - vi.useRealTimers(); - } ); - - it( 'asks for a last nightly pre-release version', () => { - vi.mocked( tools.shExec ).mockResolvedValue( JSON.stringify( [ - '0.0.0-nightly-20230615.0', - '37.0.0-alpha.0', - '42.0.0' - ] ) ); - - return getNextNightly() - .then( result => { - expect( result ).to.equal( '0.0.0-nightly-20230615.1' ); - } ); - } ); - } ); - - describe( 'getLastTagFromGit()', () => { - it( 'returns last tag if exists', () => { - vi.mocked( tools.shExec ).mockReturnValue( 'v1.0.0' ); - - expect( getLastTagFromGit() ).to.equal( '1.0.0' ); - } ); - - it( 'returns null if tags do not exist', () => { - vi.mocked( tools.shExec ).mockReturnValue( '' ); - - expect( getLastTagFromGit() ).to.equal( null ); - } ); - } ); - - describe( 'getCurrent()', () => { - it( 'returns current version from "package.json"', () => { - vi.mocked( getPackageJson ).mockReturnValue( { version: '0.1.2' } ); - - expect( getCurrent() ).to.equal( '0.1.2' ); - } ); + expect( getCurrent() ).to.equal( '0.1.2' ); } ); } ); } ); From 6690c430904dd86b523e62aab81136788a0e89f5 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 12:50:44 +0200 Subject: [PATCH 073/172] Init. --- .eslintrc.js | 1 + .../lib/builds/getdllpluginwebpackconfig.js | 28 +- .../ckeditor5-dev-utils/lib/builds/index.js | 6 +- .../lib/bundler/createentryfile.js | 14 +- .../lib/bundler/geteditorconfig.js | 8 +- .../lib/bundler/getlicensebanner.js | 6 +- .../lib/bundler/getplugins.js | 8 +- .../ckeditor5-dev-utils/lib/bundler/index.js | 8 +- packages/ckeditor5-dev-utils/lib/index.js | 18 +- .../lib/loaders/ck-debug-loader.js | 4 +- .../ckeditor5-dev-utils/lib/loaders/index.js | 279 +++++++++--------- packages/ckeditor5-dev-utils/lib/logger.js | 9 +- packages/ckeditor5-dev-utils/lib/stream.js | 10 +- .../lib/styles/getpostcssconfig.js | 25 +- .../ckeditor5-dev-utils/lib/styles/index.js | 10 +- .../lib/styles/themeimporter.js | 30 +- .../lib/styles/themelogger.js | 10 +- .../lib/styles/utils/getpackagename.js | 6 +- packages/ckeditor5-dev-utils/lib/tools.js | 21 +- .../lib/tools/createspinner.js | 14 +- packages/ckeditor5-dev-utils/package.json | 13 +- .../tests/_utils/testsetup.js | 9 + .../tests/builds/getdllpluginwebpackconfig.js | 177 +++++------ .../ckeditor5-dev-utils/tests/builds/index.js | 14 +- packages/ckeditor5-dev-utils/vitest.config.js | 30 ++ 25 files changed, 381 insertions(+), 377 deletions(-) create mode 100644 packages/ckeditor5-dev-utils/tests/_utils/testsetup.js create mode 100644 packages/ckeditor5-dev-utils/vitest.config.js diff --git a/.eslintrc.js b/.eslintrc.js index 33af49749..a0c5542d9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { { files: [ // TODO: add packages as they are migrated to ESM. + './packages/ckeditor5-dev-utils/**/*', './packages/ckeditor5-dev-translations/**/*', './packages/ckeditor5-dev-release-tools/**/*', './packages/ckeditor5-dev-bump-year/**/*', diff --git a/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js b/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js index 6df015019..db5aca77d 100644 --- a/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js +++ b/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js @@ -3,13 +3,11 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const fs = require( 'fs-extra' ); -const { CKEditorTranslationsPlugin } = require( '@ckeditor/ckeditor5-dev-translations' ); -const bundler = require( '../bundler' ); -const loaders = require( '../loaders' ); +import path from 'path'; +import fs from 'fs-extra'; +import { CKEditorTranslationsPlugin } from '@ckeditor/ckeditor5-dev-translations'; +import { getLicenseBanner } from '../bundler/index.js'; +import { getIconsLoader, getStylesLoader, getTypeScriptLoader } from '../loaders/index.js'; /** * Returns a webpack configuration that creates a bundle file for the specified package. Thanks to that, plugins exported @@ -26,11 +24,11 @@ const loaders = require( '../loaders' ); * @param {Boolean} [options.isDevelopmentMode=false] Whether to build a dev mode of the package. * @returns {Object} */ -module.exports = function getDllPluginWebpackConfig( webpack, options ) { +export default async function getDllPluginWebpackConfig( webpack, options ) { // Terser requires webpack. However, it's needed in runtime. To avoid the "Cannot find module 'webpack'" error, // let's load the Terser dependency when `getDllPluginWebpackConfig()` is executed. // See: https://github.com/ckeditor/ckeditor5/issues/13136. - const TerserPlugin = require( 'terser-webpack-plugin' ); + const TerserPlugin = ( await import( 'terser-webpack-plugin' ) ).default; const { name: packageName } = fs.readJsonSync( path.join( options.packagePath, 'package.json' ) ); const langDirExists = fs.existsSync( path.join( options.packagePath, 'lang' ) ); @@ -58,11 +56,11 @@ module.exports = function getDllPluginWebpackConfig( webpack, options ) { plugins: [ new webpack.BannerPlugin( { - banner: bundler.getLicenseBanner(), + banner: getLicenseBanner(), raw: true } ), new webpack.DllReferencePlugin( { - manifest: require( options.manifestPath ), + manifest: fs.readJsonSync( options.manifestPath ), scope: 'ckeditor5/src', name: 'CKEditor5.dll' } ) @@ -77,12 +75,12 @@ module.exports = function getDllPluginWebpackConfig( webpack, options ) { module: { rules: [ - loaders.getIconsLoader( { matchExtensionOnly: true } ), - loaders.getStylesLoader( { + getIconsLoader( { matchExtensionOnly: true } ), + getStylesLoader( { themePath: options.themePath, minify: true } ), - loaders.getTypeScriptLoader( { + getTypeScriptLoader( { configFile: options.tsconfigPath || 'tsconfig.json' } ) ] @@ -127,7 +125,7 @@ module.exports = function getDllPluginWebpackConfig( webpack, options ) { } return webpackConfig; -}; +} /** * Transforms the package name (`@ckeditor/ckeditor5-foo-bar`) to the name that will be used while diff --git a/packages/ckeditor5-dev-utils/lib/builds/index.js b/packages/ckeditor5-dev-utils/lib/builds/index.js index 4519c5e3d..d3a5f8a80 100644 --- a/packages/ckeditor5-dev-utils/lib/builds/index.js +++ b/packages/ckeditor5-dev-utils/lib/builds/index.js @@ -3,8 +3,4 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = { - getDllPluginWebpackConfig: require( './getdllpluginwebpackconfig' ) -}; +export { default as getDllPluginWebpackConfig } from './getdllpluginwebpackconfig.js'; diff --git a/packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js b/packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js index 569948e8a..77cbae473 100644 --- a/packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js +++ b/packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const getPlugins = require( './getplugins' ); -const getEditorConfig = require( './geteditorconfig' ); +import fs from 'fs'; +import getPlugins from './getplugins.js'; +import getEditorConfig from './geteditorconfig.js'; /** * Generates an entry file which can be compiled by bundler, e.g. Webpack or Rollup. @@ -19,11 +17,11 @@ const getEditorConfig = require( './geteditorconfig' ); * @param {String} options.editor A path to class which defined the editor. * @param {Object} options.config Additional editor's configuration which will be built-in. */ -module.exports = function createEntryFile( destinationPath, options ) { +export default function createEntryFile( destinationPath, options ) { const entryFileContent = renderEntryFile( options ); fs.writeFileSync( destinationPath, entryFileContent ); -}; +} function renderEntryFile( options ) { const plugins = getPlugins( options.plugins ); @@ -49,7 +47,7 @@ ${ options.moduleName }.build = { ${ Object.keys( plugins ).join( ',\n\t\t' ) } ], config: ${ getEditorConfig( options.config ) } -}; +} `; return content; diff --git a/packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js b/packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js index 44968d929..e3eb9f260 100644 --- a/packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js +++ b/packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const javascriptStringify = require( 'javascript-stringify' ); +import javascriptStringify from 'javascript-stringify'; /** * Transforms specified configuration to a string that match to our code style. @@ -13,7 +11,7 @@ const javascriptStringify = require( 'javascript-stringify' ); * @param {Object} config * @returns {String} */ -module.exports = function getEditorConfig( config ) { +export default function getEditorConfig( config ) { if ( !config ) { return '{}'; } @@ -21,4 +19,4 @@ module.exports = function getEditorConfig( config ) { return javascriptStringify( config, null, '\t' ) // Indent all but the first line (so it can be easily concatenated with `config = ${ editorConfig }`). .replace( /\n/g, '\n\t' ); -}; +} diff --git a/packages/ckeditor5-dev-utils/lib/bundler/getlicensebanner.js b/packages/ckeditor5-dev-utils/lib/bundler/getlicensebanner.js index e9d4ad409..a826c3273 100644 --- a/packages/ckeditor5-dev-utils/lib/bundler/getlicensebanner.js +++ b/packages/ckeditor5-dev-utils/lib/bundler/getlicensebanner.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = function getLicenseBanner() { +export default function getLicenseBanner() { const date = new Date(); // License banner starts with `!`. That combines with uglifyjs' `comments` /^!/ option @@ -20,4 +18,4 @@ module.exports = function getLicenseBanner() { */` ); /* eslint-enable indent */ -}; +} diff --git a/packages/ckeditor5-dev-utils/lib/bundler/getplugins.js b/packages/ckeditor5-dev-utils/lib/bundler/getplugins.js index dc7d99246..f60d54add 100644 --- a/packages/ckeditor5-dev-utils/lib/bundler/getplugins.js +++ b/packages/ckeditor5-dev-utils/lib/bundler/getplugins.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); +import path from 'path'; /** * Transforms specified an array of plugin paths to an object contains plugin names @@ -20,7 +18,7 @@ const path = require( 'path' ); * @param {Array.} pluginPaths * @returns {Object} */ -module.exports = function getPlugins( pluginPaths ) { +export default function getPlugins( pluginPaths ) { const plugins = {}; pluginPaths.forEach( pathToFile => { @@ -36,7 +34,7 @@ module.exports = function getPlugins( pluginPaths ) { } ); return plugins; -}; +} function capitalize( string ) { return string.charAt( 0 ).toUpperCase() + string.slice( 1 ); diff --git a/packages/ckeditor5-dev-utils/lib/bundler/index.js b/packages/ckeditor5-dev-utils/lib/bundler/index.js index f097c1388..f268e2e5d 100644 --- a/packages/ckeditor5-dev-utils/lib/bundler/index.js +++ b/packages/ckeditor5-dev-utils/lib/bundler/index.js @@ -3,9 +3,5 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = { - createEntryFile: require( './createentryfile' ), - getLicenseBanner: require( './getlicensebanner' ) -}; +export { default as createEntryFile } from './createentryfile.js'; +export { default as getLicenseBanner } from './getlicensebanner.js'; diff --git a/packages/ckeditor5-dev-utils/lib/index.js b/packages/ckeditor5-dev-utils/lib/index.js index 12dfc2017..32928d804 100644 --- a/packages/ckeditor5-dev-utils/lib/index.js +++ b/packages/ckeditor5-dev-utils/lib/index.js @@ -3,14 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = { - logger: require( './logger' ), - tools: require( './tools' ), - loaders: require( './loaders' ), - stream: require( './stream' ), - bundler: require( './bundler/index' ), - builds: require( './builds/index' ), - styles: require( './styles/index' ) -}; +export { default as logger } from './logger.js'; +export { default as tools } from './tools.js'; +export { default as loaders } from './loaders/index.js'; +export { default as stream } from './stream.js'; +export { default as bundler } from './bundler/index.js'; +export { default as builds } from './builds/index.js'; +export { default as styles } from './styles/index.js'; diff --git a/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js b/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js index 4e5c91f14..6aace870c 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js @@ -12,7 +12,7 @@ * @param {String} source * @param {any} map */ -module.exports = function ckDebugLoader( source, map ) { +export default function ckDebugLoader( source, map ) { source = source.replace( /\/\/ @if (!?[\w]+) \/\/(.+)/g, ( match, flagName, body ) => { // `this.query` comes from the webpack loader configuration specified as the loader options. // { @@ -32,4 +32,4 @@ module.exports = function ckDebugLoader( source, map ) { } ); this.callback( null, source, map ); -}; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/index.js b/packages/ckeditor5-dev-utils/lib/loaders/index.js index 553189dfb..4ed0335cc 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/index.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/index.js @@ -3,155 +3,150 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' ); -const { getPostCssConfig } = require( '../styles' ); +import path from 'path'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import { getPostCssConfig } from '../styles/index.js'; const escapedPathSep = path.sep == '/' ? '/' : '\\\\'; -module.exports = { - /** - * @param {Object} [options] - * @param {String} [options.configFile] - * @param {Array.} [options.debugFlags] - * @param {Boolean} [options.includeDebugLoader] - * @returns {Object} - */ - getTypeScriptLoader( options = {} ) { - const { - configFile = 'tsconfig.json', - debugFlags = [], - includeDebugLoader = false - } = options; - - return { - test: /\.ts$/, - use: [ - { - loader: 'esbuild-loader', - options: { - target: 'es2022', - tsconfig: configFile - } - }, - includeDebugLoader ? getDebugLoader( debugFlags ) : null - ].filter( Boolean ) - }; - }, - - /** - * @param {Object} options - * @param {Array.} options.debugFlags - * @returns {Object} - */ - getJavaScriptLoader( { debugFlags } ) { - return { - test: /\.js$/, - ...getDebugLoader( debugFlags ) - }; - }, - - /** - * @param {Object} options - * @param {String} options.themePath - * @param {Boolean} [options.minify] - * @param {Boolean} [options.sourceMap] - * @param {Boolean} [options.extractToSeparateFile] - * @param {Boolean} [options.skipPostCssLoader] - * @returns {Object} - */ - getStylesLoader( options ) { - const { - themePath, - minify = false, - sourceMap = false, - extractToSeparateFile = false, - skipPostCssLoader = false - } = options; - - const getBundledLoader = () => ( { - loader: 'style-loader', - options: { - injectType: 'singletonStyleTag', - attributes: { - 'data-cke': true +/** + * @param {Object} [options] + * @param {String} [options.configFile] + * @param {Array.} [options.debugFlags] + * @param {Boolean} [options.includeDebugLoader] + * @returns {Object} + */ +export function getTypeScriptLoader( options = {} ) { + const { + configFile = 'tsconfig.json', + debugFlags = [], + includeDebugLoader = false + } = options; + + return { + test: /\.ts$/, + use: [ + { + loader: 'esbuild-loader', + options: { + target: 'es2022', + tsconfig: configFile } + }, + includeDebugLoader ? getDebugLoader( debugFlags ) : null + ].filter( Boolean ) + }; +} + +/** + * @param {Object} options + * @param {Array.} options.debugFlags + * @returns {Object} + */ +export function getJavaScriptLoader( { debugFlags } ) { + return { + test: /\.js$/, + ...getDebugLoader( debugFlags ) + }; +} + +/** + * @param {Object} options + * @param {String} options.themePath + * @param {Boolean} [options.minify] + * @param {Boolean} [options.sourceMap] + * @param {Boolean} [options.extractToSeparateFile] + * @param {Boolean} [options.skipPostCssLoader] + * @returns {Object} + */ +export function getStylesLoader( options ) { + const { + themePath, + minify = false, + sourceMap = false, + extractToSeparateFile = false, + skipPostCssLoader = false + } = options; + + const getBundledLoader = () => ( { + loader: 'style-loader', + options: { + injectType: 'singletonStyleTag', + attributes: { + 'data-cke': true } - } ); - - const getExtractedLoader = () => { - return MiniCssExtractPlugin.loader; - }; - - return { - test: /\.css$/, - use: [ - extractToSeparateFile ? getExtractedLoader() : getBundledLoader(), - 'css-loader', - skipPostCssLoader ? null : { - loader: 'postcss-loader', - options: { - postcssOptions: getPostCssConfig( { - themeImporter: { themePath }, - minify, - sourceMap - } ) - } - } - ].filter( Boolean ) - }; - }, - - /** - * @param {Object} [options] - * @param {Boolean} [options.matchExtensionOnly] - * @returns {Object} - */ - getIconsLoader( { matchExtensionOnly = false } = {} ) { - return { - test: matchExtensionOnly ? /\.svg$/ : /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/, - use: [ 'raw-loader' ] - }; - }, - - /** - * @returns {Object} - */ - getFormattedTextLoader() { - return { - test: /\.(txt|html|rtf)$/, - use: [ 'raw-loader' ] - }; - }, - - /** - * @param {Object} options] - * @param {Array.} options.files - * @returns {Object} - */ - getCoverageLoader( { files } ) { - return { - test: /\.[jt]s$/, - use: [ - { - loader: 'babel-loader', - options: { - plugins: [ - 'babel-plugin-istanbul' - ] - } + } + } ); + + const getExtractedLoader = () => { + return MiniCssExtractPlugin.loader; + }; + + return { + test: /\.css$/, + use: [ + extractToSeparateFile ? getExtractedLoader() : getBundledLoader(), + 'css-loader', + skipPostCssLoader ? null : { + loader: 'postcss-loader', + options: { + postcssOptions: getPostCssConfig( { + themeImporter: { themePath }, + minify, + sourceMap + } ) } - ], - include: getPathsToIncludeForCoverage( files ), - exclude: [ - new RegExp( `${ escapedPathSep }(lib)${ escapedPathSep }` ) - ] - }; - } -}; + } + ].filter( Boolean ) + }; +} + +/** + * @param {Object} [options] + * @param {Boolean} [options.matchExtensionOnly] + * @returns {Object} + */ +export function getIconsLoader( { matchExtensionOnly = false } = {} ) { + return { + test: matchExtensionOnly ? /\.svg$/ : /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/, + use: [ 'raw-loader' ] + }; +} + +/** + * @returns {Object} + */ +export function getFormattedTextLoader() { + return { + test: /\.(txt|html|rtf)$/, + use: [ 'raw-loader' ] + }; +} +/** + * @param {Object} options] + * @param {Array.} options.files + * @returns {Object} + */ +export function getCoverageLoader( { files } ) { + return { + test: /\.[jt]s$/, + use: [ + { + loader: 'babel-loader', + options: { + plugins: [ + 'babel-plugin-istanbul' + ] + } + } + ], + include: getPathsToIncludeForCoverage( files ), + exclude: [ + new RegExp( `${ escapedPathSep }(lib)${ escapedPathSep }` ) + ] + }; +} /** * @param {Array.} debugFlags * @returns {Object} diff --git a/packages/ckeditor5-dev-utils/lib/logger.js b/packages/ckeditor5-dev-utils/lib/logger.js index 1cdf69a6f..e6a9ddcf5 100644 --- a/packages/ckeditor5-dev-utils/lib/logger.js +++ b/packages/ckeditor5-dev-utils/lib/logger.js @@ -3,9 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import chalk from 'chalk'; -const chalk = require( 'chalk' ); const levels = new Map(); // Displays everything. @@ -27,7 +26,7 @@ levels.set( 'error', new Set( [ 'info', 'warning', 'error' ] ) ); * * Usage: * - * const logger = require( '@ckeditor/ckeditor5-dev-utils' ).logger; + * import { logger } from '@ckeditor/ckeditor5-dev-utils'; * * const infoLog = logger( 'info' ); * infoLog.info( 'Message.' ); // This message will be always displayed. @@ -52,7 +51,7 @@ levels.set( 'error', new Set( [ 'info', 'warning', 'error' ] ) ); * @returns {Function} logger.warning * @returns {Function} logger.error */ -module.exports = ( moduleVerbosity = 'info' ) => { +export default function logger( moduleVerbosity = 'info' ) { return { /** * Displays a message when verbosity level is equal to 'info'. @@ -100,4 +99,4 @@ module.exports = ( moduleVerbosity = 'info' ) => { } } }; -}; +} diff --git a/packages/ckeditor5-dev-utils/lib/stream.js b/packages/ckeditor5-dev-utils/lib/stream.js index cd44129f1..1c81cd8e2 100644 --- a/packages/ckeditor5-dev-utils/lib/stream.js +++ b/packages/ckeditor5-dev-utils/lib/stream.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const PassThrough = require( 'stream' ).PassThrough; -const through = require( 'through2' ); +import path from 'path'; +import { PassThrough } from 'stream'; +import through from 'through2'; const stream = { /** @@ -79,4 +77,4 @@ const stream = { } }; -module.exports = stream; +export default stream; diff --git a/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js b/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js index 136037ca9..1a5bfddd3 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js +++ b/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js @@ -3,10 +3,15 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /* eslint-env node */ +import postCssImport from 'postcss-import'; +import postCssMixins from 'postcss-mixins'; +import postCssNesting from 'postcss-nesting'; +import cssnano from 'cssnano'; +import themeLogger from './themelogger.js'; +import themeImporter from './themeimporter.js'; + /** * Returns a PostCSS configuration to build the editor styles (e.g. used by postcss-loader). * @@ -17,17 +22,17 @@ * See the plugin to learn more. * @returns {Object} A PostCSS configuration object, e.g. to be used by the postcss-loader. */ -module.exports = function getPostCssConfig( options = {} ) { +export default function getPostCssConfig( options = {} ) { const config = { plugins: [ - require( 'postcss-import' )(), - require( './themeimporter' )( options.themeImporter ), - require( 'postcss-mixins' )(), - require( 'postcss-nesting' )( { + postCssImport(), + themeImporter( options.themeImporter ), + postCssMixins(), + postCssNesting( { // https://github.com/ckeditor/ckeditor5/issues/11730 noIsPseudoSelector: true } ), - require( './themelogger' )() + themeLogger() ] }; @@ -36,7 +41,7 @@ module.exports = function getPostCssConfig( options = {} ) { } if ( options.minify ) { - config.plugins.push( require( 'cssnano' )( { + config.plugins.push( cssnano( { preset: 'default', autoprefixer: false, reduceIdents: false @@ -44,4 +49,4 @@ module.exports = function getPostCssConfig( options = {} ) { } return config; -}; +} diff --git a/packages/ckeditor5-dev-utils/lib/styles/index.js b/packages/ckeditor5-dev-utils/lib/styles/index.js index e8f1e70d5..7e624f209 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/index.js +++ b/packages/ckeditor5-dev-utils/lib/styles/index.js @@ -3,10 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = { - getPostCssConfig: require( './getpostcssconfig' ), - themeImporter: require( './themeimporter' ), - themeLogger: require( './themelogger' ) -}; +export { default as getPostCssConfig } from './getpostcssconfig.js'; +export { default as themeImporter } from './themeimporter.js'; +export { default as themeLogger } from './themelogger.js'; diff --git a/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js b/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js index 106ac24ab..9c791f94c 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js +++ b/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js @@ -3,16 +3,18 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /* eslint-env node */ -const fs = require( 'fs' ); -const path = require( 'path' ); -const postcss = require( 'postcss' ); -const chalk = require( 'chalk' ); -const log = require( '../logger' )(); -const getPackageName = require( './utils/getpackagename' ); +import fs from 'fs'; +import path from 'path'; +import postcss from 'postcss'; +import postCssImport from 'postcss-import'; +import chalk from 'chalk'; +import logger from '../logger.js'; +import themeLogger from './themelogger.js'; +import getPackageName from './utils/getpackagename.js'; + +const log = logger(); /** * A PostCSS plugin that loads a theme files from specified path. @@ -38,7 +40,7 @@ const getPackageName = require( './utils/getpackagename' ); * @param {ThemeImporterOptions} pluginOptions * @returns {Function} A PostCSS plugin. */ -module.exports = ( pluginOptions = {} ) => { +function themeImporter( pluginOptions = {} ) { return { postcssPlugin: 'postcss-ckeditor5-theme-importer', Once( root, { result } ) { @@ -47,8 +49,8 @@ module.exports = ( pluginOptions = {} ) => { debug: pluginOptions.debug || false, postCssOptions: { plugins: [ - require( 'postcss-import' )(), - require( './themelogger' )() + postCssImport(), + themeLogger() ] }, root, result @@ -57,9 +59,11 @@ module.exports = ( pluginOptions = {} ) => { return importThemeFile( options ); } }; -}; +} + +themeImporter.postcss = true; -module.exports.postcss = true; +export default themeImporter; /** * Imports a complementary theme file corresponding with a CSS file being processed by diff --git a/packages/ckeditor5-dev-utils/lib/styles/themelogger.js b/packages/ckeditor5-dev-utils/lib/styles/themelogger.js index e9fb67f43..9effaf283 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/themelogger.js +++ b/packages/ckeditor5-dev-utils/lib/styles/themelogger.js @@ -3,21 +3,21 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * A plugin that prepends a path to the file in the comment for each file * processed by PostCSS. * * @returns {Function} A PostCSS plugin. */ -module.exports = () => { +function themeLogger() { return { postcssPlugin: 'postcss-ckeditor5-theme-logger', Once( root ) { root.prepend( `/* ${ root.source.input.file } */ \n` ); } }; -}; +} + +themeLogger.postcss = true; -module.exports.postcss = true; +export default themeLogger; diff --git a/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js b/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js index 1ea16e247..87b9d1c6d 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js +++ b/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js @@ -3,8 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /* eslint-env node */ /** @@ -33,7 +31,7 @@ * @param {String} inputFilePath A path to the file. * @returns {String} The name of the package. */ -module.exports = function getPackageName( inputFilePath ) { +export default function getPackageName( inputFilePath ) { const match = inputFilePath.match( /^.+[/\\](ckeditor5-[^/\\]+)/ ); if ( match ) { @@ -41,4 +39,4 @@ module.exports = function getPackageName( inputFilePath ) { } else { return null; } -}; +} diff --git a/packages/ckeditor5-dev-utils/lib/tools.js b/packages/ckeditor5-dev-utils/lib/tools.js index 3fae17989..f0c934006 100644 --- a/packages/ckeditor5-dev-utils/lib/tools.js +++ b/packages/ckeditor5-dev-utils/lib/tools.js @@ -3,12 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const createSpinner = require( './tools/createspinner' ); - -module.exports = { +import chalk from 'chalk'; +import createSpinner from './tools/createspinner.js'; +import logger from './logger.js'; +import sh from 'shelljs'; +import fs from 'fs'; +import pth from 'path'; + +export default { createSpinner, /** @@ -29,9 +31,7 @@ module.exports = { async = false } = options; - const logger = require( './logger' ); const log = logger( verbosity ); - const sh = require( 'shelljs' ); sh.config.silent = true; @@ -89,9 +89,6 @@ module.exports = { * @returns {Array} */ getDirectories( path ) { - const fs = require( 'fs' ); - const pth = require( 'path' ); - const isDirectory = path => { try { return fs.statSync( path ).isDirectory(); @@ -112,8 +109,6 @@ module.exports = { * modified JSON object to save. */ updateJSONFile( path, updateFunction ) { - const fs = require( 'fs' ); - const contents = fs.readFileSync( path, 'utf-8' ); let json = JSON.parse( contents ); json = updateFunction( json ); diff --git a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js index 726903bd1..b025260e9 100644 --- a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js +++ b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const readline = require( 'readline' ); -const isInteractive = require( 'is-interactive' ); -const cliSpinners = require( 'cli-spinners' ); -const cliCursor = require( 'cli-cursor' ); +import readline from 'readline'; +import isInteractive from 'is-interactive'; +import cliSpinners from 'cli-spinners'; +import cliCursor from 'cli-cursor'; // A size of default indent for a log. const INDENT_SIZE = 3; @@ -29,7 +27,7 @@ const INDENT_SIZE = 3; * this option allows customizing the displayed line. * @returns {CKEditor5Spinner} */ -module.exports = function createSpinner( title, options = {} ) { +export default function createSpinner( title, options = {} ) { const isEnabled = !options.isDisabled && isInteractive(); const indentLevel = options.indentLevel || 0; const indent = ' '.repeat( indentLevel * INDENT_SIZE ); @@ -113,7 +111,7 @@ module.exports = function createSpinner( title, options = {} ) { readline.clearLine( process.stdout, 1 ); readline.cursorTo( process.stdout, 0 ); } -}; +} /** * @typedef {Object} CKEditor5Spinner diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index 8a8ee239c..bf602182b 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -17,6 +17,7 @@ "npm": ">=5.7.1" }, "main": "lib/index.js", + "type": "module", "files": [ "lib" ], @@ -46,14 +47,14 @@ "through2": "^3.0.1" }, "devDependencies": { - "chai": "^4.2.0", - "mockery": "^2.1.0", - "sinon": "^9.2.4", - "vinyl": "^2.1.0" + "jest-extended": "^4.0.2", + "handlebars": "^4.7.6", + "vinyl": "^2.1.0", + "vitest": "^2.0.5" }, "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.js --coverage" }, "depcheckIgnore": [ "css-loader", diff --git a/packages/ckeditor5-dev-utils/tests/_utils/testsetup.js b/packages/ckeditor5-dev-utils/tests/_utils/testsetup.js new file mode 100644 index 000000000..52e540b96 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/_utils/testsetup.js @@ -0,0 +1,9 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { expect } from 'vitest'; +import * as matchers from 'jest-extended'; + +expect.extend( matchers ); diff --git a/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js b/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js index 7408203ba..132eef78b 100644 --- a/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js +++ b/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js @@ -3,19 +3,24 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const chai = require( 'chai' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); -const TerserPlugin = require( 'terser-webpack-plugin' ); -const expect = chai.expect; - -describe( 'builds/getDllPluginWebpackConfig()', () => { - let sandbox, stubs, getDllPluginWebpackConfig; - - const manifest = { +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import getDllPluginWebpackConfig from '../../lib/builds/getdllpluginwebpackconfig.js'; +import { getLicenseBanner } from '../../lib/bundler/index.js'; +import { getIconsLoader, getStylesLoader, getTypeScriptLoader } from '../../lib/loaders/index.js'; + +const stubs = vi.hoisted( () => ( { + CKEditorTranslationsPlugin: { + constructor: vi.fn() + }, + TerserPlugin: { + constructor: vi.fn() + }, + webpack: { + BannerPlugin: vi.fn(), + DllReferencePlugin: vi.fn() + }, + manifest: { content: { '../../node_modules/lodash-es/_DataView.js': { id: '../../node_modules/lodash-es/_DataView.js', @@ -27,54 +32,53 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } } } - }; + } +} ) ); + +vi.mock( '../../lib/loaders/index.js' ); +vi.mock( '../../lib/bundler/index.js' ); +vi.mock( 'fs-extra' ); +vi.mock( 'path', () => ( { + default: { + join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ) + } +} ) ); +vi.mock( '@ckeditor/ckeditor5-dev-translations', () => ( { + CKEditorTranslationsPlugin: class { + constructor( ...args ) { + stubs.CKEditorTranslationsPlugin.constructor( ...args ); + } + } +} ) ); +vi.mock( 'terser-webpack-plugin', () => ( { + default: class TerserPluginMock { + constructor( ...args ) { + stubs.TerserPlugin.constructor( ...args ); + } + } +} ) ); +describe( 'builds/getDllPluginWebpackConfig()', () => { beforeEach( () => { - sandbox = sinon.createSandbox(); - - stubs = { - fs: { - existsSync: sandbox.stub(), - readJsonSync: sandbox.stub() - }, - webpack: { - BannerPlugin: sandbox.stub(), - DllReferencePlugin: sandbox.stub() - }, - loaders: { - getIconsLoader: sinon.stub(), - getStylesLoader: sinon.stub(), - getTypeScriptLoader: sinon.stub() + vi.mocked( fs ).readJsonSync.mockImplementation( input => { + if ( input === '/manifest/path' ) { + return stubs.manifest; } - }; - - stubs.fs.readJsonSync.returns( { - name: '@ckeditor/ckeditor5-dev' - } ); - sandbox.stub( path, 'join' ).callsFake( ( ...args ) => args.join( '/' ) ); + if ( input === '/package/html-embed/package.json' ) { + return { + name: '@ckeditor/ckeditor5-html-embed' + }; + } - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false + return { + name: '@ckeditor/ckeditor5-dev' + }; } ); - - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( '../loaders', stubs.loaders ); - mockery.registerMock( '/manifest/path', manifest ); - - getDllPluginWebpackConfig = require( '../../lib/builds/getdllpluginwebpackconfig' ); - } ); - - afterEach( () => { - mockery.deregisterAll(); - mockery.disable(); - sandbox.restore(); } ); - it( 'returns the webpack configuration in production mode by default', () => { - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + it( 'returns the webpack configuration in production mode by default', async () => { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -95,16 +99,12 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( webpackConfig.optimization.minimizer.length ).to.equal( 1 ); // Due to versions mismatch, the `instanceof` check does not pass. - expect( webpackConfig.optimization.minimizer[ 0 ].constructor.name ).to.equal( TerserPlugin.name ); + expect( webpackConfig.optimization.minimizer[ 0 ].constructor.name ).to.equal( 'TerserPluginMock' ); } ); - it( 'transforms package with many dashes in its name', () => { - stubs.fs.readJsonSync.returns( { - name: '@ckeditor/ckeditor5-html-embed' - } ); - - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { - packagePath: '/package/path', + it( 'transforms package with many dashes in its name', async () => { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { + packagePath: '/package/html-embed', themePath: '/theme/path', manifestPath: '/manifest/path' } ); @@ -114,8 +114,8 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( webpackConfig.output.filename ).to.equal( 'html-embed.js' ); } ); - it( 'does not minify the destination file when in dev mode', () => { - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + it( 'does not minify the destination file when in dev mode', async () => { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path', @@ -127,8 +127,8 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( webpackConfig.optimization.minimizer ).to.be.undefined; } ); - it( 'should not export any library by default', () => { - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + it( 'should not export any library by default', async () => { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -137,10 +137,10 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( webpackConfig.output.libraryExport ).to.be.undefined; } ); - it( 'uses index.ts entry file by default', () => { - stubs.fs.existsSync.callsFake( file => file == '/package/path/src/index.ts' ); + it( 'uses index.ts entry file by default', async () => { + vi.mocked( fs ).existsSync.mockImplementation( file => file == '/package/path/src/index.ts' ) - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -149,10 +149,10 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( webpackConfig.entry ).to.equal( '/package/path/src/index.ts' ); } ); - it( 'uses index.js entry file if exists (over its TS version)', () => { - stubs.fs.existsSync.callsFake( file => file == '/package/path/src/index.js' ); + it( 'uses index.js entry file if exists (over its TS version)', async () => { + vi.mocked( fs ).existsSync.mockImplementation( file => file == '/package/path/src/index.js' ); - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -161,10 +161,10 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( webpackConfig.entry ).to.equal( '/package/path/src/index.js' ); } ); - it( 'loads JavaScript files over TypeScript when building for a JavaScript package', () => { - stubs.fs.existsSync.callsFake( file => file == '/package/path/src/index.js' ); + it( 'loads JavaScript files over TypeScript when building for a JavaScript package', async () => { + vi.mocked( fs ).existsSync.mockImplementation( file => file == '/package/path/src/index.js' ); - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -174,8 +174,8 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } ); describe( '#plugins', () => { - it( 'loads the webpack.DllReferencePlugin plugin', () => { - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + it( 'loads the webpack.DllReferencePlugin plugin', async () => { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -184,16 +184,17 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { const dllReferencePlugin = webpackConfig.plugins.find( plugin => plugin instanceof stubs.webpack.DllReferencePlugin ); expect( dllReferencePlugin ).to.be.an.instanceOf( stubs.webpack.DllReferencePlugin ); - expect( stubs.webpack.DllReferencePlugin.firstCall.args[ 0 ].manifest ).to.deep.equal( manifest ); - expect( stubs.webpack.DllReferencePlugin.firstCall.args[ 0 ].scope ).to.equal( 'ckeditor5/src' ); - expect( stubs.webpack.DllReferencePlugin.firstCall.args[ 0 ].name ).to.equal( 'CKEditor5.dll' ); - expect( stubs.webpack.DllReferencePlugin.firstCall.args[ 0 ].extensions ).to.be.undefined; + expect( stubs.webpack.DllReferencePlugin ).toHaveBeenCalledExactlyOnceWith( { + manifest: stubs.manifest, + scope: 'ckeditor5/src', + name: 'CKEditor5.dll' + } ); } ); - it( 'loads the CKEditorTranslationsPlugin plugin when lang dir exists', () => { + it( 'loads the CKEditorTranslationsPlugin plugin when lang dir exists', async () => { stubs.fs.existsSync.returns( true ); - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -213,10 +214,10 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( 'ckeditor5-basic-styles/src/bold.ts' ).to.not.match( ckeditor5TranslationsPlugin.options.sourceFilesPattern ); } ); - it( 'does not load the CKEditorTranslationsPlugin plugin when lang dir does not exist', () => { + it( 'does not load the CKEditorTranslationsPlugin plugin when lang dir does not exist', async () => { stubs.fs.existsSync.returns( false ); - const webpackConfig = getDllPluginWebpackConfig( stubs.webpack, { + const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' @@ -232,7 +233,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { describe( '#loaders', () => { describe( 'getTypeScriptLoader()', () => { - it( 'it should use the default tsconfig.json if the "options.tsconfigPath" option is not specified', () => { + it( 'it should use the default tsconfig.json if the "options.tsconfigPath" option is not specified', async () => { getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', @@ -245,7 +246,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( options ).to.have.property( 'configFile', 'tsconfig.json' ); } ); - it( 'it should the specified "options.tsconfigPath" value', () => { + it( 'it should the specified "options.tsconfigPath" value', async () => { getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', @@ -261,7 +262,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } ); describe( 'getIconsLoader()', () => { - it( 'it should get the loader', () => { + it( 'it should get the loader', async () => { getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', @@ -276,7 +277,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } ); describe( 'getStylesLoader()', () => { - it( 'it should get the loader', () => { + it( 'it should get the loader', async () => { getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', diff --git a/packages/ckeditor5-dev-utils/tests/builds/index.js b/packages/ckeditor5-dev-utils/tests/builds/index.js index 4d644a0ea..7a5196d17 100644 --- a/packages/ckeditor5-dev-utils/tests/builds/index.js +++ b/packages/ckeditor5-dev-utils/tests/builds/index.js @@ -3,21 +3,17 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import * as tasks from '../../lib/builds/index.js'; +import getDllPluginWebpackConfig from '../../lib/builds/getdllpluginwebpackconfig.js'; -const chai = require( 'chai' ); -const expect = chai.expect; +vi.mock( '../../lib/builds/getdllpluginwebpackconfig.js' ); describe( 'builds', () => { - let tasks; - - beforeEach( () => { - tasks = require( '../../lib/builds/index' ); - } ); - describe( 'getDllPluginWebpackConfig()', () => { it( 'should be a function', () => { expect( tasks.getDllPluginWebpackConfig ).to.be.a( 'function' ); + expect( tasks.getDllPluginWebpackConfig ).toEqual( getDllPluginWebpackConfig ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/vitest.config.js b/packages/ckeditor5-dev-utils/vitest.config.js new file mode 100644 index 000000000..075897d63 --- /dev/null +++ b/packages/ckeditor5-dev-utils/vitest.config.js @@ -0,0 +1,30 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + setupFiles: [ + './tests/_utils/testsetup.js' + ], + testTimeout: 10000, + mockReset: true, + restoreMocks: true, + include: [ + 'tests/**/*.js' + ], + exclude: [ + './tests/_utils/**/*.js' + ], + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From 0ca7640d7b3542fb6ad2a13041584d40c578eeed Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 15:02:51 +0200 Subject: [PATCH 074/172] Release tools: __dirname. --- .../ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js | 4 ++++ .../ckeditor5-dev-release-tools/tests/templates/commit.js | 4 ++++ .../tests/utils/getchangedfilesforcommit.js | 4 ++++ .../ckeditor5-dev-release-tools/tests/utils/getcommits.js | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js index 053008a79..be6cb971b 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js @@ -5,8 +5,12 @@ import fs from 'fs'; import path from 'path'; +import { fileURLToPath } from 'url'; import { getTypeOrder } from './transformcommitutils.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + const templatePath = path.join( __dirname, '..', 'templates' ); /** diff --git a/packages/ckeditor5-dev-release-tools/tests/templates/commit.js b/packages/ckeditor5-dev-release-tools/tests/templates/commit.js index d1535b94f..b02b23bf0 100644 --- a/packages/ckeditor5-dev-release-tools/tests/templates/commit.js +++ b/packages/ckeditor5-dev-release-tools/tests/templates/commit.js @@ -4,10 +4,14 @@ */ import { beforeEach, describe, expect, it } from 'vitest'; +import { fileURLToPath } from 'url'; import fs from 'fs'; import path from 'upath'; import handlebars from 'handlebars'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + const templatePath = path.resolve( __dirname, '..', '..', 'lib', 'templates', 'commit.hbs' ); const templateContent = fs.readFileSync( templatePath, 'utf-8' ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js index 376b8c755..e5d1efd32 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getchangedfilesforcommit.js @@ -4,11 +4,15 @@ */ import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest'; +import { fileURLToPath } from 'url'; import fs from 'fs'; import path from 'path'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; import getChangedFilesForCommit from '../../lib/utils/getchangedfilesforcommit.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + describe( 'getChangedFilesForCommit()', { timeout: 15000 }, function() { let tmpCwd, cwd; diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 9c66af702..641057e37 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -4,11 +4,15 @@ */ import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach } from 'vitest'; +import { fileURLToPath } from 'url'; import fs from 'fs'; import path from 'path'; import gitRawCommits from 'git-raw-commits'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + describe( 'getCommits()', () => { let tmpCwd, cwd, getCommits, stubs; From 59aa6a26f53ff178fe0d9de1ccd5ca8f24ecf7b0 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 15:05:26 +0200 Subject: [PATCH 075/172] Dev docs: __dirname. --- .../tests/validators/fires-validator/index.js | 6 +++++- .../tests/validators/link-validator/index.js | 6 +++++- .../tests/validators/module-validator/index.js | 6 +++++- .../tests/validators/overloads-validator/index.js | 6 +++++- .../tests/validators/see-validator/index.js | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js index ac5b2c688..ef8614dcf 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js @@ -4,10 +4,14 @@ */ import { describe, it, expect, vi } from 'vitest'; +import { fileURLToPath } from 'url'; +import path from 'path'; import testUtils from '../../_utils.js'; - import build from '../../../lib/buildtypedoc.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + const stubs = vi.hoisted( () => { return { onErrorCallback: vi.fn() diff --git a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js index 6f417d1ed..1620653e8 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js @@ -4,10 +4,14 @@ */ import { describe, it, expect, vi } from 'vitest'; +import { fileURLToPath } from 'url'; +import path from 'path'; import testUtils from '../../_utils.js'; - import build from '../../../lib/buildtypedoc.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + const stubs = vi.hoisted( () => { return { onErrorCallback: vi.fn() diff --git a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js index d1a7a9d71..1d17bfd81 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js @@ -4,10 +4,14 @@ */ import { describe, it, expect, vi } from 'vitest'; +import { fileURLToPath } from 'url'; +import path from 'path'; import testUtils from '../../_utils.js'; - import build from '../../../lib/buildtypedoc.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + const stubs = vi.hoisted( () => { return { onErrorCallback: vi.fn() diff --git a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js index 3bd540603..52b846e40 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js @@ -4,10 +4,14 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { fileURLToPath } from 'url'; +import path from 'path'; import testUtils from '../../_utils.js'; - import build from '../../../lib/buildtypedoc.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + const stubs = vi.hoisted( () => { return { onErrorCallback: vi.fn() diff --git a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js index 18f0ca300..4787c959f 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js @@ -4,10 +4,14 @@ */ import { describe, it, expect, vi } from 'vitest'; +import { fileURLToPath } from 'url'; +import path from 'path'; import testUtils from '../../_utils.js'; - import build from '../../../lib/buildtypedoc.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + const stubs = vi.hoisted( () => { return { onErrorCallback: vi.fn() From bf1346d3dd2d85f9a89e375fdf6db8feda5af7d3 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 15:07:20 +0200 Subject: [PATCH 076/172] Dev stale-bot: __dirname. --- packages/ckeditor5-dev-stale-bot/lib/githubrepository.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js index 189373de4..0f89d0654 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js +++ b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js @@ -5,6 +5,7 @@ import upath from 'upath'; import fs from 'fs-extra'; +import { fileURLToPath } from 'url'; import { GraphQLClient } from 'graphql-request'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; import { @@ -20,6 +21,9 @@ import isIssueOrPullRequestToClose from './utils/isissueorpullrequesttoclose.js' import isPendingIssueToStale from './utils/ispendingissuetostale.js'; import isPendingIssueToUnlabel from './utils/ispendingissuetounlabel.js'; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = upath.dirname( __filename ); + const GRAPHQL_PATH = upath.join( __dirname, 'graphql' ); /** From f90ec3d2ed655ea1d278206d42ac883e84805bd7 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 15:08:21 +0200 Subject: [PATCH 077/172] Dev translations: ESM. --- .../ckeditor5-dev-translations/lib/index.js | 18 +++++------------- .../lib/servetranslations.js | 12 +++++++++--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/ckeditor5-dev-translations/lib/index.js b/packages/ckeditor5-dev-translations/lib/index.js index 20e0293da..db5991323 100644 --- a/packages/ckeditor5-dev-translations/lib/index.js +++ b/packages/ckeditor5-dev-translations/lib/index.js @@ -3,16 +3,8 @@ * For licensing, see LICENSE.md. */ -import findMessages from './findmessages.js'; -import cleanPoFileContent from './cleanpofilecontent.js'; -import MultipleLanguageTranslationService from './multiplelanguagetranslationservice.js'; -import createDictionaryFromPoFileContent from './createdictionaryfrompofilecontent.js'; -import CKEditorTranslationsPlugin from './ckeditortranslationsplugin.js'; - -export default { - findMessages, - cleanPoFileContent, - MultipleLanguageTranslationService, - createDictionaryFromPoFileContent, - CKEditorTranslationsPlugin -}; +export { default as findMessages } from './findmessages.js'; +export { default as cleanPoFileContent } from './cleanpofilecontent.js'; +export { default as MultipleLanguageTranslationService } from './multiplelanguagetranslationservice.js'; +export { default as createDictionaryFromPoFileContent } from './createdictionaryfrompofilecontent.js'; +export { default as CKEditorTranslationsPlugin } from './ckeditortranslationsplugin.js'; diff --git a/packages/ckeditor5-dev-translations/lib/servetranslations.js b/packages/ckeditor5-dev-translations/lib/servetranslations.js index 515780292..d6334a0a2 100644 --- a/packages/ckeditor5-dev-translations/lib/servetranslations.js +++ b/packages/ckeditor5-dev-translations/lib/servetranslations.js @@ -3,11 +3,17 @@ * For licensing, see LICENSE.md. */ -import chalk from 'chalk'; -import rimraf from 'rimraf'; import fs from 'fs'; import path from 'path'; -import { RawSource, ConcatSource } from 'webpack-sources'; +import { fileURLToPath } from 'url'; +import chalk from 'chalk'; +import rimraf from 'rimraf'; +import webpackSources from 'webpack-sources'; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + +const { RawSource, ConcatSource } = webpackSources; /** * Serve translations depending on the used translation service and passed options. From 514c615e9c12078dad105b8fd097ee04c82cfe83 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 15:09:55 +0200 Subject: [PATCH 078/172] Removed unused files. --- .../lib/bundler/createentryfile.js | 54 ------------- .../lib/bundler/geteditorconfig.js | 22 ------ .../lib/bundler/getplugins.js | 41 ---------- .../ckeditor5-dev-utils/lib/bundler/index.js | 1 - .../ckeditor5-dev-utils/lib/styles/index.js | 1 - .../tests/bundler/createentryfile.js | 79 ------------------- .../tests/bundler/geteditorconfig.js | 53 ------------- .../tests/bundler/getplugins.js | 41 ---------- 8 files changed, 292 deletions(-) delete mode 100644 packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js delete mode 100644 packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js delete mode 100644 packages/ckeditor5-dev-utils/lib/bundler/getplugins.js delete mode 100644 packages/ckeditor5-dev-utils/tests/bundler/createentryfile.js delete mode 100644 packages/ckeditor5-dev-utils/tests/bundler/geteditorconfig.js delete mode 100644 packages/ckeditor5-dev-utils/tests/bundler/getplugins.js diff --git a/packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js b/packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js deleted file mode 100644 index 77cbae473..000000000 --- a/packages/ckeditor5-dev-utils/lib/bundler/createentryfile.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import fs from 'fs'; -import getPlugins from './getplugins.js'; -import getEditorConfig from './geteditorconfig.js'; - -/** - * Generates an entry file which can be compiled by bundler, e.g. Webpack or Rollup. - * - * @param {String} destinationPath A path where entry file will be saved. - * @param {Object} options - * @param {Array.} options.plugins An array with paths to the plugins for the editor. - * @param {String} options.moduleName Name of exported UMD module. - * @param {String} options.editor A path to class which defined the editor. - * @param {Object} options.config Additional editor's configuration which will be built-in. - */ -export default function createEntryFile( destinationPath, options ) { - const entryFileContent = renderEntryFile( options ); - - fs.writeFileSync( destinationPath, entryFileContent ); -} - -function renderEntryFile( options ) { - const plugins = getPlugins( options.plugins ); - const date = new Date(); - - let content = `/** - * @license Copyright (c) 2003-${ date.getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import ${ options.moduleName }Base from '${ options.editor }'; -`; - - for ( const pluginName of Object.keys( plugins ) ) { - content += `import ${ pluginName } from '${ plugins[ pluginName ] }';\n`; - } - - content += ` -export default class ${ options.moduleName } extends ${ options.moduleName }Base {} - -${ options.moduleName }.build = { - plugins: [ - ${ Object.keys( plugins ).join( ',\n\t\t' ) } - ], - config: ${ getEditorConfig( options.config ) } -} -`; - - return content; -} diff --git a/packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js b/packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js deleted file mode 100644 index e3eb9f260..000000000 --- a/packages/ckeditor5-dev-utils/lib/bundler/geteditorconfig.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import javascriptStringify from 'javascript-stringify'; - -/** - * Transforms specified configuration to a string that match to our code style. - * - * @param {Object} config - * @returns {String} - */ -export default function getEditorConfig( config ) { - if ( !config ) { - return '{}'; - } - - return javascriptStringify( config, null, '\t' ) - // Indent all but the first line (so it can be easily concatenated with `config = ${ editorConfig }`). - .replace( /\n/g, '\n\t' ); -} diff --git a/packages/ckeditor5-dev-utils/lib/bundler/getplugins.js b/packages/ckeditor5-dev-utils/lib/bundler/getplugins.js deleted file mode 100644 index f60d54add..000000000 --- a/packages/ckeditor5-dev-utils/lib/bundler/getplugins.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import path from 'path'; - -/** - * Transforms specified an array of plugin paths to an object contains plugin names - * and paths to the plugins. - * - * Names of plugins returned in the object will be always unique. - * - * In case of two ore more plugins will have the same name: - * - typing => TypingPlugin - * - path/to/other/plugin/typing => Typing1Plugin - * - * @param {Array.} pluginPaths - * @returns {Object} - */ -export default function getPlugins( pluginPaths ) { - const plugins = {}; - - pluginPaths.forEach( pathToFile => { - const basePluginName = capitalize( path.basename( pathToFile, '.js' ) ); - let pluginName = basePluginName + 'Plugin'; - let i = 0; - - while ( pluginName in plugins ) { - pluginName = basePluginName + ( ++i ).toString() + 'Plugin'; - } - - plugins[ pluginName ] = pathToFile; - } ); - - return plugins; -} - -function capitalize( string ) { - return string.charAt( 0 ).toUpperCase() + string.slice( 1 ); -} diff --git a/packages/ckeditor5-dev-utils/lib/bundler/index.js b/packages/ckeditor5-dev-utils/lib/bundler/index.js index f268e2e5d..df7bf8b38 100644 --- a/packages/ckeditor5-dev-utils/lib/bundler/index.js +++ b/packages/ckeditor5-dev-utils/lib/bundler/index.js @@ -3,5 +3,4 @@ * For licensing, see LICENSE.md. */ -export { default as createEntryFile } from './createentryfile.js'; export { default as getLicenseBanner } from './getlicensebanner.js'; diff --git a/packages/ckeditor5-dev-utils/lib/styles/index.js b/packages/ckeditor5-dev-utils/lib/styles/index.js index 7e624f209..05c612216 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/index.js +++ b/packages/ckeditor5-dev-utils/lib/styles/index.js @@ -5,4 +5,3 @@ export { default as getPostCssConfig } from './getpostcssconfig.js'; export { default as themeImporter } from './themeimporter.js'; -export { default as themeLogger } from './themelogger.js'; diff --git a/packages/ckeditor5-dev-utils/tests/bundler/createentryfile.js b/packages/ckeditor5-dev-utils/tests/bundler/createentryfile.js deleted file mode 100644 index 5ca2e12a0..000000000 --- a/packages/ckeditor5-dev-utils/tests/bundler/createentryfile.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const fs = require( 'fs' ); -const chai = require( 'chai' ); -const expect = chai.expect; -const sinon = require( 'sinon' ); - -describe( 'bundler', () => { - let createEntryFile, sandbox; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - createEntryFile = require( '../../lib/bundler/createentryfile' ); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - - describe( 'createEntryFile()', () => { - it( 'should create an entry file', () => { - const writeFileSyncStub = sandbox.stub( fs, 'writeFileSync' ); - - createEntryFile( 'destination/path/file.js', { - plugins: [ - '@ckeditor/ckeditor5-basic-styles/src/bold', - '@ckeditor/ckeditor5-clipboard/src/clipboard' - ], - moduleName: 'ClassicEditor', - editor: '@ckeditor/ckeditor5-editor-classic/src/editor', - config: { - undo: { - step: 3 - }, - toolbar: [ - 'image' - ] - } - } ); - - const expectedEntryFile = `/** - * @license Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/editor'; -import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold'; -import ClipboardPlugin from '@ckeditor/ckeditor5-clipboard/src/clipboard'; - -export default class ClassicEditor extends ClassicEditorBase {} - -ClassicEditor.build = { - plugins: [ - BoldPlugin, - ClipboardPlugin - ], - config: { - undo: { - step: 3 - }, - toolbar: [ - 'image' - ] - } -}; -`; - - expect( writeFileSyncStub.calledOnce ).to.equal( true ); - expect( writeFileSyncStub.firstCall.args[ 0 ] ).to.equal( 'destination/path/file.js' ); - expect( writeFileSyncStub.firstCall.args[ 1 ] ).to.equal( expectedEntryFile ); - } ); - } ); -} ); diff --git a/packages/ckeditor5-dev-utils/tests/bundler/geteditorconfig.js b/packages/ckeditor5-dev-utils/tests/bundler/geteditorconfig.js deleted file mode 100644 index 7602cac52..000000000 --- a/packages/ckeditor5-dev-utils/tests/bundler/geteditorconfig.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; - -describe( 'bundler', () => { - let getEditorConfig; - - beforeEach( () => { - getEditorConfig = require( '../../lib/bundler/geteditorconfig' ); - } ); - - describe( 'getEditorConfig()', () => { - it( 'returns empty object as string if config was not specified', () => { - expect( getEditorConfig() ).to.equal( '{}' ); - } ); - - it( 'returns given object as string with proper indents', () => { - const config = { - firstKey: 1, - secondKey: [ 1, 2, 3 ], - plugin: { - enabled: true, - key: 'PRIVATE_KEY' - }, - 'key with spaces': null, - anotherKey: 'Key with "quotation marks".' - }; - - const expectedConfig = `{ - firstKey: 1, - secondKey: [ - 1, - 2, - 3 - ], - plugin: { - enabled: true, - key: 'PRIVATE_KEY' - }, - 'key with spaces': null, - anotherKey: 'Key with "quotation marks".' - }`; - - expect( getEditorConfig( config ) ).to.equal( expectedConfig ); - } ); - } ); -} ); diff --git a/packages/ckeditor5-dev-utils/tests/bundler/getplugins.js b/packages/ckeditor5-dev-utils/tests/bundler/getplugins.js deleted file mode 100644 index 82822f16b..000000000 --- a/packages/ckeditor5-dev-utils/tests/bundler/getplugins.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; - -describe( 'bundler', () => { - let getPlugins; - - beforeEach( () => { - getPlugins = require( '../../lib/bundler/getplugins' ); - } ); - - describe( 'getPlugins()', () => { - it( 'returns plugin names and paths', () => { - const plugins = getPlugins( [ - '@ckeditor/ckeditor5-essentials/src/essentials', - '@ckeditor/ckeditor5-basic-styles/src/bold', - '@ckeditor/ckeditor5-basic-styles/src/italic' - ] ); - - expect( plugins ).to.have.property( 'EssentialsPlugin', '@ckeditor/ckeditor5-essentials/src/essentials' ); - expect( plugins ).to.have.property( 'BoldPlugin', '@ckeditor/ckeditor5-basic-styles/src/bold' ); - expect( plugins ).to.have.property( 'ItalicPlugin', '@ckeditor/ckeditor5-basic-styles/src/italic' ); - } ); - - it( 'does not duplicate plugins with the same name', () => { - const plugins = getPlugins( [ - '@ckeditor/ckeditor5-essentials/src/essentials', - 'ckeditor5-foo/src/essentials' - ] ); - - expect( plugins ).to.have.property( 'EssentialsPlugin', '@ckeditor/ckeditor5-essentials/src/essentials' ); - expect( plugins ).to.have.property( 'Essentials1Plugin', 'ckeditor5-foo/src/essentials' ); - } ); - } ); -} ); From 5806de055d8710b3228511959423d951c581fb11 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 15:11:23 +0200 Subject: [PATCH 079/172] Extracted loaders to seperate modules. --- .../lib/loaders/getcoverageloader.js | 69 ++++ .../lib/loaders/getdebugloader.js | 21 + .../lib/loaders/getformattedtextloader.js | 14 + .../lib/loaders/geticonsloader.js | 16 + .../lib/loaders/getjavascriptloader.js | 18 + .../lib/loaders/getstylesloader.js | 58 +++ .../lib/loaders/gettypescriptloader.js | 35 ++ .../ckeditor5-dev-utils/lib/loaders/index.js | 197 +--------- .../tests/loaders/getcoverageloader.js | 82 ++++ .../tests/loaders/getdebugloader.js | 16 + .../tests/loaders/getformattedtextloader.js | 33 ++ .../tests/loaders/geticonsloader.js | 41 ++ .../tests/loaders/getjavascriptloader.js | 33 ++ .../tests/loaders/getstylesloader.js | 88 +++++ .../tests/loaders/gettypescriptloader.js | 80 ++++ .../tests/loaders/index.js | 360 ++---------------- 16 files changed, 648 insertions(+), 513 deletions(-) create mode 100644 packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js create mode 100644 packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js create mode 100644 packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js create mode 100644 packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js create mode 100644 packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js create mode 100644 packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js create mode 100644 packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js create mode 100644 packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js create mode 100644 packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js create mode 100644 packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js create mode 100644 packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js create mode 100644 packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js create mode 100644 packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js create mode 100644 packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js new file mode 100644 index 000000000..f42b2b1ec --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js @@ -0,0 +1,69 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import path from 'path'; + +const escapedPathSep = path.sep == '/' ? '/' : '\\\\'; + +/** + * @param {Object} options] + * @param {Array.} options.files + * @returns {Object} + */ +export default function getCoverageLoader( { files } ) { + return { + test: /\.[jt]s$/, + use: [ + { + loader: 'babel-loader', + options: { + plugins: [ + 'babel-plugin-istanbul' + ] + } + } + ], + include: getPathsToIncludeForCoverage( files ), + exclude: [ + new RegExp( `${ escapedPathSep }(lib)${ escapedPathSep }` ) + ] + }; +} + +/** + * Returns an array of `/ckeditor5-name\/src\//` regexps based on passed globs. + * E.g., `ckeditor5-utils/**\/*.js` will be converted to `/ckeditor5-utils\/src/`. + * + * This loose way of matching packages for CC works with packages under various paths. + * E.g., `workspace/ckeditor5-utils` and `ckeditor5/node_modules/ckeditor5-utils` and every other path. + * + * @param {Array.} globs + * @returns {Array.} + */ +function getPathsToIncludeForCoverage( globs ) { + const values = globs + .reduce( ( returnedPatterns, globPatterns ) => { + returnedPatterns.push( ...globPatterns ); + + return returnedPatterns; + }, [] ) + .map( glob => { + const matchCKEditor5 = glob.match( /\/(ckeditor5-[^/]+)\/(?!.*ckeditor5-)/ ); + + if ( matchCKEditor5 ) { + const packageName = matchCKEditor5[ 1 ] + // A special case when --files='!engine' or --files='!engine|ui' was passed. + // Convert it to /ckeditor5-(?!engine)[^/]\/src\//. + .replace( /ckeditor5-!\(([^)]+)\)\*/, 'ckeditor5-(?!$1)[^' + escapedPathSep + ']+' ) + .replace( 'ckeditor5-*', 'ckeditor5-[a-z]+' ); + + return new RegExp( packageName + escapedPathSep + 'src' + escapedPathSep ); + } + } ) + // Filter undefined ones. + .filter( path => path ); + + return [ ...new Set( values ) ]; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js new file mode 100644 index 000000000..d60b66081 --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js @@ -0,0 +1,21 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); + +/** + * @param {Array.} debugFlags + * @returns {Object} + */ +export default function getDebugLoader( debugFlags ) { + return { + loader: path.join( __dirname, 'ck-debug-loader.js' ), + options: { debugFlags } + }; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js new file mode 100644 index 000000000..253e0734e --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js @@ -0,0 +1,14 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @returns {Object} + */ +export default function getFormattedTextLoader() { + return { + test: /\.(txt|html|rtf)$/, + use: [ 'raw-loader' ] + }; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js b/packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js new file mode 100644 index 000000000..bf3e08875 --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js @@ -0,0 +1,16 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @param {Object} [options] + * @param {Boolean} [options.matchExtensionOnly] + * @returns {Object} + */ +export default function getIconsLoader( { matchExtensionOnly = false } = {} ) { + return { + test: matchExtensionOnly ? /\.svg$/ : /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/, + use: [ 'raw-loader' ] + }; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js new file mode 100644 index 000000000..fc36258c8 --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js @@ -0,0 +1,18 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import getDebugLoader from './getdebugloader.js'; + +/** + * @param {Object} options + * @param {Array.} options.debugFlags + * @returns {Object} + */ +export default function getJavaScriptLoader( { debugFlags } ) { + return { + test: /\.js$/, + ...getDebugLoader( debugFlags ) + }; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js new file mode 100644 index 000000000..5f44b2d75 --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js @@ -0,0 +1,58 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import { getPostCssConfig } from '../styles/index.js'; + +/** + * @param {Object} options + * @param {String} options.themePath + * @param {Boolean} [options.minify] + * @param {Boolean} [options.sourceMap] + * @param {Boolean} [options.extractToSeparateFile] + * @param {Boolean} [options.skipPostCssLoader] + * @returns {Object} + */ +export default function getStylesLoader( options ) { + const { + themePath, + minify = false, + sourceMap = false, + extractToSeparateFile = false, + skipPostCssLoader = false + } = options; + + const getBundledLoader = () => ( { + loader: 'style-loader', + options: { + injectType: 'singletonStyleTag', + attributes: { + 'data-cke': true + } + } + } ); + + const getExtractedLoader = () => { + return MiniCssExtractPlugin.loader; + }; + + return { + test: /\.css$/, + use: [ + extractToSeparateFile ? getExtractedLoader() : getBundledLoader(), + 'css-loader', + skipPostCssLoader ? null : { + loader: 'postcss-loader', + options: { + postcssOptions: getPostCssConfig( { + themeImporter: { themePath }, + minify, + sourceMap + } ) + } + } + ].filter( Boolean ) + }; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js b/packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js new file mode 100644 index 000000000..7e571dd88 --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js @@ -0,0 +1,35 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import getDebugLoader from './getdebugloader.js'; + +/** + * @param {Object} [options] + * @param {String} [options.configFile] + * @param {Array.} [options.debugFlags] + * @param {Boolean} [options.includeDebugLoader] + * @returns {Object} + */ +export default function getTypeScriptLoader( options = {} ) { + const { + configFile = 'tsconfig.json', + debugFlags = [], + includeDebugLoader = false + } = options; + + return { + test: /\.ts$/, + use: [ + { + loader: 'esbuild-loader', + options: { + target: 'es2022', + tsconfig: configFile + } + }, + includeDebugLoader ? getDebugLoader( debugFlags ) : null + ].filter( Boolean ) + }; +} diff --git a/packages/ckeditor5-dev-utils/lib/loaders/index.js b/packages/ckeditor5-dev-utils/lib/loaders/index.js index 4ed0335cc..507823e7b 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/index.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/index.js @@ -3,193 +3,10 @@ * For licensing, see LICENSE.md. */ -import path from 'path'; -import MiniCssExtractPlugin from 'mini-css-extract-plugin'; -import { getPostCssConfig } from '../styles/index.js'; - -const escapedPathSep = path.sep == '/' ? '/' : '\\\\'; - -/** - * @param {Object} [options] - * @param {String} [options.configFile] - * @param {Array.} [options.debugFlags] - * @param {Boolean} [options.includeDebugLoader] - * @returns {Object} - */ -export function getTypeScriptLoader( options = {} ) { - const { - configFile = 'tsconfig.json', - debugFlags = [], - includeDebugLoader = false - } = options; - - return { - test: /\.ts$/, - use: [ - { - loader: 'esbuild-loader', - options: { - target: 'es2022', - tsconfig: configFile - } - }, - includeDebugLoader ? getDebugLoader( debugFlags ) : null - ].filter( Boolean ) - }; -} - -/** - * @param {Object} options - * @param {Array.} options.debugFlags - * @returns {Object} - */ -export function getJavaScriptLoader( { debugFlags } ) { - return { - test: /\.js$/, - ...getDebugLoader( debugFlags ) - }; -} - -/** - * @param {Object} options - * @param {String} options.themePath - * @param {Boolean} [options.minify] - * @param {Boolean} [options.sourceMap] - * @param {Boolean} [options.extractToSeparateFile] - * @param {Boolean} [options.skipPostCssLoader] - * @returns {Object} - */ -export function getStylesLoader( options ) { - const { - themePath, - minify = false, - sourceMap = false, - extractToSeparateFile = false, - skipPostCssLoader = false - } = options; - - const getBundledLoader = () => ( { - loader: 'style-loader', - options: { - injectType: 'singletonStyleTag', - attributes: { - 'data-cke': true - } - } - } ); - - const getExtractedLoader = () => { - return MiniCssExtractPlugin.loader; - }; - - return { - test: /\.css$/, - use: [ - extractToSeparateFile ? getExtractedLoader() : getBundledLoader(), - 'css-loader', - skipPostCssLoader ? null : { - loader: 'postcss-loader', - options: { - postcssOptions: getPostCssConfig( { - themeImporter: { themePath }, - minify, - sourceMap - } ) - } - } - ].filter( Boolean ) - }; -} - -/** - * @param {Object} [options] - * @param {Boolean} [options.matchExtensionOnly] - * @returns {Object} - */ -export function getIconsLoader( { matchExtensionOnly = false } = {} ) { - return { - test: matchExtensionOnly ? /\.svg$/ : /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/, - use: [ 'raw-loader' ] - }; -} - -/** - * @returns {Object} - */ -export function getFormattedTextLoader() { - return { - test: /\.(txt|html|rtf)$/, - use: [ 'raw-loader' ] - }; -} - -/** - * @param {Object} options] - * @param {Array.} options.files - * @returns {Object} - */ -export function getCoverageLoader( { files } ) { - return { - test: /\.[jt]s$/, - use: [ - { - loader: 'babel-loader', - options: { - plugins: [ - 'babel-plugin-istanbul' - ] - } - } - ], - include: getPathsToIncludeForCoverage( files ), - exclude: [ - new RegExp( `${ escapedPathSep }(lib)${ escapedPathSep }` ) - ] - }; -} -/** - * @param {Array.} debugFlags - * @returns {Object} - */ -function getDebugLoader( debugFlags ) { - return { - loader: path.join( __dirname, 'ck-debug-loader' ), - options: { debugFlags } - }; -} - -/** - * Returns an array of `/ckeditor5-name\/src\//` regexps based on passed globs. - * E.g., `ckeditor5-utils/**\/*.js` will be converted to `/ckeditor5-utils\/src/`. - * - * This loose way of matching packages for CC works with packages under various paths. - * E.g., `workspace/ckeditor5-utils` and `ckeditor5/node_modules/ckeditor5-utils` and every other path. - * - * @param {Array.} globs - * @returns {Array.} - */ -function getPathsToIncludeForCoverage( globs ) { - const values = globs - .reduce( ( returnedPatterns, globPatterns ) => { - returnedPatterns.push( ...globPatterns ); - - return returnedPatterns; - }, [] ) - .map( glob => { - const matchCKEditor5 = glob.match( /\/(ckeditor5-[^/]+)\/(?!.*ckeditor5-)/ ); - - if ( matchCKEditor5 ) { - const packageName = matchCKEditor5[ 1 ] - // A special case when --files='!engine' or --files='!engine|ui' was passed. - // Convert it to /ckeditor5-(?!engine)[^/]\/src\//. - .replace( /ckeditor5-!\(([^)]+)\)\*/, 'ckeditor5-(?!$1)[^' + escapedPathSep + ']+' ) - .replace( 'ckeditor5-*', 'ckeditor5-[a-z]+' ); - - return new RegExp( packageName + escapedPathSep + 'src' + escapedPathSep ); - } - } ) - // Filter undefined ones. - .filter( path => path ); - - return [ ...new Set( values ) ]; -} +export { default as getCoverageLoader } from './getcoverageloader.js'; +export { default as getTypeScriptLoader } from './gettypescriptloader.js'; +export { default as getDebugLoader } from './getdebugloader.js'; +export { default as getIconsLoader } from './geticonsloader.js'; +export { default as getFormattedTextLoader } from './getformattedtextloader.js'; +export { default as getJavaScriptLoader } from './getjavascriptloader.js'; +export { default as getStylesLoader } from './getstylesloader.js'; diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js new file mode 100644 index 000000000..e72d2d3c7 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js @@ -0,0 +1,82 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; + + +describe( 'getCoverageLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getCoverageLoader ).to.be.a( 'function' ); + } ); + + it( 'should return a definition containing a loader for measuring the coverage', () => { + const coverageLoader = loaders.getCoverageLoader( { + files: [] + } ); + + expect( coverageLoader ).to.be.an( 'object' ); + expect( '/path/to/javascript.js' ).to.match( coverageLoader.test ); + expect( '/path/to/typescript.ts' ).to.match( coverageLoader.test ); + + expect( coverageLoader.include ).to.be.an( 'array' ); + expect( coverageLoader.include ).to.lengthOf( 0 ); + expect( coverageLoader.exclude ).to.be.an( 'array' ); + expect( coverageLoader.exclude ).to.lengthOf( 1 ); + + expect( coverageLoader.use ).to.be.an( 'array' ); + expect( coverageLoader.use ).to.lengthOf( 1 ); + + const babelLoader = coverageLoader.use[ 0 ]; + + expect( babelLoader.loader ).to.equal( 'babel-loader' ); + } ); + + it( 'should return a definition containing a loader for measuring the coverage (include glob check)', () => { + const coverageLoader = loaders.getCoverageLoader( { + files: [ + // -f utils + [ 'node_modules/ckeditor5-utils/tests/**/*.js' ] + ] + } ); + + expect( coverageLoader ).to.be.an( 'object' ); + expect( coverageLoader ).to.have.property( 'include' ); + expect( coverageLoader.include ).to.be.an( 'array' ); + expect( coverageLoader.include ).to.deep.equal( [ + new RegExp( [ 'ckeditor5-utils', 'src', '' ].join( escapedPathSep ) ) + ] ); + } ); + + it( 'should return a definition containing a loader for measuring the coverage (exclude glob check)', () => { + const coverageLoader = loaders.getCoverageLoader( { + files: [ + // -f !utils + [ 'node_modules/ckeditor5-!(utils)/tests/**/*.js' ] + ] + } ); + + expect( coverageLoader ).to.be.an( 'object' ); + expect( coverageLoader ).to.have.property( 'include' ); + expect( coverageLoader.include ).to.be.an( 'array' ); + expect( coverageLoader.include ).to.deep.equal( [ + new RegExp( [ 'ckeditor5-!(utils)', 'src', '' ].join( escapedPathSep ) ) + ] ); + } ); + + it( 'should return a definition containing a loader for measuring the coverage (for root named ckeditor5-*)', () => { + const coverageLoader = loaders.getCoverageLoader( { + files: [ + [ '/ckeditor5-collab/packages/ckeditor5-alignment/tests/**/*.{js,ts}' ] + ] + } ); + + expect( coverageLoader ).to.be.an( 'object' ); + expect( coverageLoader ).to.have.property( 'include' ); + expect( coverageLoader.include ).to.be.an( 'array' ); + expect( coverageLoader.include ).to.deep.equal( [ + new RegExp( [ 'ckeditor5-alignment', 'src', '' ].join( escapedPathSep ) ) + ] ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js new file mode 100644 index 000000000..215111f67 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js @@ -0,0 +1,16 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; + +describe( 'getCoverageLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getCoverageLoader ).to.be.a( 'function' ); + } ); + + it( 'should return a definition containing a loader for measuring the coverage', () => { + + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js new file mode 100644 index 000000000..60888435b --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js @@ -0,0 +1,33 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; + + +describe( 'getFormattedTextLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getFormattedTextLoader ).to.be.a( 'function' ); + } ); + + it( 'should return a definition accepting files that store readable content', () => { + const textLoader = loaders.getFormattedTextLoader(); + + expect( textLoader ).to.be.an( 'object' ); + expect( textLoader ).to.have.property( 'use' ); + expect( textLoader.use ).to.include( 'raw-loader' ); + expect( textLoader ).to.have.property( 'test' ); + + const loaderRegExp = textLoader.test; + + expect( 'C:\\Program Files\\ckeditor\\italic.html' ).to.match( loaderRegExp, 'HTML: Windows' ); + expect( '/home/ckeditor/italic.html' ).to.match( loaderRegExp, 'HTML: Linux' ); + + expect( 'C:\\Program Files\\ckeditor\\italic.txt' ).to.match( loaderRegExp, 'TXT: Windows' ); + expect( '/home/ckeditor/italic.txt' ).to.match( loaderRegExp, 'TXT: Linux' ); + + expect( 'C:\\Program Files\\ckeditor\\italic.rtf' ).to.match( loaderRegExp, 'RTF: Windows' ); + expect( '/home/ckeditor/italic.rtf' ).to.match( loaderRegExp, 'RTF: Linux' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js b/packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js new file mode 100644 index 000000000..782c78b82 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js @@ -0,0 +1,41 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; + + +describe( 'getIconsLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getIconsLoader ).to.be.a( 'function' ); + } ); + + it( 'should return a definition loading the svg files properly (a full CKEditor 5 icon path check)', () => { + const svgLoader = loaders.getIconsLoader(); + + expect( svgLoader ).to.be.an( 'object' ); + expect( svgLoader ).to.have.property( 'use' ); + expect( svgLoader.use ).to.include( 'raw-loader' ); + expect( svgLoader ).to.have.property( 'test' ); + + const svgRegExp = svgLoader.test; + + expect( 'C:\\Program Files\\ckeditor\\ckeditor5-basic-styles\\theme\\icons\\italic.svg' ).to.match( svgRegExp, 'Windows' ); + expect( '/home/ckeditor/ckeditor5-basic-styles/theme/icons/italic.svg' ).to.match( svgRegExp, 'Linux' ); + } ); + + it( 'should return a definition loading the svg files properly (accept any svg file)', () => { + const svgLoader = loaders.getIconsLoader( { matchExtensionOnly: true } ); + + expect( svgLoader ).to.be.an( 'object' ); + expect( svgLoader ).to.have.property( 'use' ); + expect( svgLoader.use ).to.include( 'raw-loader' ); + expect( svgLoader ).to.have.property( 'test' ); + + const svgRegExp = svgLoader.test; + + expect( 'C:\\Program Files\\ckeditor\\italic.svg' ).to.match( svgRegExp, 'Windows' ); + expect( '/home/ckeditor/italic.svg' ).to.match( svgRegExp, 'Linux' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js new file mode 100644 index 000000000..50154179b --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js @@ -0,0 +1,33 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; + + +describe( 'getJavaScriptLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getJavaScriptLoader ).to.be.a( 'function' ); + } ); + + it( 'should return a definition that enables the ck-debug-loader', () => { + const debugLoader = loaders.getJavaScriptLoader( { + debugFlags: [ 'ENGINE' ] + } ); + + expect( debugLoader ).to.be.an( 'object' ); + expect( debugLoader ).to.have.property( 'test' ); + + expect( 'C:\\Program Files\\ckeditor\\plugin.js' ).to.match( debugLoader.test, 'Windows' ); + expect( '/home/ckeditor/plugin.js' ).to.match( debugLoader.test, 'Linux' ); + + expect( debugLoader ).to.have.property( 'loader' ); + expect( debugLoader.loader.endsWith( 'ck-debug-loader' ) ).to.equal( true ); + expect( debugLoader ).to.have.property( 'options' ); + expect( debugLoader.options ).to.be.an( 'object' ); + expect( debugLoader.options ).to.have.property( 'debugFlags' ); + expect( debugLoader.options.debugFlags ).to.be.an( 'array' ); + expect( debugLoader.options.debugFlags ).to.include( 'ENGINE' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js new file mode 100644 index 000000000..ef903c04b --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js @@ -0,0 +1,88 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; + + +describe( 'getStylesLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getStylesLoader ).to.be.a( 'function' ); + } ); + + it( 'should return a definition that allow saving the produced CSS into a file using `mini-css-extract-plugin#loader`', () => { + const loader = loaders.getStylesLoader( { + extractToSeparateFile: true, + themePath: 'path/to/theme' + } ); + + expect( loader ).to.be.an( 'object' ); + + const cssLoader = loader.use[ 0 ]; + + expect( cssLoader ).to.be.equal( '/path/to/mini-css-extract-plugin/loader' ); + } ); + + it( 'should return a definition that allow attaching the produced CSS on a site using `style-loader`', () => { + const loader = loaders.getStylesLoader( { + themePath: 'path/to/theme' + } ); + + expect( loader ).to.be.an( 'object' ); + + const styleLoader = loader.use[ 0 ]; + + expect( styleLoader ).to.be.an( 'object' ); + expect( styleLoader ).to.have.property( 'loader', 'style-loader' ); + expect( styleLoader ).to.have.property( 'options' ); + expect( styleLoader.options ).to.be.an( 'object' ); + expect( styleLoader.options ).to.have.property( 'injectType', 'singletonStyleTag' ); + expect( styleLoader.options ).to.have.property( 'attributes' ); + } ); + + it( 'should return a definition containing the correct setup of the `postcss-loader`', () => { + const loader = loaders.getStylesLoader( { + themePath: 'path/to/theme' + } ); + + expect( loader ).to.be.an( 'object' ); + + // Array.at() is available since Node 16.6. + const postCssLoader = loader.use.pop(); + + expect( postCssLoader ).to.be.an( 'object' ); + expect( postCssLoader ).to.have.property( 'loader', 'postcss-loader' ); + expect( postCssLoader ).to.have.property( 'options' ); + expect( postCssLoader.options ).to.be.an( 'object' ); + expect( postCssLoader.options ).to.have.property( 'postcssOptions', 'styles.getPostCssConfig()' ); + + expect( postCssOptions ).to.be.an( 'object' ); + expect( postCssOptions ).to.have.property( 'themeImporter' ); + expect( postCssOptions ).to.have.property( 'minify', false ); + expect( postCssOptions ).to.have.property( 'sourceMap', false ); + expect( postCssOptions.themeImporter ).to.be.an( 'object' ); + expect( postCssOptions.themeImporter ).to.have.property( 'themePath', 'path/to/theme' ); + } ); + + it( 'should return a definition containing the correct setup of the `css-loader`', () => { + const loader = loaders.getStylesLoader( { + skipPostCssLoader: true + } ); + + for ( const definition of loader.use ) { + expect( definition.loader ).to.not.equal( 'postcss-loader' ); + } + } ); + + it( 'should allow skipping adding the postcss-loader', () => { + const loader = loaders.getStylesLoader( { + skipPostCssLoader: true + } ); + + // Array.at() is available since Node 16.6. + const cssLoader = loader.use.pop(); + + expect( cssLoader ).to.be.equal( 'css-loader' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js b/packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js new file mode 100644 index 000000000..72198e28b --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js @@ -0,0 +1,80 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; + + +describe( 'getTypeScriptLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getTypeScriptLoader ).to.be.a( 'function' ); + } ); + + it( 'should return a definition that allows processing `*.ts` files using esbuild-loader', () => { + const tsLoader = loaders.getTypeScriptLoader( { + configFile: '/home/project/configs/tsconfig.json' + } ); + + expect( tsLoader ).to.be.an( 'object' ); + expect( tsLoader ).to.have.property( 'test' ); + + expect( 'C:\\Program Files\\ckeditor\\plugin.ts' ).to.match( tsLoader.test, 'Windows' ); + expect( '/home/ckeditor/plugin.ts' ).to.match( tsLoader.test, 'Linux' ); + + const esbuildLoader = tsLoader.use.find( item => item.loader === 'esbuild-loader' ); + + expect( esbuildLoader ).to.be.an( 'object' ); + expect( esbuildLoader ).to.have.property( 'options' ); + expect( esbuildLoader.options ).to.have.property( 'tsconfig', '/home/project/configs/tsconfig.json' ); + } ); + + it( 'should return a definition that allows processing `*.ts` files using esbuild-loader (skipping `options.configFile`)', () => { + const tsLoader = loaders.getTypeScriptLoader(); + + expect( tsLoader ).to.be.an( 'object' ); + expect( tsLoader ).to.have.property( 'test' ); + + expect( 'C:\\Program Files\\ckeditor\\plugin.ts' ).to.match( tsLoader.test, 'Windows' ); + expect( '/home/ckeditor/plugin.ts' ).to.match( tsLoader.test, 'Linux' ); + + const esbuildLoader = tsLoader.use.find( item => item.loader === 'esbuild-loader' ); + + expect( esbuildLoader ).to.be.an( 'object' ); + expect( esbuildLoader ).to.have.property( 'options' ); + expect( esbuildLoader.options ).to.have.property( 'tsconfig', 'tsconfig.json' ); + } ); + + it( 'should return a definition that enables the debug loader before the typescript files', () => { + const tsLoader = loaders.getTypeScriptLoader( { + configFile: '/home/project/configs/tsconfig.json', + includeDebugLoader: true, + debugFlags: [ 'ENGINE' ] + } ); + + const ckDebugLoaderIndex = tsLoader.use.findIndex( item => item.loader.endsWith( 'ck-debug-loader' ) ); + const tsLoaderIndex = tsLoader.use.findIndex( item => item.loader === 'esbuild-loader' ); + + // Webpack reads the "use" array from back to the front. + expect( ckDebugLoaderIndex ).to.equal( 1 ); + expect( tsLoaderIndex ).to.equal( 0 ); + } ); + + it( 'should pass the debug options into the debug loader', () => { + const tsLoader = loaders.getTypeScriptLoader( { + configFile: '/home/project/configs/tsconfig.json', + includeDebugLoader: true, + debugFlags: [ 'ENGINE' ] + } ); + + const debugLoader = tsLoader.use.find( item => item.loader.endsWith( 'ck-debug-loader' ) ); + + expect( debugLoader ).to.be.an( 'object' ); + expect( debugLoader ).to.have.property( 'loader' ); + expect( debugLoader ).to.have.property( 'options' ); + expect( debugLoader.options ).to.be.an( 'object' ); + expect( debugLoader.options ).to.have.property( 'debugFlags' ); + expect( debugLoader.options.debugFlags ).to.be.an( 'array' ); + expect( debugLoader.options.debugFlags ).to.include( 'ENGINE' ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/index.js b/packages/ckeditor5-dev-utils/tests/loaders/index.js index a33a619d0..f6d1e55b3 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/index.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/index.js @@ -3,357 +3,71 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { expect } = require( 'chai' ); -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); - -const escapedPathSep = require( 'path' ).sep === '/' ? '/' : '\\\\'; - -describe( 'loaders', () => { - let loaders, postCssOptions; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'mini-css-extract-plugin', { - loader: '/path/to/mini-css-extract-plugin/loader' - } ); - - mockery.registerMock( '../styles', { - getPostCssConfig: options => { - postCssOptions = options; - - return 'styles.getPostCssConfig()'; - } +import { describe, expect, it, vi } from 'vitest'; +import * as loaders from '../../lib/loaders/index.js'; +import getCoverageLoader from '../../lib/loaders/getcoverageloader.js'; +import getTypeScriptLoader from '../../lib/loaders/gettypescriptloader.js'; +import getDebugLoader from '../../lib/loaders/getdebugloader.js'; +import getIconsLoader from '../../lib/loaders/geticonsloader.js'; +import getFormattedTextLoader from '../../lib/loaders/getformattedtextloader.js'; +import getJavaScriptLoader from '../../lib/loaders/getjavascriptloader.js'; +import getStylesLoader from '../../lib/loaders/getstylesloader.js'; + +vi.mock( '../../lib/loaders/getcoverageloader.js' ); +vi.mock( '../../lib/loaders/gettypescriptloader.js' ); +vi.mock( '../../lib/loaders/getdebugloader.js' ); +vi.mock( '../../lib/loaders/geticonsloader.js' ); +vi.mock( '../../lib/loaders/getformattedtextloader.js' ); +vi.mock( '../../lib/loaders/getjavascriptloader.js' ); +vi.mock( '../../lib/loaders/getstylesloader.js' ); + +describe( 'loaders/index.js', () => { + describe( 'getCoverageLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getCoverageLoader ).to.be.a( 'function' ); + expect( loaders.getCoverageLoader ).toEqual( getCoverageLoader ); } ); - - loaders = require( '../../lib/loaders/index' ); - } ); - - afterEach( () => { - sinon.restore(); - mockery.disable(); - mockery.deregisterAll(); } ); describe( 'getTypeScriptLoader()', () => { it( 'should be a function', () => { expect( loaders.getTypeScriptLoader ).to.be.a( 'function' ); - } ); - - it( 'should return a definition that allows processing `*.ts` files using esbuild-loader', () => { - const tsLoader = loaders.getTypeScriptLoader( { - configFile: '/home/project/configs/tsconfig.json' - } ); - - expect( tsLoader ).to.be.an( 'object' ); - expect( tsLoader ).to.have.property( 'test' ); - - expect( 'C:\\Program Files\\ckeditor\\plugin.ts' ).to.match( tsLoader.test, 'Windows' ); - expect( '/home/ckeditor/plugin.ts' ).to.match( tsLoader.test, 'Linux' ); - - const esbuildLoader = tsLoader.use.find( item => item.loader === 'esbuild-loader' ); - - expect( esbuildLoader ).to.be.an( 'object' ); - expect( esbuildLoader ).to.have.property( 'options' ); - expect( esbuildLoader.options ).to.have.property( 'tsconfig', '/home/project/configs/tsconfig.json' ); - } ); - - it( 'should return a definition that allows processing `*.ts` files using esbuild-loader (skipping `options.configFile`)', () => { - const tsLoader = loaders.getTypeScriptLoader(); - - expect( tsLoader ).to.be.an( 'object' ); - expect( tsLoader ).to.have.property( 'test' ); - - expect( 'C:\\Program Files\\ckeditor\\plugin.ts' ).to.match( tsLoader.test, 'Windows' ); - expect( '/home/ckeditor/plugin.ts' ).to.match( tsLoader.test, 'Linux' ); - - const esbuildLoader = tsLoader.use.find( item => item.loader === 'esbuild-loader' ); - - expect( esbuildLoader ).to.be.an( 'object' ); - expect( esbuildLoader ).to.have.property( 'options' ); - expect( esbuildLoader.options ).to.have.property( 'tsconfig', 'tsconfig.json' ); - } ); - - it( 'should return a definition that enables the debug loader before the typescript files', () => { - const tsLoader = loaders.getTypeScriptLoader( { - configFile: '/home/project/configs/tsconfig.json', - includeDebugLoader: true, - debugFlags: [ 'ENGINE' ] - } ); - - const ckDebugLoaderIndex = tsLoader.use.findIndex( item => item.loader.endsWith( 'ck-debug-loader' ) ); - const tsLoaderIndex = tsLoader.use.findIndex( item => item.loader === 'esbuild-loader' ); - - // Webpack reads the "use" array from back to the front. - expect( ckDebugLoaderIndex ).to.equal( 1 ); - expect( tsLoaderIndex ).to.equal( 0 ); - } ); - - it( 'should pass the debug options into the debug loader', () => { - const tsLoader = loaders.getTypeScriptLoader( { - configFile: '/home/project/configs/tsconfig.json', - includeDebugLoader: true, - debugFlags: [ 'ENGINE' ] - } ); - - const debugLoader = tsLoader.use.find( item => item.loader.endsWith( 'ck-debug-loader' ) ); - - expect( debugLoader ).to.be.an( 'object' ); - expect( debugLoader ).to.have.property( 'loader' ); - expect( debugLoader ).to.have.property( 'options' ); - expect( debugLoader.options ).to.be.an( 'object' ); - expect( debugLoader.options ).to.have.property( 'debugFlags' ); - expect( debugLoader.options.debugFlags ).to.be.an( 'array' ); - expect( debugLoader.options.debugFlags ).to.include( 'ENGINE' ); + expect( loaders.getTypeScriptLoader ).toEqual( getTypeScriptLoader ); } ); } ); - describe( 'getJavaScriptLoader()', () => { + describe( 'getDebugLoader()', () => { it( 'should be a function', () => { - expect( loaders.getJavaScriptLoader ).to.be.a( 'function' ); - } ); - - it( 'should return a definition that enables the ck-debug-loader', () => { - const debugLoader = loaders.getJavaScriptLoader( { - debugFlags: [ 'ENGINE' ] - } ); - - expect( debugLoader ).to.be.an( 'object' ); - expect( debugLoader ).to.have.property( 'test' ); - - expect( 'C:\\Program Files\\ckeditor\\plugin.js' ).to.match( debugLoader.test, 'Windows' ); - expect( '/home/ckeditor/plugin.js' ).to.match( debugLoader.test, 'Linux' ); - - expect( debugLoader ).to.have.property( 'loader' ); - expect( debugLoader.loader.endsWith( 'ck-debug-loader' ) ).to.equal( true ); - expect( debugLoader ).to.have.property( 'options' ); - expect( debugLoader.options ).to.be.an( 'object' ); - expect( debugLoader.options ).to.have.property( 'debugFlags' ); - expect( debugLoader.options.debugFlags ).to.be.an( 'array' ); - expect( debugLoader.options.debugFlags ).to.include( 'ENGINE' ); - } ); - } ); - - describe( 'getStylesLoader()', () => { - it( 'should be a function', () => { - expect( loaders.getStylesLoader ).to.be.a( 'function' ); - } ); - - it( 'should return a definition that allow saving the produced CSS into a file using `mini-css-extract-plugin#loader`', () => { - const loader = loaders.getStylesLoader( { - extractToSeparateFile: true, - themePath: 'path/to/theme' - } ); - - expect( loader ).to.be.an( 'object' ); - - const cssLoader = loader.use[ 0 ]; - - expect( cssLoader ).to.be.equal( '/path/to/mini-css-extract-plugin/loader' ); - } ); - - it( 'should return a definition that allow attaching the produced CSS on a site using `style-loader`', () => { - const loader = loaders.getStylesLoader( { - themePath: 'path/to/theme' - } ); - - expect( loader ).to.be.an( 'object' ); - - const styleLoader = loader.use[ 0 ]; - - expect( styleLoader ).to.be.an( 'object' ); - expect( styleLoader ).to.have.property( 'loader', 'style-loader' ); - expect( styleLoader ).to.have.property( 'options' ); - expect( styleLoader.options ).to.be.an( 'object' ); - expect( styleLoader.options ).to.have.property( 'injectType', 'singletonStyleTag' ); - expect( styleLoader.options ).to.have.property( 'attributes' ); - } ); - - it( 'should return a definition containing the correct setup of the `postcss-loader`', () => { - const loader = loaders.getStylesLoader( { - themePath: 'path/to/theme' - } ); - - expect( loader ).to.be.an( 'object' ); - - // Array.at() is available since Node 16.6. - const postCssLoader = loader.use.pop(); - - expect( postCssLoader ).to.be.an( 'object' ); - expect( postCssLoader ).to.have.property( 'loader', 'postcss-loader' ); - expect( postCssLoader ).to.have.property( 'options' ); - expect( postCssLoader.options ).to.be.an( 'object' ); - expect( postCssLoader.options ).to.have.property( 'postcssOptions', 'styles.getPostCssConfig()' ); - - expect( postCssOptions ).to.be.an( 'object' ); - expect( postCssOptions ).to.have.property( 'themeImporter' ); - expect( postCssOptions ).to.have.property( 'minify', false ); - expect( postCssOptions ).to.have.property( 'sourceMap', false ); - expect( postCssOptions.themeImporter ).to.be.an( 'object' ); - expect( postCssOptions.themeImporter ).to.have.property( 'themePath', 'path/to/theme' ); - } ); - - it( 'should return a definition containing the correct setup of the `css-loader`', () => { - const loader = loaders.getStylesLoader( { - skipPostCssLoader: true - } ); - - for ( const definition of loader.use ) { - expect( definition.loader ).to.not.equal( 'postcss-loader' ); - } - } ); - - it( 'should allow skipping adding the postcss-loader', () => { - const loader = loaders.getStylesLoader( { - skipPostCssLoader: true - } ); - - // Array.at() is available since Node 16.6. - const cssLoader = loader.use.pop(); - - expect( cssLoader ).to.be.equal( 'css-loader' ); + expect( loaders.getDebugLoader ).to.be.a( 'function' ); + expect( loaders.getDebugLoader ).toEqual( getDebugLoader ); } ); } ); describe( 'getIconsLoader()', () => { it( 'should be a function', () => { expect( loaders.getIconsLoader ).to.be.a( 'function' ); - } ); - - it( 'should return a definition loading the svg files properly (a full CKEditor 5 icon path check)', () => { - const svgLoader = loaders.getIconsLoader(); - - expect( svgLoader ).to.be.an( 'object' ); - expect( svgLoader ).to.have.property( 'use' ); - expect( svgLoader.use ).to.include( 'raw-loader' ); - expect( svgLoader ).to.have.property( 'test' ); - - const svgRegExp = svgLoader.test; - - expect( 'C:\\Program Files\\ckeditor\\ckeditor5-basic-styles\\theme\\icons\\italic.svg' ).to.match( svgRegExp, 'Windows' ); - expect( '/home/ckeditor/ckeditor5-basic-styles/theme/icons/italic.svg' ).to.match( svgRegExp, 'Linux' ); - } ); - - it( 'should return a definition loading the svg files properly (accept any svg file)', () => { - const svgLoader = loaders.getIconsLoader( { matchExtensionOnly: true } ); - - expect( svgLoader ).to.be.an( 'object' ); - expect( svgLoader ).to.have.property( 'use' ); - expect( svgLoader.use ).to.include( 'raw-loader' ); - expect( svgLoader ).to.have.property( 'test' ); - - const svgRegExp = svgLoader.test; - - expect( 'C:\\Program Files\\ckeditor\\italic.svg' ).to.match( svgRegExp, 'Windows' ); - expect( '/home/ckeditor/italic.svg' ).to.match( svgRegExp, 'Linux' ); + expect( loaders.getIconsLoader ).toEqual( getIconsLoader ); } ); } ); describe( 'getFormattedTextLoader()', () => { it( 'should be a function', () => { expect( loaders.getFormattedTextLoader ).to.be.a( 'function' ); - } ); - - it( 'should return a definition accepting files that store readable content', () => { - const textLoader = loaders.getFormattedTextLoader(); - - expect( textLoader ).to.be.an( 'object' ); - expect( textLoader ).to.have.property( 'use' ); - expect( textLoader.use ).to.include( 'raw-loader' ); - expect( textLoader ).to.have.property( 'test' ); - - const loaderRegExp = textLoader.test; - - expect( 'C:\\Program Files\\ckeditor\\italic.html' ).to.match( loaderRegExp, 'HTML: Windows' ); - expect( '/home/ckeditor/italic.html' ).to.match( loaderRegExp, 'HTML: Linux' ); - - expect( 'C:\\Program Files\\ckeditor\\italic.txt' ).to.match( loaderRegExp, 'TXT: Windows' ); - expect( '/home/ckeditor/italic.txt' ).to.match( loaderRegExp, 'TXT: Linux' ); - - expect( 'C:\\Program Files\\ckeditor\\italic.rtf' ).to.match( loaderRegExp, 'RTF: Windows' ); - expect( '/home/ckeditor/italic.rtf' ).to.match( loaderRegExp, 'RTF: Linux' ); + expect( loaders.getFormattedTextLoader ).toEqual( getFormattedTextLoader ); } ); } ); - describe( 'getCoverageLoader()', () => { + describe( 'getJavaScriptLoader()', () => { it( 'should be a function', () => { - expect( loaders.getCoverageLoader ).to.be.a( 'function' ); - } ); - - it( 'should return a definition containing a loader for measuring the coverage', () => { - const coverageLoader = loaders.getCoverageLoader( { - files: [] - } ); - - expect( coverageLoader ).to.be.an( 'object' ); - expect( '/path/to/javascript.js' ).to.match( coverageLoader.test ); - expect( '/path/to/typescript.ts' ).to.match( coverageLoader.test ); - - expect( coverageLoader.include ).to.be.an( 'array' ); - expect( coverageLoader.include ).to.lengthOf( 0 ); - expect( coverageLoader.exclude ).to.be.an( 'array' ); - expect( coverageLoader.exclude ).to.lengthOf( 1 ); - - expect( coverageLoader.use ).to.be.an( 'array' ); - expect( coverageLoader.use ).to.lengthOf( 1 ); - - const babelLoader = coverageLoader.use[ 0 ]; - - expect( babelLoader.loader ).to.equal( 'babel-loader' ); - } ); - - it( 'should return a definition containing a loader for measuring the coverage (include glob check)', () => { - const coverageLoader = loaders.getCoverageLoader( { - files: [ - // -f utils - [ 'node_modules/ckeditor5-utils/tests/**/*.js' ] - ] - } ); - - expect( coverageLoader ).to.be.an( 'object' ); - expect( coverageLoader ).to.have.property( 'include' ); - expect( coverageLoader.include ).to.be.an( 'array' ); - expect( coverageLoader.include ).to.deep.equal( [ - new RegExp( [ 'ckeditor5-utils', 'src', '' ].join( escapedPathSep ) ) - ] ); - } ); - - it( 'should return a definition containing a loader for measuring the coverage (exclude glob check)', () => { - const coverageLoader = loaders.getCoverageLoader( { - files: [ - // -f !utils - [ 'node_modules/ckeditor5-!(utils)/tests/**/*.js' ] - ] - } ); - - expect( coverageLoader ).to.be.an( 'object' ); - expect( coverageLoader ).to.have.property( 'include' ); - expect( coverageLoader.include ).to.be.an( 'array' ); - expect( coverageLoader.include ).to.deep.equal( [ - new RegExp( [ 'ckeditor5-!(utils)', 'src', '' ].join( escapedPathSep ) ) - ] ); + expect( loaders.getJavaScriptLoader ).to.be.a( 'function' ); + expect( loaders.getJavaScriptLoader ).toEqual( getJavaScriptLoader ); } ); + } ); - it( 'should return a definition containing a loader for measuring the coverage (for root named ckeditor5-*)', () => { - const coverageLoader = loaders.getCoverageLoader( { - files: [ - [ '/ckeditor5-collab/packages/ckeditor5-alignment/tests/**/*.{js,ts}' ] - ] - } ); - - expect( coverageLoader ).to.be.an( 'object' ); - expect( coverageLoader ).to.have.property( 'include' ); - expect( coverageLoader.include ).to.be.an( 'array' ); - expect( coverageLoader.include ).to.deep.equal( [ - new RegExp( [ 'ckeditor5-alignment', 'src', '' ].join( escapedPathSep ) ) - ] ); + describe( 'getStylesLoader()', () => { + it( 'should be a function', () => { + expect( loaders.getStylesLoader ).to.be.a( 'function' ); + expect( loaders.getStylesLoader ).toEqual( getStylesLoader ); } ); } ); } ); From 37c6046ccdccc6f15a8848eb50f8a6a484373a0f Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 11 Sep 2024 15:11:35 +0200 Subject: [PATCH 080/172] Refactor. --- packages/ckeditor5-dev-utils/lib/index.js | 14 +- packages/ckeditor5-dev-utils/lib/tools.js | 177 +++++++++--------- .../tests/builds/getdllpluginwebpackconfig.js | 80 ++++---- .../ckeditor5-dev-utils/tests/builds/index.js | 2 +- .../tests/bundler/getlicensebanner.js | 10 +- .../tests/bundler/index.js | 24 +-- 6 files changed, 148 insertions(+), 159 deletions(-) diff --git a/packages/ckeditor5-dev-utils/lib/index.js b/packages/ckeditor5-dev-utils/lib/index.js index 32928d804..636073812 100644 --- a/packages/ckeditor5-dev-utils/lib/index.js +++ b/packages/ckeditor5-dev-utils/lib/index.js @@ -3,10 +3,10 @@ * For licensing, see LICENSE.md. */ -export { default as logger } from './logger.js'; -export { default as tools } from './tools.js'; -export { default as loaders } from './loaders/index.js'; -export { default as stream } from './stream.js'; -export { default as bundler } from './bundler/index.js'; -export { default as builds } from './builds/index.js'; -export { default as styles } from './styles/index.js'; +export * as logger from './logger.js'; +export * as tools from './tools.js'; +export * as loaders from './loaders/index.js'; +export * as stream from './stream.js'; +export * as bundler from './bundler/index.js'; +export * as builds from './builds/index.js'; +export * as styles from './styles/index.js'; diff --git a/packages/ckeditor5-dev-utils/lib/tools.js b/packages/ckeditor5-dev-utils/lib/tools.js index f0c934006..67b0261f5 100644 --- a/packages/ckeditor5-dev-utils/lib/tools.js +++ b/packages/ckeditor5-dev-utils/lib/tools.js @@ -4,115 +4,112 @@ */ import chalk from 'chalk'; -import createSpinner from './tools/createspinner.js'; import logger from './logger.js'; import sh from 'shelljs'; import fs from 'fs'; import pth from 'path'; -export default { - createSpinner, - - /** - * Executes a shell command. - * - * @param {String} command The command to be executed. - * @param {Object} options - * @param {'info'|'warning'|'error'|'silent'} [options.verbosity='info'] Level of the verbosity. If set as 'info' - * both outputs (stdout and stderr) will be logged. If set as 'error', only stderr output will be logged. - * @param {String} [options.cwd=process.cwd()] - * @param {Boolean} [options.async=false] If set, the command execution is asynchronous. The execution is synchronous by default. - * @returns {String|Promise.} The command output. - */ - shExec( command, options = {} ) { - const { - verbosity = 'info', - cwd = process.cwd(), - async = false - } = options; - - const log = logger( verbosity ); - - sh.config.silent = true; - - const execOptions = { cwd }; - - if ( async ) { - return new Promise( ( resolve, reject ) => { - sh.exec( command, execOptions, ( code, stdout, stderr ) => { - try { - const result = execHandler( code, stdout, stderr ); - - resolve( result ); - } catch ( err ) { - reject( err ); - } - } ); - } ); - } +export { default as createSpinner } from './tools/createspinner.js'; - const { code, stdout, stderr } = sh.exec( command, execOptions ); +/** + * Executes a shell command. + * + * @param {String} command The command to be executed. + * @param {Object} options + * @param {'info'|'warning'|'error'|'silent'} [options.verbosity='info'] Level of the verbosity. If set as 'info' + * both outputs (stdout and stderr) will be logged. If set as 'error', only stderr output will be logged. + * @param {String} [options.cwd=process.cwd()] + * @param {Boolean} [options.async=false] If set, the command execution is asynchronous. The execution is synchronous by default. + * @returns {String|Promise.} The command output. + */ +export function shExec( command, options = {} ) { + const { + verbosity = 'info', + cwd = process.cwd(), + async = false + } = options; - return execHandler( code, stdout, stderr ); + const log = logger( verbosity ); - function execHandler( code, stdout, stderr ) { - const grey = chalk.grey; + sh.config.silent = true; - if ( code ) { - if ( stdout ) { - log.error( grey( stdout ) ); - } + const execOptions = { cwd }; + + if ( async ) { + return new Promise( ( resolve, reject ) => { + sh.exec( command, execOptions, ( code, stdout, stderr ) => { + try { + const result = execHandler( code, stdout, stderr ); - if ( stderr ) { - log.error( grey( stderr ) ); + resolve( result ); + } catch ( err ) { + reject( err ); } + } ); + } ); + } - throw new Error( `Error while executing ${ command }: ${ stderr }` ); - } + const { code, stdout, stderr } = sh.exec( command, execOptions ); + + return execHandler( code, stdout, stderr ); + function execHandler( code, stdout, stderr ) { + const grey = chalk.grey; + + if ( code ) { if ( stdout ) { - log.info( grey( stdout ) ); + log.error( grey( stdout ) ); } if ( stderr ) { - log.info( grey( stderr ) ); + log.error( grey( stderr ) ); } - return stdout; + throw new Error( `Error while executing ${ command }: ${ stderr }` ); } - }, - - /** - * Returns array with all directories under specified path. - * - * @param {String} path - * @returns {Array} - */ - getDirectories( path ) { - const isDirectory = path => { - try { - return fs.statSync( path ).isDirectory(); - } catch ( e ) { - return false; - } - }; - return fs.readdirSync( path ).filter( item => { - return isDirectory( pth.join( path, item ) ); - } ); - }, - - /** - * Updates JSON file under specified path. - * @param {String} path Path to file on disk. - * @param {Function} updateFunction Function that will be called with parsed JSON object. It should return - * modified JSON object to save. - */ - updateJSONFile( path, updateFunction ) { - const contents = fs.readFileSync( path, 'utf-8' ); - let json = JSON.parse( contents ); - json = updateFunction( json ); - - fs.writeFileSync( path, JSON.stringify( json, null, 2 ) + '\n', 'utf-8' ); + if ( stdout ) { + log.info( grey( stdout ) ); + } + + if ( stderr ) { + log.info( grey( stderr ) ); + } + + return stdout; } -}; +} + +/** + * Returns array with all directories under specified path. + * + * @param {String} path + * @returns {Array} + */ +export function getDirectories( path ) { + const isDirectory = path => { + try { + return fs.statSync( path ).isDirectory(); + } catch ( e ) { + return false; + } + }; + + return fs.readdirSync( path ).filter( item => { + return isDirectory( pth.join( path, item ) ); + } ); +} + +/** + * Updates JSON file under specified path. + * @param {String} path Path to file on disk. + * @param {Function} updateFunction Function that will be called with parsed JSON object. It should return + * modified JSON object to save. + */ +export function updateJSONFile( path, updateFunction ) { + const contents = fs.readFileSync( path, 'utf-8' ); + let json = JSON.parse( contents ); + json = updateFunction( json ); + + fs.writeFileSync( path, JSON.stringify( json, null, 2 ) + '\n', 'utf-8' ); +} diff --git a/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js b/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js index 132eef78b..c6ff9bd02 100644 --- a/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js +++ b/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js @@ -6,7 +6,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import fs from 'fs-extra'; import getDllPluginWebpackConfig from '../../lib/builds/getdllpluginwebpackconfig.js'; -import { getLicenseBanner } from '../../lib/bundler/index.js'; import { getIconsLoader, getStylesLoader, getTypeScriptLoader } from '../../lib/loaders/index.js'; const stubs = vi.hoisted( () => ( { @@ -44,21 +43,23 @@ vi.mock( 'path', () => ( { } } ) ); vi.mock( '@ckeditor/ckeditor5-dev-translations', () => ( { - CKEditorTranslationsPlugin: class { + CKEditorTranslationsPlugin: class CKEditorTranslationsPlugin { constructor( ...args ) { + this.name = 'CKEditorTranslationsPlugin'; + stubs.CKEditorTranslationsPlugin.constructor( ...args ); } } } ) ); vi.mock( 'terser-webpack-plugin', () => ( { - default: class TerserPluginMock { + default: class TerserPlugin { constructor( ...args ) { stubs.TerserPlugin.constructor( ...args ); } } } ) ); -describe( 'builds/getDllPluginWebpackConfig()', () => { +describe( 'getDllPluginWebpackConfig()', () => { beforeEach( () => { vi.mocked( fs ).readJsonSync.mockImplementation( input => { if ( input === '/manifest/path' ) { @@ -99,7 +100,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { expect( webpackConfig.optimization.minimizer.length ).to.equal( 1 ); // Due to versions mismatch, the `instanceof` check does not pass. - expect( webpackConfig.optimization.minimizer[ 0 ].constructor.name ).to.equal( 'TerserPluginMock' ); + expect( webpackConfig.optimization.minimizer[ 0 ].constructor.name ).to.equal( 'TerserPlugin' ); } ); it( 'transforms package with many dashes in its name', async () => { @@ -138,7 +139,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } ); it( 'uses index.ts entry file by default', async () => { - vi.mocked( fs ).existsSync.mockImplementation( file => file == '/package/path/src/index.ts' ) + vi.mocked( fs ).existsSync.mockImplementation( file => file === '/package/path/src/index.ts' ); const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', @@ -150,7 +151,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } ); it( 'uses index.js entry file if exists (over its TS version)', async () => { - vi.mocked( fs ).existsSync.mockImplementation( file => file == '/package/path/src/index.js' ); + vi.mocked( fs ).existsSync.mockImplementation( file => file === '/package/path/src/index.js' ); const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', @@ -162,7 +163,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } ); it( 'loads JavaScript files over TypeScript when building for a JavaScript package', async () => { - vi.mocked( fs ).existsSync.mockImplementation( file => file == '/package/path/src/index.js' ); + vi.mocked( fs ).existsSync.mockImplementation( file => file === '/package/path/src/index.js' ); const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', @@ -192,7 +193,7 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { } ); it( 'loads the CKEditorTranslationsPlugin plugin when lang dir exists', async () => { - stubs.fs.existsSync.returns( true ); + vi.mocked( fs ).existsSync.mockReturnValue( true ); const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', @@ -205,17 +206,24 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { .find( plugin => plugin.constructor.name === 'CKEditorTranslationsPlugin' ); expect( ckeditor5TranslationsPlugin ).to.not.be.undefined; - expect( ckeditor5TranslationsPlugin.options.language ).to.equal( 'en' ); - expect( ckeditor5TranslationsPlugin.options.additionalLanguages ).to.equal( 'all' ); - expect( ckeditor5TranslationsPlugin.options.skipPluralFormFunction ).to.equal( true ); - expect( 'src/bold.js' ).to.match( ckeditor5TranslationsPlugin.options.sourceFilesPattern ); - expect( 'src/bold.ts' ).to.match( ckeditor5TranslationsPlugin.options.sourceFilesPattern ); - expect( 'ckeditor5-basic-styles/src/bold.js' ).to.not.match( ckeditor5TranslationsPlugin.options.sourceFilesPattern ); - expect( 'ckeditor5-basic-styles/src/bold.ts' ).to.not.match( ckeditor5TranslationsPlugin.options.sourceFilesPattern ); + + expect( stubs.CKEditorTranslationsPlugin.constructor ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + language: 'en', + additionalLanguages: 'all', + skipPluralFormFunction: true + } ) ); + + const [ firstCall ] = stubs.CKEditorTranslationsPlugin.constructor.mock.calls; + const { sourceFilesPattern } = firstCall[ 0 ]; + + expect( 'src/bold.js' ).to.match( sourceFilesPattern ); + expect( 'src/bold.ts' ).to.match( sourceFilesPattern ); + expect( 'ckeditor5-basic-styles/src/bold.js' ).to.not.match( sourceFilesPattern ); + expect( 'ckeditor5-basic-styles/src/bold.ts' ).to.not.match( sourceFilesPattern ); } ); it( 'does not load the CKEditorTranslationsPlugin plugin when lang dir does not exist', async () => { - stubs.fs.existsSync.returns( false ); + vi.mocked( fs ).existsSync.mockReturnValue( false ); const webpackConfig = await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', @@ -234,61 +242,57 @@ describe( 'builds/getDllPluginWebpackConfig()', () => { describe( '#loaders', () => { describe( 'getTypeScriptLoader()', () => { it( 'it should use the default tsconfig.json if the "options.tsconfigPath" option is not specified', async () => { - getDllPluginWebpackConfig( stubs.webpack, { + await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' } ); - expect( stubs.loaders.getTypeScriptLoader.calledOnce ).to.equal( true ); - - const options = stubs.loaders.getTypeScriptLoader.firstCall.args[ 0 ]; - expect( options ).to.have.property( 'configFile', 'tsconfig.json' ); + expect( vi.mocked( getTypeScriptLoader ) ).toHaveBeenCalledExactlyOnceWith( { + configFile: 'tsconfig.json' + } ); } ); it( 'it should the specified "options.tsconfigPath" value', async () => { - getDllPluginWebpackConfig( stubs.webpack, { + await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path', tsconfigPath: '/config/tsconfig.json' } ); - expect( stubs.loaders.getTypeScriptLoader.calledOnce ).to.equal( true ); - - const options = stubs.loaders.getTypeScriptLoader.firstCall.args[ 0 ]; - expect( options ).to.have.property( 'configFile', '/config/tsconfig.json' ); + expect( vi.mocked( getTypeScriptLoader ) ).toHaveBeenCalledExactlyOnceWith( { + configFile: '/config/tsconfig.json' + } ); } ); } ); describe( 'getIconsLoader()', () => { it( 'it should get the loader', async () => { - getDllPluginWebpackConfig( stubs.webpack, { + await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' } ); - expect( stubs.loaders.getIconsLoader.calledOnce ).to.equal( true ); - - const options = stubs.loaders.getIconsLoader.firstCall.args[ 0 ]; - expect( options ).to.have.property( 'matchExtensionOnly', true ); + expect( vi.mocked( getIconsLoader ) ).toHaveBeenCalledExactlyOnceWith( { + matchExtensionOnly: true + } ); } ); } ); describe( 'getStylesLoader()', () => { it( 'it should get the loader', async () => { - getDllPluginWebpackConfig( stubs.webpack, { + await getDllPluginWebpackConfig( stubs.webpack, { packagePath: '/package/path', themePath: '/theme/path', manifestPath: '/manifest/path' } ); - expect( stubs.loaders.getStylesLoader.calledOnce ).to.equal( true ); - - const options = stubs.loaders.getStylesLoader.firstCall.args[ 0 ]; - expect( options ).to.have.property( 'minify', true ); - expect( options ).to.have.property( 'themePath', '/theme/path' ); + expect( vi.mocked( getStylesLoader ) ).toHaveBeenCalledExactlyOnceWith( { + minify: true, + themePath: '/theme/path' + } ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/builds/index.js b/packages/ckeditor5-dev-utils/tests/builds/index.js index 7a5196d17..1ff005c06 100644 --- a/packages/ckeditor5-dev-utils/tests/builds/index.js +++ b/packages/ckeditor5-dev-utils/tests/builds/index.js @@ -9,7 +9,7 @@ import getDllPluginWebpackConfig from '../../lib/builds/getdllpluginwebpackconfi vi.mock( '../../lib/builds/getdllpluginwebpackconfig.js' ); -describe( 'builds', () => { +describe( 'builds/index.js', () => { describe( 'getDllPluginWebpackConfig()', () => { it( 'should be a function', () => { expect( tasks.getDllPluginWebpackConfig ).to.be.a( 'function' ); diff --git a/packages/ckeditor5-dev-utils/tests/bundler/getlicensebanner.js b/packages/ckeditor5-dev-utils/tests/bundler/getlicensebanner.js index 0a3d68dc4..ee8762975 100644 --- a/packages/ckeditor5-dev-utils/tests/bundler/getlicensebanner.js +++ b/packages/ckeditor5-dev-utils/tests/bundler/getlicensebanner.js @@ -3,13 +3,11 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it } from 'vitest'; +import getLicenseBanner from '../../lib/bundler/getlicensebanner.js'; -const expect = require( 'chai' ).expect; -const getLicenseBanner = require( '../../lib/bundler/getlicensebanner' ); - -describe( 'bundler', () => { - describe( 'getLicenseBanner()', () => { +describe( 'getLicenseBanner()', () => { + it( 'should return a banner', () => { expect( getLicenseBanner() ).to.match( /\/\*![\S\s]+\*\//g ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/bundler/index.js b/packages/ckeditor5-dev-utils/tests/bundler/index.js index 67aa7dada..dc4a89ec4 100644 --- a/packages/ckeditor5-dev-utils/tests/bundler/index.js +++ b/packages/ckeditor5-dev-utils/tests/bundler/index.js @@ -3,27 +3,17 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import * as bundler from '../../lib/bundler/index.js'; +import getLicenseBanner from '../../lib/bundler/getlicensebanner.js'; -const chai = require( 'chai' ); -const expect = chai.expect; - -describe( 'bundler', () => { - let tasks; - - beforeEach( () => { - tasks = require( '../../lib/bundler/index' ); - } ); - - describe( 'createEntryFile()', () => { - it( 'should be a function', () => { - expect( tasks.createEntryFile ).to.be.a( 'function' ); - } ); - } ); +vi.mock( '../../lib/bundler/getlicensebanner.js' ); +describe( 'bundler/index.js', () => { describe( 'getLicenseBanner()', () => { it( 'should be a function', () => { - expect( tasks.getLicenseBanner ).to.be.a( 'function' ); + expect( bundler.getLicenseBanner ).to.be.a( 'function' ); + expect( bundler.getLicenseBanner ).toEqual( getLicenseBanner ); } ); } ); } ); From f176262e1ca74711d38d633e6473a18eb3507586 Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:33:31 +0200 Subject: [PATCH 081/172] Migrated some tests. --- .../lib/createpotfiles.js | 21 +- .../ckeditor5-dev-transifex/lib/download.js | 22 +- .../ckeditor5-dev-transifex/lib/gettoken.js | 8 +- packages/ckeditor5-dev-transifex/lib/index.js | 16 +- .../lib/transifexservice.js | 6 +- .../ckeditor5-dev-transifex/lib/upload.js | 22 +- packages/ckeditor5-dev-transifex/lib/utils.js | 8 +- packages/ckeditor5-dev-transifex/package.json | 11 +- .../tests/createpotfiles.js | 10 +- .../ckeditor5-dev-transifex/tests/download.js | 10 +- .../tests/transifexservice.js | 9 +- .../ckeditor5-dev-transifex/tests/upload.js | 923 +++++++++--------- .../ckeditor5-dev-transifex/tests/utils.js | 111 ++- .../ckeditor5-dev-transifex/vitest.config.js | 23 + 14 files changed, 610 insertions(+), 590 deletions(-) create mode 100644 packages/ckeditor5-dev-transifex/vitest.config.js diff --git a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js index 26f5aa87d..eaabab1dc 100644 --- a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js @@ -3,15 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const fs = require( 'fs-extra' ); -const del = require( 'del' ); -const defaultLogger = require( '@ckeditor/ckeditor5-dev-utils' ).logger(); -const { findMessages } = require( '@ckeditor/ckeditor5-dev-translations' ); -const { verifyProperties } = require( './utils' ); - +import path from 'path'; +import fs from 'fs-extra'; +import del from 'del'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; +import { verifyProperties } from './utils.js'; + +const defaultLogger = logger(); const langContextSuffix = path.join( 'lang', 'contexts.json' ); const corePackageName = 'ckeditor5-core'; @@ -29,7 +28,7 @@ const corePackageName = 'ckeditor5-core'; * @param {Boolean} [options.skipLicenseHeader=false] Whether to skip the license header in created `*.pot` files. * @param {Logger} [options.logger] A logger. */ -module.exports = function createPotFiles( options ) { +export default function createPotFiles( options ) { verifyProperties( options, [ 'sourceFiles', 'packagePaths', 'corePackagePath', 'translationsDirectory' ] ); const { @@ -83,7 +82,7 @@ module.exports = function createPotFiles( options ) { translationsDirectory } ); } -}; +} /** * Traverses all packages and returns a map of all found language contexts diff --git a/packages/ckeditor5-dev-transifex/lib/download.js b/packages/ckeditor5-dev-transifex/lib/download.js index a9bb21648..ac893e7b6 100644 --- a/packages/ckeditor5-dev-transifex/lib/download.js +++ b/packages/ckeditor5-dev-transifex/lib/download.js @@ -3,16 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const fs = require( 'fs-extra' ); -const chalk = require( 'chalk' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); -const { cleanPoFileContent, createDictionaryFromPoFileContent } = require( '@ckeditor/ckeditor5-dev-translations' ); -const transifexService = require( './transifexservice' ); -const { verifyProperties, createLogger } = require( './utils' ); -const languageCodeMap = require( './languagecodemap.json' ); +import path from 'path'; +import fs from 'fs-extra'; +import chalk from 'chalk'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { cleanPoFileContent, createDictionaryFromPoFileContent } from '@ckeditor/ckeditor5-dev-translations'; +import transifexService from './transifexservice.js'; +import { verifyProperties, createLogger } from './utils.js'; +import languageCodeMap from './languagecodemap.json'; const logger = createLogger(); @@ -33,7 +31,7 @@ const logger = createLogger(); * @param {String} config.cwd Current work directory. * @param {Boolean} [config.simplifyLicenseHeader=false] Whether to skip adding the contribute guide URL in the output `*.po` files. */ -module.exports = async function downloadTranslations( config ) { +export default async function downloadTranslations( config ) { verifyProperties( config, [ 'organizationName', 'projectName', 'token', 'packages', 'cwd' ] ); transifexService.init( config.token ); @@ -106,7 +104,7 @@ module.exports = async function downloadTranslations( config ) { } else { logger.progress( 'Saved all translations.' ); } -}; +} /** * Saves all valid translations on the filesystem. For each translation entry: diff --git a/packages/ckeditor5-dev-transifex/lib/gettoken.js b/packages/ckeditor5-dev-transifex/lib/gettoken.js index 3211a4b81..09890291b 100644 --- a/packages/ckeditor5-dev-transifex/lib/gettoken.js +++ b/packages/ckeditor5-dev-transifex/lib/gettoken.js @@ -3,16 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const inquirer = require( 'inquirer' ); +import inquirer from 'inquirer'; /** * Takes username and password from prompt and returns promise that resolves with object that contains them. * * @returns {Promise.} */ -module.exports = async function getToken() { +export default async function getToken() { const { token } = await inquirer.prompt( [ { type: 'password', message: 'Provide the Transifex token (generate it here: https://www.transifex.com/user/settings/api/):', @@ -20,4 +18,4 @@ module.exports = async function getToken() { } ] ); return token; -}; +} diff --git a/packages/ckeditor5-dev-transifex/lib/index.js b/packages/ckeditor5-dev-transifex/lib/index.js index 1cef95915..2bf38063c 100644 --- a/packages/ckeditor5-dev-transifex/lib/index.js +++ b/packages/ckeditor5-dev-transifex/lib/index.js @@ -3,16 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import createPotFiles from './createpotfiles.js'; +import uploadPotFiles from './upload.js'; +import downloadTranslations from './download.js'; +import getToken from './gettoken.js'; +import transifexService from './transifexservice.js'; +import transifexUtils from './utils.js'; -const createPotFiles = require( './createpotfiles' ); -const uploadPotFiles = require( './upload' ); -const downloadTranslations = require( './download' ); -const getToken = require( './gettoken' ); -const transifexService = require( './transifexservice' ); -const transifexUtils = require( './utils' ); - -module.exports = { +export default { createPotFiles, uploadPotFiles, downloadTranslations, diff --git a/packages/ckeditor5-dev-transifex/lib/transifexservice.js b/packages/ckeditor5-dev-transifex/lib/transifexservice.js index d8563045d..1266839cf 100644 --- a/packages/ckeditor5-dev-transifex/lib/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/lib/transifexservice.js @@ -3,8 +3,8 @@ * For licensing, see LICENSE.md. */ -const { transifexApi } = require( '@transifex/api' ); -const fetch = require( 'node-fetch' ); +import { transifexApi } from '@transifex/api'; +import fetch from 'node-fetch'; const MAX_REQUEST_ATTEMPTS = 10; const REQUEST_RETRY_TIMEOUT = 3000; // In milliseconds. @@ -18,7 +18,7 @@ const REQUEST_START_OFFSET_TIMEOUT = 100; * * @see https://docs.transifex.com/api-3-0/introduction-to-api-3-0 for API documentation. */ -module.exports = { +export default { init, getProjectData, getTranslations, diff --git a/packages/ckeditor5-dev-transifex/lib/upload.js b/packages/ckeditor5-dev-transifex/lib/upload.js index 20aab16ba..916359d02 100644 --- a/packages/ckeditor5-dev-transifex/lib/upload.js +++ b/packages/ckeditor5-dev-transifex/lib/upload.js @@ -3,15 +3,13 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs/promises' ); -const path = require( 'path' ); -const Table = require( 'cli-table' ); -const chalk = require( 'chalk' ); -const transifexService = require( './transifexservice' ); -const { verifyProperties, createLogger } = require( './utils' ); -const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +import fs from 'fs/promises'; +import path from 'path'; +import Table from 'cli-table'; +import chalk from 'chalk'; +import transifexService from './transifexservice.js'; +import { verifyProperties, createLogger } from './utils.js'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; const RESOURCE_REGEXP = /r:(?[a-z0-9_-]+)$/i; @@ -38,7 +36,7 @@ const TRANSIFEX_RESOURCE_ERRORS = {}; * @param {Map.} config.packages A resource name -> package path map for which translations should be uploaded. * @returns {Promise} */ -module.exports = async function upload( config ) { +export default async function upload( config ) { verifyProperties( config, [ 'token', 'organizationName', 'projectName', 'cwd', 'packages' ] ); const logger = createLogger(); @@ -53,7 +51,7 @@ module.exports = async function upload( config ) { logger.warning( 'Found the file containing a list of packages that failed during the last script execution.' ); logger.warning( 'The script will process only packages listed in the file instead of all passed as "config.packages".' ); - failedPackages = Object.keys( require( pathToFailedUploads ) ); + failedPackages = Object.keys( await import( pathToFailedUploads ) ); } logger.progress( 'Fetching project information...' ); @@ -171,7 +169,7 @@ module.exports = async function upload( config ) { else if ( isFailedUploadFileAvailable ) { await fs.unlink( pathToFailedUploads ); } -}; +} /** * Returns a factory function that process a response from Transifex and prepares a single resource diff --git a/packages/ckeditor5-dev-transifex/lib/utils.js b/packages/ckeditor5-dev-transifex/lib/utils.js index 530a2ecb4..8914fba3b 100644 --- a/packages/ckeditor5-dev-transifex/lib/utils.js +++ b/packages/ckeditor5-dev-transifex/lib/utils.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const { logger: loggerFactory } = require( '@ckeditor/ckeditor5-dev-utils' ); +import chalk from 'chalk'; +import { logger as loggerFactory } from '@ckeditor/ckeditor5-dev-utils'; const utils = { /** @@ -50,4 +48,4 @@ const utils = { } }; -module.exports = utils; +export default utils; diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index 7d68ce4b8..575a2a77d 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -17,6 +17,7 @@ "npm": ">=5.7.1" }, "main": "lib/index.js", + "type": "module", "files": [ "lib" ], @@ -32,14 +33,10 @@ "cli-table": "^0.3.1" }, "devDependencies": { - "chai": "^4.2.0", - "mocha": "^7.1.2", - "mockery": "^2.1.0", - "proxyquire": "^2.1.3", - "sinon": "^9.2.4" + "vitest": "^2.0.5" }, "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.js --coverage" } } diff --git a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js index b5c264a09..e247ad8ca 100644 --- a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js @@ -3,14 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; +import { posix } from 'path'; -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const { posix } = require( 'path' ); -const { expect } = require( 'chai' ); - -describe( 'dev-transifex/createPotFiles()', () => { +describe.skip( 'dev-transifex/createPotFiles()', () => { let stubs; let createPotFiles; diff --git a/packages/ckeditor5-dev-transifex/tests/download.js b/packages/ckeditor5-dev-transifex/tests/download.js index 5874eff1a..43ac8ebc7 100644 --- a/packages/ckeditor5-dev-transifex/tests/download.js +++ b/packages/ckeditor5-dev-transifex/tests/download.js @@ -3,14 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import path from 'path'; +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; -const path = require( 'path' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); -const { expect } = require( 'chai' ); - -describe( 'dev-transifex/download()', () => { +describe.skip( 'dev-transifex/download()', () => { let stubs, mocks, download; beforeEach( () => { diff --git a/packages/ckeditor5-dev-transifex/tests/transifexservice.js b/packages/ckeditor5-dev-transifex/tests/transifexservice.js index 7caf62ed0..85a906eea 100644 --- a/packages/ckeditor5-dev-transifex/tests/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/tests/transifexservice.js @@ -3,14 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; -const chai = require( 'chai' ); -const sinon = require( 'sinon' ); -const expect = chai.expect; -const mockery = require( 'mockery' ); - -describe( 'dev-transifex/transifex-service', () => { +describe.skip( 'dev-transifex/transifex-service', () => { let stubs, mocks, transifexService; beforeEach( () => { diff --git a/packages/ckeditor5-dev-transifex/tests/upload.js b/packages/ckeditor5-dev-transifex/tests/upload.js index 4bcac4715..da898a1df 100644 --- a/packages/ckeditor5-dev-transifex/tests/upload.js +++ b/packages/ckeditor5-dev-transifex/tests/upload.js @@ -3,123 +3,166 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi, beforeEach } from 'vitest'; +import upload from '../lib/upload.js'; + +const { + fsReadFileMock, + fsWriteFileMock, + fsUnlinkMock, + fsLstatMock, + pathJoinMock, + tableConstructorSpy, + tablePushMock, + tableToStringMock, + chalkGrayMock, + chalkCyanMock, + chalkItalicMock, + chalkUnderlineMock, + transifexServiceInitMock, + transifexServiceGetProjectDataMock, + transifexServiceCreateResourceMock, + transifexServiceCreateSourceFileMock, + transifexServiceGetResourceUploadDetailsMock, + utilsVerifyPropertiesMock, + utilsCreateLoggerMock, + toolsCreateSpinnerMock +} = vi.hoisted( () => { + return { + fsReadFileMock: vi.fn(), + fsWriteFileMock: vi.fn(), + fsUnlinkMock: vi.fn(), + fsLstatMock: vi.fn(), + pathJoinMock: vi.fn(), + tableConstructorSpy: vi.fn(), + tablePushMock: vi.fn(), + tableToStringMock: vi.fn(), + chalkGrayMock: vi.fn(), + chalkCyanMock: vi.fn(), + chalkItalicMock: vi.fn(), + chalkUnderlineMock: vi.fn(), + transifexServiceInitMock: vi.fn(), + transifexServiceGetProjectDataMock: vi.fn(), + transifexServiceCreateResourceMock: vi.fn(), + transifexServiceCreateSourceFileMock: vi.fn(), + transifexServiceGetResourceUploadDetailsMock: vi.fn(), + utilsVerifyPropertiesMock: vi.fn(), + utilsCreateLoggerMock: vi.fn(), + toolsCreateSpinnerMock: vi.fn() + }; +} ); -const sinon = require( 'sinon' ); -const { expect } = require( 'chai' ); -const proxyquire = require( 'proxyquire' ); -const mockery = require( 'mockery' ); +vi.mock( 'fs/promises', () => { + return { + default: { + readFile: fsReadFileMock, + writeFile: fsWriteFileMock, + unlink: fsUnlinkMock, + lstat: fsLstatMock + } + }; +} ); -describe( 'dev-transifex/upload()', () => { - let stubs, upload; +vi.mock( 'path', () => { + return { + default: { + join: pathJoinMock + } + }; +} ); - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); +vi.mock( 'cli-table', () => { + return { + default: class { + constructor( ...args ) { + tableConstructorSpy( ...args ); - stubs = { - fs: { - readFile: sinon.stub(), - writeFile: sinon.stub(), - lstat: sinon.stub(), - unlink: sinon.stub() - }, - - path: { - join: sinon.stub().callsFake( ( ...chunks ) => chunks.join( '/' ) ) - }, - - logger: { - progress: sinon.stub(), - info: sinon.stub(), - warning: sinon.stub(), - error: sinon.stub(), - _log: sinon.stub() - }, - - transifexService: { - init: sinon.stub(), - getProjectData: sinon.stub(), - createResource: sinon.stub(), - createSourceFile: sinon.stub(), - getResourceUploadDetails: sinon.stub() - }, - - table: { - constructor: sinon.stub(), - push: sinon.stub(), - toString: sinon.stub() - }, - - tools: { - createSpinner: sinon.stub() - }, - - chalk: { - gray: sinon.stub().callsFake( msg => msg ), - cyan: sinon.stub().callsFake( msg => msg ), - italic: sinon.stub().callsFake( msg => msg ), - underline: sinon.stub().callsFake( msg => msg ) - }, - - utils: { - verifyProperties: sinon.stub(), - createLogger: sinon.stub() + this.push = tablePushMock; + this.toString = tableToStringMock; } - }; + } + }; +} ); - stubs.utils.createLogger.returns( { - progress: stubs.logger.progress, - info: stubs.logger.info, - warning: stubs.logger.warning, - error: stubs.logger.error, - _log: stubs.logger._log - } ); +vi.mock( 'chalk', () => { + return { + default: { + gray: chalkGrayMock, + cyan: chalkCyanMock, + italic: chalkItalicMock, + underline: chalkUnderlineMock + } + }; +} ); - // `proxyquire` does not understand dynamic imports. - mockery.registerMock( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', { - 'ckeditor5-non-existing-01': [ - 'Resource with this Slug and Project already exists.' - ], - 'ckeditor5-non-existing-02': [ - 'Object not found. It may have been deleted or not been created yet.' - ] - } ); +vi.mock( '../lib/transifexservice.js', () => { + return { + default: { + init: transifexServiceInitMock, + getProjectData: transifexServiceGetProjectDataMock, + createResource: transifexServiceCreateResourceMock, + createSourceFile: transifexServiceCreateSourceFileMock, + getResourceUploadDetails: transifexServiceGetResourceUploadDetailsMock + } + }; +} ); - upload = proxyquire( '../lib/upload', { - '@ckeditor/ckeditor5-dev-utils': { - tools: stubs.tools - }, - 'path': stubs.path, - 'fs/promises': stubs.fs, - 'chalk': stubs.chalk, - 'cli-table': class Table { - constructor( ...args ) { - stubs.table.constructor( ...args ); - } +vi.mock( '../lib/utils.js', () => { + return { + verifyProperties: utilsVerifyPropertiesMock, + createLogger: utilsCreateLoggerMock + }; +} ); - push( ...args ) { - return stubs.table.push( ...args ); - } +vi.mock( '@ckeditor/ckeditor5-dev-utils', () => { + return { + tools: { + createSpinner: toolsCreateSpinnerMock + } + }; +} ); - toString( ...args ) { - return stubs.table.toString( ...args ); - } - }, - './transifexservice': stubs.transifexService, - './utils': stubs.utils - } ); +vi.mock( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', () => ( { + default: { + 'ckeditor5-non-existing-01': [ + 'Resource with this Slug and Project already exists.' + ], + 'ckeditor5-non-existing-02': [ + 'Object not found. It may have been deleted or not been created yet.' + ] + } +} ) ); - stubs.fs.lstat.withArgs( '/home/ckeditor5/.transifex-failed-uploads.json' ).rejects(); - } ); +describe( 'dev-transifex/upload()', () => { + let loggerProgressMock, loggerInfoMock, loggerWarningMock, loggerErrorMock, loggerLogMock; + + beforeEach( () => { + vi.mocked( chalkGrayMock ).mockImplementation( string => string ); + vi.mocked( chalkCyanMock ).mockImplementation( string => string ); + vi.mocked( chalkItalicMock ).mockImplementation( string => string ); + vi.mocked( chalkUnderlineMock ).mockImplementation( string => string ); - afterEach( () => { - sinon.restore(); - mockery.deregisterAll(); - mockery.disable(); + vi.mocked( pathJoinMock ).mockImplementation( ( ...args ) => args.join( '/' ) ); + + loggerProgressMock = vi.fn(); + loggerInfoMock = vi.fn(); + loggerWarningMock = vi.fn(); + loggerErrorMock = vi.fn(); + loggerErrorMock = vi.fn(); + + vi.mocked( fsLstatMock ).mockImplementation( async () => { + return Promise.reject(); + } ); + + vi.mocked( utilsCreateLoggerMock ).mockImplementation( () => { + return { + progress: loggerProgressMock, + info: loggerInfoMock, + warning: loggerWarningMock, + error: loggerErrorMock, + _log: loggerLogMock + }; + } ); } ); it( 'should reject a promise if required properties are not specified', () => { @@ -131,35 +174,32 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - stubs.utils.verifyProperties.throws( error ); + vi.mocked( utilsVerifyPropertiesMock ).mockImplementation( () => { + throw new Error( error ); + } ); return upload( config ) .then( () => { throw new Error( 'Expected to be rejected.' ); }, - err => { - expect( err ).to.equal( error ); - - expect( stubs.utils.verifyProperties.callCount ).to.equal( 1 ); - expect( stubs.utils.verifyProperties.firstCall.args[ 0 ] ).to.deep.equal( config ); - expect( stubs.utils.verifyProperties.firstCall.args[ 1 ] ).to.deep.equal( [ - 'token', - 'organizationName', - 'projectName', - 'cwd', - 'packages' - ] ); + caughtError => { + expect( caughtError.message.endsWith( error.message ) ).toEqual( true ); + + expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledWith( + config, [ 'token', 'organizationName', 'projectName', 'cwd', 'packages' ] + ); } ); } ); - it( 'should store an error log if cannot find the project details', () => { + it( 'should store an error log if cannot find the project details', async () => { const packages = new Map( [ [ 'ckeditor5-existing-11', 'build/.transifex/ckeditor5-existing-11' ] ] ); - stubs.transifexService.getProjectData.rejects( new Error( 'Invalid auth' ) ); + vi.mocked( transifexServiceGetProjectDataMock ).mockRejectedValue( new Error( 'Invalid auth' ) ); const config = { packages, @@ -169,24 +209,25 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - return upload( config ) - .then( () => { - expect( stubs.logger.error.callCount ).to.equal( 2 ); - expect( stubs.logger.error.firstCall.args[ 0 ] ).to.equal( 'Cannot find project details for "ckeditor/ckeditor5".' ); - expect( stubs.logger.error.secondCall.args[ 0 ] ).to.equal( - 'Make sure you specified a valid auth token or an organization/project names.' - ); - - expect( stubs.transifexService.getProjectData.callCount ).to.equal( 1 ); - expect( stubs.transifexService.getProjectData.firstCall.args[ 0 ] ).to.equal( 'ckeditor' ); - expect( stubs.transifexService.getProjectData.firstCall.args[ 1 ] ).to.equal( 'ckeditor5' ); - expect( stubs.transifexService.getProjectData.firstCall.args[ 2 ] ).to.deep.equal( [ ...packages.keys() ] ); - - expect( stubs.transifexService.createResource.callCount ).to.equal( 0 ); - } ); + await upload( config ); + + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenNthCalledWith( + 1, 'Cannot find project details for "ckeditor/ckeditor5".' + ); + expect( vi.mocked( loggerErrorMock ) ).toHaveBeenNthCalledWith( + 2, 'Make sure you specified a valid auth token or an organization/project names.' + ); + + expect( vi.mocked( transifexServiceGetProjectDataMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexServiceGetProjectDataMock ) ).toHaveBeenCalledWith( + 'ckeditor', 'ckeditor5', [ ...packages.keys() ] + ); + + expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 0 ); } ); - it( 'should create a new resource if the package is processed for the first time', () => { + it( 'should create a new resource if the package is processed for the first time', async () => { const packages = new Map( [ [ 'ckeditor5-non-existing-01', 'build/.transifex/ckeditor5-non-existing-01' ] ] ); @@ -199,33 +240,29 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - stubs.transifexService.getProjectData.resolves( { - resources: [] - } ); - - stubs.transifexService.createResource.resolves(); - stubs.transifexService.createSourceFile.resolves( 'uuid-01' ); - stubs.transifexService.getResourceUploadDetails.resolves( + vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [] } ); + vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); + vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-01' ); + vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 0, 0, 0 ) ); - stubs.tools.createSpinner.returns( { - start: sinon.stub(), - finish: sinon.stub() + vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + start: vi.fn(), + finish: vi.fn() } ); - return upload( config ) - .then( () => { - expect( stubs.transifexService.createResource.callCount ).to.equal( 1 ); - expect( stubs.transifexService.createResource.firstCall.args[ 0 ] ).to.deep.equal( { - organizationName: 'ckeditor', - projectName: 'ckeditor5', - resourceName: 'ckeditor5-non-existing-01' - } ); - } ); + await upload( config ); + + expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledWith( { + organizationName: 'ckeditor', + projectName: 'ckeditor5', + resourceName: 'ckeditor5-non-existing-01' + } ); } ); - it( 'should not create a new resource if the package exists on Transifex', () => { + it( 'should not create a new resource if the package exists on Transifex', async () => { const packages = new Map( [ [ 'ckeditor5-existing-11', 'build/.transifex/ckeditor5-existing-11' ] ] ); @@ -238,30 +275,29 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - stubs.transifexService.getProjectData.resolves( { + vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [ { attributes: { name: 'ckeditor5-existing-11' } } ] } ); - stubs.transifexService.createSourceFile.resolves( 'uuid-11' ); + vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-11' ); - stubs.transifexService.getResourceUploadDetails.resolves( + vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) ); - stubs.tools.createSpinner.returns( { - start: sinon.stub(), - finish: sinon.stub() + vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + start: vi.fn(), + finish: vi.fn() } ); - return upload( config ) - .then( () => { - expect( stubs.transifexService.createResource.callCount ).to.equal( 0 ); - } ); + await upload( config ); + + expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 0 ); } ); - it( 'should send a new translation source to Transifex', () => { + it( 'should send a new translation source to Transifex', async () => { const packages = new Map( [ [ 'ckeditor5-existing-11', 'build/.transifex/ckeditor5-existing-11' ] ] ); @@ -274,43 +310,45 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - stubs.transifexService.getProjectData.resolves( { + vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [ { attributes: { name: 'ckeditor5-existing-11' } } ] } ); - stubs.transifexService.createSourceFile.resolves( 'uuid-11' ); + vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-11' ); - stubs.transifexService.getResourceUploadDetails.resolves( + vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) ); - stubs.fs.readFile.resolves( '# Example file.' ); + vi.mocked( fsReadFileMock ).mockResolvedValue( '# Example file.' ); - stubs.tools.createSpinner.returns( { - start: sinon.stub(), - finish: sinon.stub() + vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + start: vi.fn(), + finish: vi.fn() } ); - return upload( config ) - .then( () => { - expect( stubs.fs.readFile.callCount ).to.equal( 1 ); - expect( stubs.fs.readFile.firstCall.args[ 0 ] ).to.equal( '/home/ckeditor5/build/.transifex/ckeditor5-existing-11/en.pot' ); - expect( stubs.fs.readFile.firstCall.args[ 1 ] ).to.equal( 'utf-8' ); - expect( stubs.transifexService.createSourceFile.callCount ).to.equal( 1 ); - expect( stubs.transifexService.createSourceFile.firstCall.args[ 0 ] ).to.deep.equal( { - organizationName: 'ckeditor', - projectName: 'ckeditor5', - resourceName: 'ckeditor5-existing-11', - content: '# Example file.' - } ); - expect( stubs.transifexService.getResourceUploadDetails.callCount ).to.equal( 1 ); - expect( stubs.transifexService.getResourceUploadDetails.firstCall.args[ 0 ] ).to.equal( 'uuid-11' ); - } ); + await upload( config ); + + expect( vi.mocked( fsReadFileMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsReadFileMock ) ).toHaveBeenCalledWith( + '/home/ckeditor5/build/.transifex/ckeditor5-existing-11/en.pot', 'utf-8' + ); + + expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledWith( { + organizationName: 'ckeditor', + projectName: 'ckeditor5', + resourceName: 'ckeditor5-existing-11', + content: '# Example file.' + } ); + + expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledWith( 'uuid-11' ); } ); - it( 'should keep informed a developer what the script does', () => { + it( 'should keep informed a developer what the script does', async () => { const packages = new Map( [ [ 'ckeditor5-non-existing-01', 'build/.transifex/ckeditor5-non-existing-01' ] ] ); @@ -323,57 +361,55 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - stubs.transifexService.getProjectData.resolves( { + vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [] } ); - stubs.transifexService.createResource.resolves(); - stubs.transifexService.createSourceFile.resolves( 'uuid-01' ); - stubs.transifexService.getResourceUploadDetails.resolves( + vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); + vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-01' ); + vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 0, 0, 0 ) ); const packageSpinner = { - start: sinon.stub(), - finish: sinon.stub() + start: vi.fn(), + finish: vi.fn() }; const processSpinner = { - start: sinon.stub(), - finish: sinon.stub() + start: vi.fn(), + finish: vi.fn() }; - stubs.tools.createSpinner.onFirstCall().returns( packageSpinner ); - stubs.tools.createSpinner.onSecondCall().returns( processSpinner ); + vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( packageSpinner ); + vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( processSpinner ); - stubs.table.toString.returns( 'â”ģ━â”ģ' ); + vi.mocked( tableToStringMock ).mockReturnValue( 'â”ģ━â”ģ' ); - return upload( config ) - .then( () => { - expect( stubs.logger.info.callCount ).to.equal( 1 ); - expect( stubs.logger.info.getCall( 0 ).args[ 0 ] ).to.equal( 'â”ģ━â”ģ' ); - - expect( stubs.logger.progress.callCount ).to.equal( 4 ); - expect( stubs.logger.progress.getCall( 0 ).args[ 0 ] ).to.equal( 'Fetching project information...' ); - expect( stubs.logger.progress.getCall( 1 ).args[ 0 ] ).to.equal( 'Uploading new translations...' ); - expect( stubs.logger.progress.getCall( 2 ).args[ 0 ] ).to.be.undefined; - expect( stubs.logger.progress.getCall( 3 ).args[ 0 ] ).to.equal( 'Done.' ); - - expect( stubs.tools.createSpinner.callCount ).to.equal( 2 ); - - expect( stubs.tools.createSpinner.firstCall.args[ 0 ] ).to.equal( 'Processing "ckeditor5-non-existing-01"' ); - expect( stubs.tools.createSpinner.firstCall.args[ 1 ] ).to.deep.equal( { - emoji: '👉', - indentLevel: 1 - } ); - expect( stubs.tools.createSpinner.secondCall.args[ 0 ] ).to.equal( 'Collecting responses... It takes a while.' ); - - expect( packageSpinner.start.called ).to.equal( true ); - expect( packageSpinner.finish.called ).to.equal( true ); - expect( processSpinner.start.called ).to.equal( true ); - expect( processSpinner.finish.called ).to.equal( true ); - expect( stubs.chalk.gray.callCount ).to.equal( 1 ); - expect( stubs.chalk.italic.callCount ).to.equal( 1 ); - } ); + await upload( config ); + + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenCalledWith( 'â”ģ━â”ģ' ); + + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenCalledTimes( 4 ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( 1, 'Fetching project information...' ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( 2, 'Uploading new translations...' ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( 3 ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( 4, 'Done.' ); + + expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + 1, 'Processing "ckeditor5-non-existing-01"', { emoji: '👉', indentLevel: 1 } + ); + expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + 2, 'Collecting responses... It takes a while.' + ); + + expect( packageSpinner.start ).toHaveBeenCalled(); + expect( packageSpinner.finish ).toHaveBeenCalled(); + expect( processSpinner.start ).toHaveBeenCalled(); + expect( processSpinner.finish ).toHaveBeenCalled(); + expect( vi.mocked( chalkGrayMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( chalkItalicMock ) ).toHaveBeenCalledTimes( 1 ); } ); describe( 'error handling', () => { @@ -395,81 +431,97 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - stubs.fs.lstat.withArgs( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ).resolves(); + vi.mocked( fsLstatMock ).mockImplementation( async path => { + if ( path === '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ) { + return Promise.resolves(); + } - stubs.transifexService.getProjectData.resolves( { + return Promise.reject(); + } ); + + vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [] } ); - stubs.transifexService.createResource.resolves(); + vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); - // Mock Tx response when uploading a new translation content. - stubs.transifexService.createSourceFile.withArgs( { - organizationName: 'ckeditor', - projectName: 'ckeditor5', - resourceName: 'ckeditor5-non-existing-01', - content: '# ckeditor5-non-existing-01' - } ).resolves( 'uuid-01' ); - stubs.transifexService.createSourceFile.withArgs( { - organizationName: 'ckeditor', - projectName: 'ckeditor5', - resourceName: 'ckeditor5-non-existing-02', - content: '# ckeditor5-non-existing-02' - } ).resolves( 'uuid-02' ); - - // Mock translation sources. - stubs.fs.readFile.withArgs( config.cwd + '/build/.transifex/ckeditor5-non-existing-01/en.pot' ) - .resolves( '# ckeditor5-non-existing-01' ); - stubs.fs.readFile.withArgs( config.cwd + '/build/.transifex/ckeditor5-non-existing-02/en.pot' ) - .resolves( '# ckeditor5-non-existing-02' ); - - // Mock upload results. - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-01' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 3, 0, 0 ) - ); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-02' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-non-existing-02', 0, 0, 0 ) - ); + vi.mocked( transifexServiceCreateSourceFileMock ).mockImplementation( options => { + if ( options.resourceName === 'ckeditor5-non-existing-01' ) { + return Promise.resolve( 'uuid-01' ); + } - stubs.tools.createSpinner.returns( { - start: sinon.stub(), - finish: sinon.stub() + if ( options.resourceName === 'ckeditor5-non-existing-02' ) { + return Promise.resolve( 'uuid-02' ); + } + + return Promise.reject( { errors: [] } ); + } ); + + vi.mocked( fsReadFileMock ).mockImplementation( path => { + if ( path === config.cwd + '/build/.transifex/ckeditor5-non-existing-01/en.pot' ) { + return Promise.resolve( '# ckeditor5-non-existing-01' ); + } + + if ( path === config.cwd + '/build/.transifex/ckeditor5-non-existing-02/en.pot' ) { + return Promise.resolve( '# ckeditor5-non-existing-02' ); + } + + return Promise.resolve( '' ); } ); - } ); - it( 'should process packages specified in the ".transifex-failed-uploads.json" file', () => { - return upload( config ) - .then( () => { - expect( stubs.logger.warning.callCount ).to.equal( 2 ); - expect( stubs.logger.warning.firstCall.args[ 0 ] ).to.equal( - 'Found the file containing a list of packages that failed during the last script execution.' + vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockImplementation( id => { + if ( id === 'uuid-01' ) { + return Promise.resolve( + createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 3, 0, 0 ) ); - expect( stubs.logger.warning.secondCall.args[ 0 ] ).to.equal( - 'The script will process only packages listed in the file instead of all passed as "config.packages".' + } + + if ( id === 'uuid-02' ) { + return Promise.resolve( + createResourceUploadDetailsResponse( 'ckeditor5-non-existing-02', 0, 0, 0 ) ); + } + + return Promise.reject(); + } ); - expect( stubs.fs.readFile.callCount ).to.equal( 2 ); - expect( stubs.transifexService.createResource.callCount ).to.equal( 2 ); - expect( stubs.transifexService.createSourceFile.callCount ).to.equal( 2 ); - expect( stubs.transifexService.getResourceUploadDetails.callCount ).to.equal( 2 ); - } ); + vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + start: vi.fn(), + finish: vi.fn() + } ); } ); - it( 'should remove the ".transifex-failed-uploads.json" file if finished with no errors', () => { - return upload( config ) - .then( () => { - expect( stubs.fs.unlink.callCount ).to.equal( 1 ); - expect( stubs.fs.unlink.firstCall.args[ 0 ] ).to.equal( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ); - } ); + it.skip( 'should process packages specified in the ".transifex-failed-uploads.json" file', async () => { + await upload( config ); + + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 1, 'Found the file containing a list of packages that failed during the last script execution.' + ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 2, 'The script will process only packages listed in the file instead of all passed as "config.packages".' + ); + + expect( vi.mocked( fsReadFileMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledTimes( 2 ); } ); - it( 'should store an error in the ".transifex-failed-uploads.json" file (cannot create a resource)', () => { + it.skip( 'should remove the ".transifex-failed-uploads.json" file if finished with no errors', async () => { + await upload( config ); + + expect( vi.mocked( fsUnlinkMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsUnlinkMock ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ); + } ); + + it.skip( 'should store an error in the ".transifex-failed-uploads.json" file (cannot create a resource)', async () => { const firstSpinner = { - start: sinon.stub(), - finish: sinon.stub() + start: vi.fn(), + finish: vi.fn() }; - stubs.tools.createSpinner.onFirstCall().returns( firstSpinner ); + vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( firstSpinner ); const error = { message: 'JsonApiError: 409', @@ -480,44 +532,41 @@ describe( 'dev-transifex/upload()', () => { ] }; - stubs.transifexService.createResource.onFirstCall().rejects( error ); - stubs.transifexService.createResource.onSecondCall().resolves(); + vi.mocked( transifexServiceCreateResourceMock ).mockRejectedValueOnce( error ); + vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValueOnce(); - return upload( config ) - .then( () => { - expect( stubs.logger.warning.callCount ).to.equal( 5 ); - expect( stubs.logger.warning.getCall( 2 ).args[ 0 ] ).to.equal( - 'Not all translations were uploaded due to errors in Transifex API.' - ); - expect( stubs.logger.warning.getCall( 3 ).args[ 0 ] ).to.equal( - 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' - ); - expect( stubs.logger.warning.getCall( 4 ).args[ 0 ] ).to.equal( - 'Re-running the script will process only packages specified in the file.' - ); + await upload( config ); - expect( firstSpinner.finish.callCount ).to.equal( 1 ); - expect( firstSpinner.finish.firstCall.args[ 0 ] ).to.deep.equal( { emoji: '❌' } ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenCalledTimes( 5 ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 3, 'Not all translations were uploaded due to errors in Transifex API.' + ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 4, 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' + ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 5, 'Re-running the script will process only packages specified in the file.' + ); - expect( stubs.fs.writeFile.callCount ).to.equal( 1 ); - expect( stubs.fs.writeFile.firstCall.args[ 0 ] ).to.equal( - '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' - ); + expect( firstSpinner.finish ).toHaveBeenCalledTimes( 1 ); + expect( firstSpinner.finish ).toHaveBeenCalledWith( { emoji: '❌' } ); - const storedErrors = JSON.parse( stubs.fs.writeFile.firstCall.args[ 1 ] ); - expect( storedErrors ).to.deep.equal( { - 'ckeditor5-non-existing-01': [ 'Resource with this Slug and Project already exists.' ] - } ); - } ); + expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledWith( + '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', + JSON.stringify( { + 'ckeditor5-non-existing-01': [ 'Resource with this Slug and Project already exists.' ] + } ) + ); } ); - it( 'should store an error in the ".transifex-failed-uploads.json" file (cannot upload a translation)', () => { + it.skip( 'should store an error in the ".transifex-failed-uploads.json" file (cannot upload a translation)', async () => { const firstSpinner = { - start: sinon.stub(), - finish: sinon.stub() + start: vi.fn(), + finish: vi.fn() }; - stubs.tools.createSpinner.onFirstCall().returns( firstSpinner ); + vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( firstSpinner ); const error = { message: 'JsonApiError: 409', @@ -528,76 +577,74 @@ describe( 'dev-transifex/upload()', () => { ] }; - stubs.transifexService.createSourceFile.withArgs( { - organizationName: 'ckeditor', - projectName: 'ckeditor5', - resourceName: 'ckeditor5-non-existing-01', - content: '# ckeditor5-non-existing-01' - } ).rejects( error ); - - return upload( config ) - .then( () => { - expect( stubs.logger.warning.callCount ).to.equal( 5 ); - expect( stubs.logger.warning.getCall( 2 ).args[ 0 ] ).to.equal( - 'Not all translations were uploaded due to errors in Transifex API.' - ); - expect( stubs.logger.warning.getCall( 3 ).args[ 0 ] ).to.equal( - 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' - ); - expect( stubs.logger.warning.getCall( 4 ).args[ 0 ] ).to.equal( - 'Re-running the script will process only packages specified in the file.' - ); - - expect( firstSpinner.finish.callCount ).to.equal( 1 ); - expect( firstSpinner.finish.firstCall.args[ 0 ] ).to.deep.equal( { emoji: '❌' } ); + vi.mocked( transifexServiceCreateSourceFileMock ).mockImplementation( options => { + if ( options.resourceName === 'ckeditor5-non-existing-01' ) { + return Promise.reject( error ); + } - expect( stubs.fs.writeFile.callCount ).to.equal( 1 ); - expect( stubs.fs.writeFile.firstCall.args[ 0 ] ).to.equal( - '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' - ); + return Promise.resolve( 'uuid-11' ); + } ); - const storedErrors = JSON.parse( stubs.fs.writeFile.firstCall.args[ 1 ] ); - expect( storedErrors ).to.deep.equal( { - 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] - } ); - } ); - } ); + await upload( config ); - it( 'should store an error in the ".transifex-failed-uploads.json" file (cannot get a status of upload)', () => { - const error = { - message: 'JsonApiError: 409', - errors: [ - { - detail: 'Object not found. It may have been deleted or not been created yet.' - } - ] - }; - - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-01' ).rejects( error ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenCalledTimes( 5 ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 3, 'Not all translations were uploaded due to errors in Transifex API.' + ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 4, 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' + ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 5, 'Re-running the script will process only packages specified in the file.' + ); - return upload( config ) - .then( () => { - expect( stubs.logger.warning.callCount ).to.equal( 5 ); - expect( stubs.logger.warning.getCall( 2 ).args[ 0 ] ).to.equal( - 'Not all translations were uploaded due to errors in Transifex API.' - ); - expect( stubs.logger.warning.getCall( 3 ).args[ 0 ] ).to.equal( - 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' - ); - expect( stubs.logger.warning.getCall( 4 ).args[ 0 ] ).to.equal( - 'Re-running the script will process only packages specified in the file.' - ); + expect( firstSpinner.finish ).toHaveBeenCalledTimes( 1 ); + expect( firstSpinner.finish ).toHaveBeenCalledWith( { emoji: '❌' } ); - expect( stubs.fs.writeFile.callCount ).to.equal( 1 ); - expect( stubs.fs.writeFile.firstCall.args[ 0 ] ).to.equal( - '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' - ); + expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledWith( + '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', + JSON.stringify( { + 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] + } ) + ); + } ); - const storedErrors = JSON.parse( stubs.fs.writeFile.firstCall.args[ 1 ] ); - expect( storedErrors ).to.deep.equal( { - 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] - } ); - } ); + it.skip( 'should store an error in the ".transifex-failed-uploads.json" file (cannot get a status of upload)', async () => { + // const error = { + // message: 'JsonApiError: 409', + // errors: [ + // { + // detail: 'Object not found. It may have been deleted or not been created yet.' + // } + // ] + // }; + + // TODO + // stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-01' ).rejects( error ); + + await upload( config ); + + // expect( stubs.logger.warning.callCount ).to.equal( 5 ); + // expect( stubs.logger.warning.getCall( 2 ).args[ 0 ] ).to.equal( + // 'Not all translations were uploaded due to errors in Transifex API.' + // ); + // expect( stubs.logger.warning.getCall( 3 ).args[ 0 ] ).to.equal( + // 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' + // ); + // expect( stubs.logger.warning.getCall( 4 ).args[ 0 ] ).to.equal( + // 'Re-running the script will process only packages specified in the file.' + // ); + + // expect( stubs.fs.writeFile.callCount ).to.equal( 1 ); + // expect( stubs.fs.writeFile.firstCall.args[ 0 ] ).to.equal( + // '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' + // ); + + // const storedErrors = JSON.parse( stubs.fs.writeFile.firstCall.args[ 1 ] ); + // expect( storedErrors ).to.deep.equal( { + // 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] + // } ); } ); } ); @@ -623,23 +670,12 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - for ( const [ packageName, packagePath ] of packages ) { - // Mock translation files. - stubs.fs.readFile.withArgs( config.cwd + '/' + packagePath + '/en.pot' ).resolves( '# ' + packageName ); - - // Mock Tx response when uploading a new translation content. - const uuid = 'uuid-' + packageName.match( /(\d+)$/ )[ 1 ]; - const withArgs = { - organizationName: 'ckeditor', - projectName: 'ckeditor5', - resourceName: packageName, - content: '# ' + packageName - }; - stubs.transifexService.createSourceFile.withArgs( withArgs ).resolves( uuid ); - } + vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-xx' ); + + vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-xx' ); // Mock resources on Transifex. - stubs.transifexService.getProjectData.resolves( { + vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [ { attributes: { name: 'ckeditor5-existing-11' } }, { attributes: { name: 'ckeditor5-existing-12' } }, @@ -648,77 +684,58 @@ describe( 'dev-transifex/upload()', () => { ] } ); - stubs.transifexService.createResource.resolves(); + vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-01' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 3, 0, 0 ) - ); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-02' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-non-existing-02', 0, 0, 0 ) - ); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-03' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-non-existing-03', 1, 0, 0 ) - ); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-11' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) - ); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-12' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-existing-12', 0, 1, 1 ) - ); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-13' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-existing-13', 2, 0, 0 ) - ); - stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-14' ).resolves( - createResourceUploadDetailsResponse( 'ckeditor5-existing-14', 0, 0, 0 ) - ); + vi.mocked( transifexServiceGetResourceUploadDetailsMock ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 3, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-02', 0, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-03', 1, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-12', 0, 1, 1 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-13', 2, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-14', 0, 0, 0 ) ); - stubs.tools.createSpinner.returns( { - start: sinon.stub(), - finish: sinon.stub() + vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + start: vi.fn(), + finish: vi.fn() } ); } ); - it( 'should handle all packages', () => { - return upload( config ) - .then( () => { - expect( stubs.transifexService.getProjectData.callCount ).to.equal( 1 ); - expect( stubs.transifexService.createResource.callCount ).to.equal( 3 ); - expect( stubs.transifexService.createSourceFile.callCount ).to.equal( 7 ); - expect( stubs.transifexService.getResourceUploadDetails.callCount ).to.equal( 7 ); - expect( stubs.tools.createSpinner.callCount ).to.equal( 8 ); - } ); + it( 'should handle all packages', async () => { + await upload( config ); + + expect( vi.mocked( transifexServiceGetProjectDataMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledTimes( 7 ); + expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledTimes( 7 ); + expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenCalledTimes( 8 ); } ); - it( 'should display a summary table with sorted packages (new, has changes, A-Z)', () => { - return upload( config ) - .then( () => { - expect( stubs.table.push.callCount ).to.equal( 1 ); - expect( stubs.table.push.firstCall.args ).to.be.an( 'array' ); - expect( stubs.table.push.firstCall.args ).to.lengthOf( 7 ); - - // 1x for printing "It takes a while", - // 5x for each column, x2 for each resource. - expect( stubs.chalk.gray.callCount ).to.equal( 11 ); - - expect( stubs.table.push.firstCall.args ).to.deep.equal( [ - [ 'ckeditor5-non-existing-01', '🆕', '3', '0', '0' ], - [ 'ckeditor5-non-existing-03', '🆕', '1', '0', '0' ], - [ 'ckeditor5-non-existing-02', '🆕', '0', '0', '0' ], - [ 'ckeditor5-existing-12', '', '0', '1', '1' ], - [ 'ckeditor5-existing-13', '', '2', '0', '0' ], - [ 'ckeditor5-existing-11', '', '0', '0', '0' ], - [ 'ckeditor5-existing-14', '', '0', '0', '0' ] - ] ); - } ); + it( 'should display a summary table with sorted packages (new, has changes, A-Z)', async () => { + await upload( config ); + + expect( vi.mocked( tablePushMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( tablePushMock ) ).toHaveBeenCalledWith( + [ 'ckeditor5-non-existing-01', '🆕', '3', '0', '0' ], + [ 'ckeditor5-non-existing-03', '🆕', '1', '0', '0' ], + [ 'ckeditor5-non-existing-02', '🆕', '0', '0', '0' ], + [ 'ckeditor5-existing-12', '', '0', '1', '1' ], + [ 'ckeditor5-existing-13', '', '2', '0', '0' ], + [ 'ckeditor5-existing-11', '', '0', '0', '0' ], + [ 'ckeditor5-existing-14', '', '0', '0', '0' ] + ); + + // 1x for printing "It takes a while", + // 5x for each column, x2 for each resource. + expect( vi.mocked( chalkGrayMock ) ).toHaveBeenCalledTimes( 11 ); } ); - it( 'should not display a summary table if none of the packages were processed', () => { + it( 'should not display a summary table if none of the packages were processed', async () => { config.packages = new Map(); - return upload( config ) - .then( () => { - expect( stubs.table.push.callCount ).to.equal( 0 ); - } ); + await upload( config ); + + expect( vi.mocked( tablePushMock ) ).toHaveBeenCalledTimes( 0 ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-transifex/tests/utils.js b/packages/ckeditor5-dev-transifex/tests/utils.js index e36943da7..b0a832461 100644 --- a/packages/ckeditor5-dev-transifex/tests/utils.js +++ b/packages/ckeditor5-dev-transifex/tests/utils.js @@ -3,47 +3,54 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const sinon = require( 'sinon' ); -const chai = require( 'chai' ); -const expect = chai.expect; -const mockery = require( 'mockery' ); - -describe( 'dev-transifex/utils', () => { - let stubs, utils; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - chalk: { - cyan: sinon.stub().callsFake( msg => msg ) - }, - logger: sinon.stub().returns( { - info: sinon.stub(), - warning: sinon.stub(), - error: sinon.stub(), - _log: sinon.stub() - } ) - }; +import { describe, expect, it, vi, beforeEach } from 'vitest'; +// import utils from '../lib/utils.js'; + +const { + chalkCyanMock, + loggerMock +} = vi.hoisted( () => { + return { + chalkCyanMock: vi.fn(), + loggerMock: vi.fn() + }; +} ); - mockery.registerMock( 'chalk', stubs.chalk ); - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - logger: stubs.logger - } ); +vi.mock( 'chalk', () => { + return { + default: { + cyan: chalkCyanMock + } + }; +} ); - utils = require( '../lib/utils' ); - } ); +vi.mock( '@ckeditor/ckeditor5-dev-utils', () => { + return { + logger: loggerMock + }; +} ); - afterEach( () => { - sinon.restore(); - mockery.deregisterAll(); - mockery.disable(); +describe( 'dev-transifex/utils', () => { + let utils; + let loggerInfoMock, loggerWarningMock, loggerErrorMock, loggerLogMock; + + beforeEach( async () => { + loggerInfoMock = vi.fn(); + loggerWarningMock = vi.fn(); + loggerErrorMock = vi.fn(); + loggerLogMock = vi.fn(); + + vi.mocked( chalkCyanMock ).mockImplementation( str => str ); + vi.mocked( loggerMock ).mockImplementation( () => { + return { + info: loggerInfoMock, + warning: loggerWarningMock, + error: loggerErrorMock, + _log: loggerLogMock + }; + } ); + + utils = ( await import( '../lib/utils.js' ) ).default; } ); describe( 'verifyProperties()', () => { @@ -136,18 +143,18 @@ describe( 'dev-transifex/utils', () => { describe( 'createLogger()', () => { it( 'should be a function', () => { - expect( utils.createLogger ).to.be.a( 'function' ); + expect( utils.createLogger ).toBeInstanceOf( Function ); } ); it( 'should return an object with methods', () => { const logger = utils.createLogger(); - expect( logger ).to.be.an( 'object' ); - expect( logger.progress ).to.be.a( 'function' ); - expect( logger.info ).to.be.a( 'function' ); - expect( logger.warning ).to.be.a( 'function' ); - expect( logger.error ).to.be.a( 'function' ); - expect( logger._log ).to.be.a( 'function' ); + expect( logger ).toBeInstanceOf( Object ); + expect( logger.progress ).toBeInstanceOf( Function ); + expect( logger.info ).toBeInstanceOf( Function ); + expect( logger.warning ).toBeInstanceOf( Function ); + expect( logger.error ).toBeInstanceOf( Function ); + expect( logger._log ).toBeInstanceOf( Function ); } ); it( 'should call the info method for a non-empty progress message', () => { @@ -155,10 +162,10 @@ describe( 'dev-transifex/utils', () => { logger.progress( 'Example step.' ); - expect( logger.info.callCount ).to.equal( 1 ); - expect( logger.info.firstCall.args[ 0 ] ).to.equal( '\n📍 Example step.' ); - expect( stubs.chalk.cyan.callCount ).to.equal( 1 ); - expect( stubs.chalk.cyan.firstCall.args[ 0 ] ).to.equal( 'Example step.' ); + expect( loggerInfoMock ).toHaveBeenCalledTimes( 1 ); + expect( loggerInfoMock ).toHaveBeenCalledWith( '\n📍 Example step.' ); + expect( chalkCyanMock ).toHaveBeenCalledTimes( 1 ); + expect( chalkCyanMock ).toHaveBeenCalledWith( 'Example step.' ); } ); it( 'should call the info method with an empty message for an empty progress message', () => { @@ -166,9 +173,9 @@ describe( 'dev-transifex/utils', () => { logger.progress(); - expect( logger.info.callCount ).to.equal( 1 ); - expect( logger.info.firstCall.args[ 0 ] ).to.equal( '' ); - expect( stubs.chalk.cyan.called ).to.equal( false ); + expect( loggerInfoMock ).toHaveBeenCalledTimes( 1 ); + expect( loggerInfoMock ).toHaveBeenCalledWith( '' ); + expect( chalkCyanMock ).toHaveBeenCalledTimes( 0 ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-transifex/vitest.config.js b/packages/ckeditor5-dev-transifex/vitest.config.js new file mode 100644 index 000000000..5ad784a28 --- /dev/null +++ b/packages/ckeditor5-dev-transifex/vitest.config.js @@ -0,0 +1,23 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + testTimeout: 10000, + restoreMocks: true, + include: [ + 'tests/**/*.js' + ], + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From 54ba4fa8747bbf28387b247f1135482272a94063 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 12 Sep 2024 08:57:29 +0200 Subject: [PATCH 082/172] Fixed issues with missing named exports from packages. --- .../ckeditor5-dev-release-tools/lib/tasks/commitandtag.js | 4 +++- .../ckeditor5-dev-release-tools/lib/tasks/updateversions.js | 6 ++++-- .../lib/utils/transformcommitfactory.js | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js index b6c32ac1f..e3a67a5ca 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js @@ -3,11 +3,13 @@ * For licensing, see LICENSE.md. */ +import upath from 'upath'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import { toUnix } from 'upath'; import { glob } from 'glob'; import shellEscape from 'shell-escape'; +const { toUnix } = upath; + /** * Creates a commit and a tag for specified version. * diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js index 30066e4a4..570c5e892 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js @@ -3,12 +3,14 @@ * For licensing, see LICENSE.md. */ -import { glob } from 'glob'; +import upath from 'upath'; import fs from 'fs-extra'; +import { glob } from 'glob'; import semver from 'semver'; -import { normalizeTrim, toUnix, dirname, join } from 'upath'; import checkVersionAvailability from '../utils/checkversionavailability.js'; +const { normalizeTrim, toUnix, dirname, join } = upath; + /** * The purpose of the script is to update the version of a root package found in the current working * directory and packages if the `options.packagesDirectory` path is provided. diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js index 56e8b256c..075503ebb 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js @@ -3,10 +3,12 @@ * For licensing, see LICENSE.md. */ -import { cloneDeepWith } from 'lodash'; +import lodash from 'lodash'; import * as utils from './transformcommitutils.js'; import getChangedFilesForCommit from './getchangedfilesforcommit.js'; +const { cloneDeepWith } = lodash; + // Squash commit follows the pattern: "A pull request title (#{number})". const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; From d1ab850e461750a392e7df1278018d9ea5a86632 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 12 Sep 2024 13:40:22 +0200 Subject: [PATCH 083/172] Extracted all utils to separate modules. --- packages/ckeditor5-dev-utils/lib/index.js | 10 +- .../lib/{logger.js => logger/index.js} | 0 packages/ckeditor5-dev-utils/lib/stream.js | 80 --- .../ckeditor5-dev-utils/lib/stream/index.js | 6 + .../ckeditor5-dev-utils/lib/stream/noop.js | 29 ++ .../lib/styles/themeimporter.js | 2 +- packages/ckeditor5-dev-utils/lib/tools.js | 115 ----- .../lib/tools/createspinner.js | 6 +- .../lib/tools/getdirectories.js | 27 ++ .../ckeditor5-dev-utils/lib/tools/index.js | 9 + .../ckeditor5-dev-utils/lib/tools/shexec.js | 85 ++++ .../lib/tools/updatejsonfile.js | 21 + packages/ckeditor5-dev-utils/tests/index.js | 66 +++ .../tests/loaders/getcoverageloader.js | 15 +- .../tests/loaders/getdebugloader.js | 14 +- .../tests/loaders/getformattedtextloader.js | 8 +- .../tests/loaders/geticonsloader.js | 10 +- .../tests/loaders/getjavascriptloader.js | 22 +- .../tests/loaders/getstylesloader.js | 45 +- .../tests/loaders/gettypescriptloader.js | 32 +- packages/ckeditor5-dev-utils/tests/logger.js | 201 -------- .../ckeditor5-dev-utils/tests/logger/index.js | 155 ++++++ packages/ckeditor5-dev-utils/tests/stream.js | 116 ----- .../ckeditor5-dev-utils/tests/stream/index.js | 19 + .../ckeditor5-dev-utils/tests/stream/noop.js | 78 +++ .../tests/styles/getpostcssconfig.js | 118 ++--- .../ckeditor5-dev-utils/tests/styles/index.js | 29 +- .../tests/styles/utils/getpackagename.js | 117 +++-- packages/ckeditor5-dev-utils/tests/tools.js | 258 ---------- .../tests/tools/createspinner.js | 454 +++++++----------- .../tests/tools/getdirectories.js | 34 ++ .../ckeditor5-dev-utils/tests/tools/index.js | 51 ++ .../ckeditor5-dev-utils/tests/tools/shexec.js | 171 +++++++ .../tests/tools/updatejsonfile.js | 30 ++ 34 files changed, 1174 insertions(+), 1259 deletions(-) rename packages/ckeditor5-dev-utils/lib/{logger.js => logger/index.js} (100%) delete mode 100644 packages/ckeditor5-dev-utils/lib/stream.js create mode 100644 packages/ckeditor5-dev-utils/lib/stream/index.js create mode 100644 packages/ckeditor5-dev-utils/lib/stream/noop.js delete mode 100644 packages/ckeditor5-dev-utils/lib/tools.js create mode 100644 packages/ckeditor5-dev-utils/lib/tools/getdirectories.js create mode 100644 packages/ckeditor5-dev-utils/lib/tools/index.js create mode 100644 packages/ckeditor5-dev-utils/lib/tools/shexec.js create mode 100644 packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js create mode 100644 packages/ckeditor5-dev-utils/tests/index.js delete mode 100644 packages/ckeditor5-dev-utils/tests/logger.js create mode 100644 packages/ckeditor5-dev-utils/tests/logger/index.js delete mode 100644 packages/ckeditor5-dev-utils/tests/stream.js create mode 100644 packages/ckeditor5-dev-utils/tests/stream/index.js create mode 100644 packages/ckeditor5-dev-utils/tests/stream/noop.js delete mode 100644 packages/ckeditor5-dev-utils/tests/tools.js create mode 100644 packages/ckeditor5-dev-utils/tests/tools/getdirectories.js create mode 100644 packages/ckeditor5-dev-utils/tests/tools/index.js create mode 100644 packages/ckeditor5-dev-utils/tests/tools/shexec.js create mode 100644 packages/ckeditor5-dev-utils/tests/tools/updatejsonfile.js diff --git a/packages/ckeditor5-dev-utils/lib/index.js b/packages/ckeditor5-dev-utils/lib/index.js index 636073812..2e1f29838 100644 --- a/packages/ckeditor5-dev-utils/lib/index.js +++ b/packages/ckeditor5-dev-utils/lib/index.js @@ -3,10 +3,10 @@ * For licensing, see LICENSE.md. */ -export * as logger from './logger.js'; -export * as tools from './tools.js'; -export * as loaders from './loaders/index.js'; -export * as stream from './stream.js'; -export * as bundler from './bundler/index.js'; export * as builds from './builds/index.js'; +export * as bundler from './bundler/index.js'; +export * as loaders from './loaders/index.js'; +export * as logger from './logger/index.js'; +export * as stream from './stream/index.js'; export * as styles from './styles/index.js'; +export * as tools from './tools/index.js'; diff --git a/packages/ckeditor5-dev-utils/lib/logger.js b/packages/ckeditor5-dev-utils/lib/logger/index.js similarity index 100% rename from packages/ckeditor5-dev-utils/lib/logger.js rename to packages/ckeditor5-dev-utils/lib/logger/index.js diff --git a/packages/ckeditor5-dev-utils/lib/stream.js b/packages/ckeditor5-dev-utils/lib/stream.js deleted file mode 100644 index 1c81cd8e2..000000000 --- a/packages/ckeditor5-dev-utils/lib/stream.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import path from 'path'; -import { PassThrough } from 'stream'; -import through from 'through2'; - -const stream = { - /** - * Creates a simple duplex stream. - * - * @param {Function} [callback] A callback which will be executed with each chunk. - * The callback can return a Promise to perform async actions before other chunks are accepted. - * @returns {Stream} - */ - noop( callback ) { - if ( !callback ) { - return new PassThrough( { objectMode: true } ); - } - - return through( { objectMode: true }, ( chunk, encoding, throughCallback ) => { - const callbackResult = callback( chunk ); - - if ( callbackResult instanceof Promise ) { - callbackResult - .then( () => { - throughCallback( null, chunk ); - } ) - .catch( err => { - throughCallback( err ); - } ); - } else { - throughCallback( null, chunk ); - } - } ); - }, - - /** - * Checks whether a file is a test file. - * - * @param {Vinyl} file - * @returns {Boolean} - */ - isTestFile( file ) { - // TODO this should be based on bender configuration (config.tests.*.paths). - if ( !file.relative.startsWith( 'tests' + path.sep ) ) { - return false; - } - - const dirFrags = file.relative.split( path.sep ); - - return !dirFrags.some( dirFrag => { - return dirFrag.startsWith( '_' ) && dirFrag != '_utils-tests'; - } ); - }, - - /** - * Checks whether a file is a source file. - * - * @param {Vinyl} file - * @returns {Boolean} - */ - isSourceFile( file ) { - return !stream.isTestFile( file ); - }, - - /** - * Checks whether a file is a JS file. - * - * @param {Vinyl} file - * @returns {Boolean} - */ - isJSFile( file ) { - return file.path.endsWith( '.js' ); - } -}; - -export default stream; diff --git a/packages/ckeditor5-dev-utils/lib/stream/index.js b/packages/ckeditor5-dev-utils/lib/stream/index.js new file mode 100644 index 000000000..e843be3ef --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/stream/index.js @@ -0,0 +1,6 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +export { default as noop } from './noop.js'; diff --git a/packages/ckeditor5-dev-utils/lib/stream/noop.js b/packages/ckeditor5-dev-utils/lib/stream/noop.js new file mode 100644 index 000000000..8388a51fd --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/stream/noop.js @@ -0,0 +1,29 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { PassThrough } from 'stream'; +import through from 'through2'; + +export default function noop( callback ) { + if ( !callback ) { + return new PassThrough( { objectMode: true } ); + } + + return through( { objectMode: true }, ( chunk, encoding, throughCallback ) => { + const callbackResult = callback( chunk ); + + if ( callbackResult instanceof Promise ) { + callbackResult + .then( () => { + throughCallback( null, chunk ); + } ) + .catch( err => { + throughCallback( err ); + } ); + } else { + throughCallback( null, chunk ); + } + } ); +} diff --git a/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js b/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js index 9c791f94c..ffc9539ad 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js +++ b/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js @@ -10,7 +10,7 @@ import path from 'path'; import postcss from 'postcss'; import postCssImport from 'postcss-import'; import chalk from 'chalk'; -import logger from '../logger.js'; +import logger from '../logger/index.js'; import themeLogger from './themelogger.js'; import getPackageName from './utils/getpackagename.js'; diff --git a/packages/ckeditor5-dev-utils/lib/tools.js b/packages/ckeditor5-dev-utils/lib/tools.js deleted file mode 100644 index 67b0261f5..000000000 --- a/packages/ckeditor5-dev-utils/lib/tools.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import chalk from 'chalk'; -import logger from './logger.js'; -import sh from 'shelljs'; -import fs from 'fs'; -import pth from 'path'; - -export { default as createSpinner } from './tools/createspinner.js'; - -/** - * Executes a shell command. - * - * @param {String} command The command to be executed. - * @param {Object} options - * @param {'info'|'warning'|'error'|'silent'} [options.verbosity='info'] Level of the verbosity. If set as 'info' - * both outputs (stdout and stderr) will be logged. If set as 'error', only stderr output will be logged. - * @param {String} [options.cwd=process.cwd()] - * @param {Boolean} [options.async=false] If set, the command execution is asynchronous. The execution is synchronous by default. - * @returns {String|Promise.} The command output. - */ -export function shExec( command, options = {} ) { - const { - verbosity = 'info', - cwd = process.cwd(), - async = false - } = options; - - const log = logger( verbosity ); - - sh.config.silent = true; - - const execOptions = { cwd }; - - if ( async ) { - return new Promise( ( resolve, reject ) => { - sh.exec( command, execOptions, ( code, stdout, stderr ) => { - try { - const result = execHandler( code, stdout, stderr ); - - resolve( result ); - } catch ( err ) { - reject( err ); - } - } ); - } ); - } - - const { code, stdout, stderr } = sh.exec( command, execOptions ); - - return execHandler( code, stdout, stderr ); - - function execHandler( code, stdout, stderr ) { - const grey = chalk.grey; - - if ( code ) { - if ( stdout ) { - log.error( grey( stdout ) ); - } - - if ( stderr ) { - log.error( grey( stderr ) ); - } - - throw new Error( `Error while executing ${ command }: ${ stderr }` ); - } - - if ( stdout ) { - log.info( grey( stdout ) ); - } - - if ( stderr ) { - log.info( grey( stderr ) ); - } - - return stdout; - } -} - -/** - * Returns array with all directories under specified path. - * - * @param {String} path - * @returns {Array} - */ -export function getDirectories( path ) { - const isDirectory = path => { - try { - return fs.statSync( path ).isDirectory(); - } catch ( e ) { - return false; - } - }; - - return fs.readdirSync( path ).filter( item => { - return isDirectory( pth.join( path, item ) ); - } ); -} - -/** - * Updates JSON file under specified path. - * @param {String} path Path to file on disk. - * @param {Function} updateFunction Function that will be called with parsed JSON object. It should return - * modified JSON object to save. - */ -export function updateJSONFile( path, updateFunction ) { - const contents = fs.readFileSync( path, 'utf-8' ); - let json = JSON.parse( contents ); - json = updateFunction( json ); - - fs.writeFileSync( path, JSON.stringify( json, null, 2 ) + '\n', 'utf-8' ); -} diff --git a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js index b025260e9..b69118c9c 100644 --- a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js +++ b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js @@ -6,7 +6,7 @@ import readline from 'readline'; import isInteractive from 'is-interactive'; import cliSpinners from 'cli-spinners'; -import cliCursor from 'cli-cursor'; +import { show as cliCursorShow, hide as cliCursorHide } from 'cli-cursor'; // A size of default indent for a log. const INDENT_SIZE = 3; @@ -64,7 +64,7 @@ export default function createSpinner( title, options = {} ) { let index = 0; let shouldClearLastLine = false; - cliCursor.hide(); + cliCursorHide(); timerId = setInterval( () => { if ( index === frames.length ) { @@ -102,7 +102,7 @@ export default function createSpinner( title, options = {} ) { clearLastLine(); } - cliCursor.show(); + cliCursorShow(); console.log( `${ indent }${ finishEmoji } ${ title }` ); } }; diff --git a/packages/ckeditor5-dev-utils/lib/tools/getdirectories.js b/packages/ckeditor5-dev-utils/lib/tools/getdirectories.js new file mode 100644 index 000000000..0400abd7a --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/tools/getdirectories.js @@ -0,0 +1,27 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; +import path from 'path'; + +/** + * Returns array with all directories under specified path. + * + * @param {String} directoryPath + * @returns {Array} + */ +export default function getDirectories( directoryPath ) { + const isDirectory = path => { + try { + return fs.statSync( path ).isDirectory(); + } catch ( e ) { + return false; + } + }; + + return fs.readdirSync( directoryPath ).filter( item => { + return isDirectory( path.join( directoryPath, item ) ); + } ); +} diff --git a/packages/ckeditor5-dev-utils/lib/tools/index.js b/packages/ckeditor5-dev-utils/lib/tools/index.js new file mode 100644 index 000000000..311ade2d7 --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/tools/index.js @@ -0,0 +1,9 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +export { default as createSpinner } from './createspinner.js'; +export { default as getDirectories } from './getdirectories.js'; +export { default as shExec } from './shexec.js'; +export { default as updateJSONFile } from './updatejsonfile.js'; diff --git a/packages/ckeditor5-dev-utils/lib/tools/shexec.js b/packages/ckeditor5-dev-utils/lib/tools/shexec.js new file mode 100644 index 000000000..d11058125 --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/tools/shexec.js @@ -0,0 +1,85 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import chalk from 'chalk'; +import sh from 'shelljs'; +import logger from '../logger/index.js'; + +/** + * Executes a shell command. + * + * @param {String} command The command to be executed. + * @param {Object} options + * @param {'info'|'warning'|'error'|'silent'} [options.verbosity='info'] Level of the verbosity. If set as 'info' + * both outputs (stdout and stderr) will be logged. If set as 'error', only stderr output will be logged. + * @param {String} [options.cwd=process.cwd()] + * @param {Boolean} [options.async=false] If set, the command execution is asynchronous. The execution is synchronous by default. + * @returns {String|Promise.} The command output. + */ +export default function shExec( command, options = {} ) { + const { + verbosity = 'info', + cwd = process.cwd(), + async = false + } = options; + + sh.config.silent = true; + + const execOptions = { cwd }; + + if ( async ) { + return new Promise( ( resolve, reject ) => { + sh.exec( command, execOptions, ( code, stdout, stderr ) => { + try { + const result = execHandler( { code, stdout, stderr, verbosity, command } ); + + resolve( result ); + } catch ( err ) { + reject( err ); + } + } ); + } ); + } + + const { code, stdout, stderr } = sh.exec( command, execOptions ); + + return execHandler( { code, stdout, stderr, verbosity, command } ); +} + +/** + * @param {Object} options + * @param {Number} options.code + * @param {String} options.stdout + * @param {String} options.stderr + * @param {'info'|'warning'|'error'|'silent'} options.verbosity + * @param {String} options.command + * @returns {String} + */ +function execHandler( { code, stdout, stderr, verbosity, command } ) { + const log = logger( verbosity ); + const grey = chalk.grey; + + if ( code ) { + if ( stdout ) { + log.error( grey( stdout ) ); + } + + if ( stderr ) { + log.error( grey( stderr ) ); + } + + throw new Error( `Error while executing ${ command }: ${ stderr }` ); + } + + if ( stdout ) { + log.info( grey( stdout ) ); + } + + if ( stderr ) { + log.info( grey( stderr ) ); + } + + return stdout; +} diff --git a/packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js b/packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js new file mode 100644 index 000000000..152b533ba --- /dev/null +++ b/packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js @@ -0,0 +1,21 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; + +/** + * Updates JSON file under specified path. + * + * @param {String} filePath Path to file on disk. + * @param {Function} updateFunction Function that will be called with parsed JSON object. It should return + * modified JSON object to save. + */ +export default function updateJSONFile( filePath, updateFunction ) { + const contents = fs.readFileSync( filePath, 'utf-8' ); + let json = JSON.parse( contents ); + json = updateFunction( json ); + + fs.writeFileSync( filePath, JSON.stringify( json, null, 2 ) + '\n', 'utf-8' ); +} diff --git a/packages/ckeditor5-dev-utils/tests/index.js b/packages/ckeditor5-dev-utils/tests/index.js new file mode 100644 index 000000000..00eea34bf --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/index.js @@ -0,0 +1,66 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import * as packageUtils from '../lib/index.js'; +import * as bundler from '../lib/bundler/index.js'; +import * as loaders from '../lib/loaders/index.js'; +import * as logger from '../lib/logger/index.js'; +import * as builds from '../lib/builds/index.js'; +import * as stream from '../lib/stream/index.js'; +import * as styles from '../lib/styles/index.js'; +import * as tools from '../lib/tools/index.js'; + +vi.mock( '../lib/builds/index.js' ); +vi.mock( '../lib/bundler/index.js' ); +vi.mock( '../lib/loaders/index.js' ); +vi.mock( '../lib/logger/index.js' ); +vi.mock( '../lib/stream/index.js' ); +vi.mock( '../lib/styles/index.js' ); +vi.mock( '../lib/tools/index.js' ); + +describe( 'index.js', () => { + describe( '#builds', () => { + it( 'should be a function', () => { + expect( packageUtils.builds ).to.equal( builds ); + } ); + } ); + + describe( '#bundler', () => { + it( 'should be a function', () => { + expect( packageUtils.bundler ).to.equal( bundler ); + } ); + } ); + + describe( '#loaders', () => { + it( 'should be a function', () => { + expect( packageUtils.loaders ).to.equal( loaders ); + } ); + } ); + + describe( '#logger', () => { + it( 'should be a function', () => { + expect( packageUtils.logger ).to.equal( logger ); + } ); + } ); + + describe( '#stream', () => { + it( 'should be a function', () => { + expect( packageUtils.stream ).to.equal( stream ); + } ); + } ); + + describe( '#styles', () => { + it( 'should be a function', () => { + expect( packageUtils.styles ).to.equal( styles ); + } ); + } ); + + describe( '#tools', () => { + it( 'should be a function', () => { + expect( packageUtils.tools ).to.equal( tools ); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js index e72d2d3c7..a6b4c3be3 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/getcoverageloader.js @@ -3,16 +3,19 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi } from 'vitest'; +import path from 'path'; +import { describe, expect, it } from 'vitest'; +import getCoverageLoader from '../../lib/loaders/getcoverageloader.js'; +const escapedPathSep = path.sep == '/' ? '/' : '\\\\'; describe( 'getCoverageLoader()', () => { it( 'should be a function', () => { - expect( loaders.getCoverageLoader ).to.be.a( 'function' ); + expect( getCoverageLoader ).to.be.a( 'function' ); } ); it( 'should return a definition containing a loader for measuring the coverage', () => { - const coverageLoader = loaders.getCoverageLoader( { + const coverageLoader = getCoverageLoader( { files: [] } ); @@ -34,7 +37,7 @@ describe( 'getCoverageLoader()', () => { } ); it( 'should return a definition containing a loader for measuring the coverage (include glob check)', () => { - const coverageLoader = loaders.getCoverageLoader( { + const coverageLoader = getCoverageLoader( { files: [ // -f utils [ 'node_modules/ckeditor5-utils/tests/**/*.js' ] @@ -50,7 +53,7 @@ describe( 'getCoverageLoader()', () => { } ); it( 'should return a definition containing a loader for measuring the coverage (exclude glob check)', () => { - const coverageLoader = loaders.getCoverageLoader( { + const coverageLoader = getCoverageLoader( { files: [ // -f !utils [ 'node_modules/ckeditor5-!(utils)/tests/**/*.js' ] @@ -66,7 +69,7 @@ describe( 'getCoverageLoader()', () => { } ); it( 'should return a definition containing a loader for measuring the coverage (for root named ckeditor5-*)', () => { - const coverageLoader = loaders.getCoverageLoader( { + const coverageLoader = getCoverageLoader( { files: [ [ '/ckeditor5-collab/packages/ckeditor5-alignment/tests/**/*.{js,ts}' ] ] diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js index 215111f67..7f4a1bea4 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/getdebugloader.js @@ -3,14 +3,22 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; +import getDebugLoader from '../../lib/loaders/getdebugloader.js'; -describe( 'getCoverageLoader()', () => { +describe( 'getDebugLoader()', () => { it( 'should be a function', () => { - expect( loaders.getCoverageLoader ).to.be.a( 'function' ); + expect( getDebugLoader ).to.be.a( 'function' ); } ); it( 'should return a definition containing a loader for measuring the coverage', () => { + const loader = getDebugLoader( [ 'CK_DEBUG_ENGINE' ] ); + expect( loader ).toEqual( { + loader: expect.stringMatching( /ck-debug-loader\.js$/ ), + options: { + debugFlags: [ 'CK_DEBUG_ENGINE' ] + } + } ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js index 60888435b..2defc69d5 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/getformattedtextloader.js @@ -3,16 +3,16 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi } from 'vitest'; - +import { describe, expect, it } from 'vitest'; +import getFormattedTextLoader from '../../lib/loaders/getformattedtextloader.js'; describe( 'getFormattedTextLoader()', () => { it( 'should be a function', () => { - expect( loaders.getFormattedTextLoader ).to.be.a( 'function' ); + expect( getFormattedTextLoader ).to.be.a( 'function' ); } ); it( 'should return a definition accepting files that store readable content', () => { - const textLoader = loaders.getFormattedTextLoader(); + const textLoader = getFormattedTextLoader(); expect( textLoader ).to.be.an( 'object' ); expect( textLoader ).to.have.property( 'use' ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js b/packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js index 782c78b82..157475454 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/geticonsloader.js @@ -3,16 +3,16 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi } from 'vitest'; - +import { describe, expect, it } from 'vitest'; +import getIconsLoader from '../../lib/loaders/geticonsloader.js'; describe( 'getIconsLoader()', () => { it( 'should be a function', () => { - expect( loaders.getIconsLoader ).to.be.a( 'function' ); + expect( getIconsLoader ).to.be.a( 'function' ); } ); it( 'should return a definition loading the svg files properly (a full CKEditor 5 icon path check)', () => { - const svgLoader = loaders.getIconsLoader(); + const svgLoader = getIconsLoader(); expect( svgLoader ).to.be.an( 'object' ); expect( svgLoader ).to.have.property( 'use' ); @@ -26,7 +26,7 @@ describe( 'getIconsLoader()', () => { } ); it( 'should return a definition loading the svg files properly (accept any svg file)', () => { - const svgLoader = loaders.getIconsLoader( { matchExtensionOnly: true } ); + const svgLoader = getIconsLoader( { matchExtensionOnly: true } ); expect( svgLoader ).to.be.an( 'object' ); expect( svgLoader ).to.have.property( 'use' ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js index 50154179b..f63c4cad5 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/getjavascriptloader.js @@ -4,18 +4,30 @@ */ import { describe, expect, it, vi } from 'vitest'; +import getJavaScriptLoader from '../../lib/loaders/getjavascriptloader.js'; +import getDebugLoader from '../../lib/loaders/getdebugloader.js'; +vi.mock( '../../lib/loaders/getdebugloader.js' ); describe( 'getJavaScriptLoader()', () => { it( 'should be a function', () => { - expect( loaders.getJavaScriptLoader ).to.be.a( 'function' ); + expect( getJavaScriptLoader ).to.be.a( 'function' ); } ); it( 'should return a definition that enables the ck-debug-loader', () => { - const debugLoader = loaders.getJavaScriptLoader( { + vi.mocked( getDebugLoader ).mockReturnValue( { + loader: 'ck-debug-loader', + options: { + debug: true + } + } ); + + const debugLoader = getJavaScriptLoader( { debugFlags: [ 'ENGINE' ] } ); + expect( vi.mocked( getDebugLoader ) ).toHaveBeenCalledExactlyOnceWith( [ 'ENGINE' ] ); + expect( debugLoader ).to.be.an( 'object' ); expect( debugLoader ).to.have.property( 'test' ); @@ -23,11 +35,9 @@ describe( 'getJavaScriptLoader()', () => { expect( '/home/ckeditor/plugin.js' ).to.match( debugLoader.test, 'Linux' ); expect( debugLoader ).to.have.property( 'loader' ); - expect( debugLoader.loader.endsWith( 'ck-debug-loader' ) ).to.equal( true ); + expect( debugLoader.loader ).to.equal( 'ck-debug-loader' ); expect( debugLoader ).to.have.property( 'options' ); expect( debugLoader.options ).to.be.an( 'object' ); - expect( debugLoader.options ).to.have.property( 'debugFlags' ); - expect( debugLoader.options.debugFlags ).to.be.an( 'array' ); - expect( debugLoader.options.debugFlags ).to.include( 'ENGINE' ); + expect( debugLoader.options ).to.have.property( 'debug', true ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js b/packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js index ef903c04b..d0f51d0e2 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/getstylesloader.js @@ -4,28 +4,38 @@ */ import { describe, expect, it, vi } from 'vitest'; +import getStylesLoader from '../../lib/loaders/getstylesloader.js'; +import { getPostCssConfig } from '../../lib/styles/index.js'; +vi.mock( 'mini-css-extract-plugin', () => ( { + default: class { + static get loader() { + return '/path/to/mini-css-extract-plugin/loader'; + } + } +} ) ); +vi.mock( '../../lib/styles/index.js' ); describe( 'getStylesLoader()', () => { it( 'should be a function', () => { - expect( loaders.getStylesLoader ).to.be.a( 'function' ); + expect( getStylesLoader ).to.be.a( 'function' ); } ); it( 'should return a definition that allow saving the produced CSS into a file using `mini-css-extract-plugin#loader`', () => { - const loader = loaders.getStylesLoader( { + const loader = getStylesLoader( { extractToSeparateFile: true, themePath: 'path/to/theme' } ); expect( loader ).to.be.an( 'object' ); - const cssLoader = loader.use[ 0 ]; + const cssLoader = loader.use.at( 0 ); expect( cssLoader ).to.be.equal( '/path/to/mini-css-extract-plugin/loader' ); } ); it( 'should return a definition that allow attaching the produced CSS on a site using `style-loader`', () => { - const loader = loaders.getStylesLoader( { + const loader = getStylesLoader( { themePath: 'path/to/theme' } ); @@ -42,14 +52,15 @@ describe( 'getStylesLoader()', () => { } ); it( 'should return a definition containing the correct setup of the `postcss-loader`', () => { - const loader = loaders.getStylesLoader( { + vi.mocked( getPostCssConfig ).mockReturnValue( 'styles.getPostCssConfig()' ); + + const loader = getStylesLoader( { themePath: 'path/to/theme' } ); expect( loader ).to.be.an( 'object' ); - // Array.at() is available since Node 16.6. - const postCssLoader = loader.use.pop(); + const postCssLoader = loader.use.at( -1 ); expect( postCssLoader ).to.be.an( 'object' ); expect( postCssLoader ).to.have.property( 'loader', 'postcss-loader' ); @@ -57,16 +68,17 @@ describe( 'getStylesLoader()', () => { expect( postCssLoader.options ).to.be.an( 'object' ); expect( postCssLoader.options ).to.have.property( 'postcssOptions', 'styles.getPostCssConfig()' ); - expect( postCssOptions ).to.be.an( 'object' ); - expect( postCssOptions ).to.have.property( 'themeImporter' ); - expect( postCssOptions ).to.have.property( 'minify', false ); - expect( postCssOptions ).to.have.property( 'sourceMap', false ); - expect( postCssOptions.themeImporter ).to.be.an( 'object' ); - expect( postCssOptions.themeImporter ).to.have.property( 'themePath', 'path/to/theme' ); + expect( vi.mocked( getPostCssConfig ) ).toHaveBeenCalledExactlyOnceWith( { + minify: false, + sourceMap: false, + themeImporter: { + themePath: 'path/to/theme' + } + } ); } ); it( 'should return a definition containing the correct setup of the `css-loader`', () => { - const loader = loaders.getStylesLoader( { + const loader = getStylesLoader( { skipPostCssLoader: true } ); @@ -76,12 +88,11 @@ describe( 'getStylesLoader()', () => { } ); it( 'should allow skipping adding the postcss-loader', () => { - const loader = loaders.getStylesLoader( { + const loader = getStylesLoader( { skipPostCssLoader: true } ); - // Array.at() is available since Node 16.6. - const cssLoader = loader.use.pop(); + const cssLoader = loader.use.at( -1 ); expect( cssLoader ).to.be.equal( 'css-loader' ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js b/packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js index 72198e28b..002ba2e42 100644 --- a/packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js +++ b/packages/ckeditor5-dev-utils/tests/loaders/gettypescriptloader.js @@ -4,15 +4,18 @@ */ import { describe, expect, it, vi } from 'vitest'; +import getTypeScriptLoader from '../../lib/loaders/gettypescriptloader.js'; +import getDebugLoader from '../../lib/loaders/getdebugloader.js'; +vi.mock( '../../lib/loaders/getdebugloader.js' ); describe( 'getTypeScriptLoader()', () => { it( 'should be a function', () => { - expect( loaders.getTypeScriptLoader ).to.be.a( 'function' ); + expect( getTypeScriptLoader ).to.be.a( 'function' ); } ); it( 'should return a definition that allows processing `*.ts` files using esbuild-loader', () => { - const tsLoader = loaders.getTypeScriptLoader( { + const tsLoader = getTypeScriptLoader( { configFile: '/home/project/configs/tsconfig.json' } ); @@ -30,7 +33,7 @@ describe( 'getTypeScriptLoader()', () => { } ); it( 'should return a definition that allows processing `*.ts` files using esbuild-loader (skipping `options.configFile`)', () => { - const tsLoader = loaders.getTypeScriptLoader(); + const tsLoader = getTypeScriptLoader(); expect( tsLoader ).to.be.an( 'object' ); expect( tsLoader ).to.have.property( 'test' ); @@ -46,7 +49,11 @@ describe( 'getTypeScriptLoader()', () => { } ); it( 'should return a definition that enables the debug loader before the typescript files', () => { - const tsLoader = loaders.getTypeScriptLoader( { + vi.mocked( getDebugLoader ).mockReturnValue( { + loader: 'ck-debug-loader' + } ); + + const tsLoader = getTypeScriptLoader( { configFile: '/home/project/configs/tsconfig.json', includeDebugLoader: true, debugFlags: [ 'ENGINE' ] @@ -61,7 +68,14 @@ describe( 'getTypeScriptLoader()', () => { } ); it( 'should pass the debug options into the debug loader', () => { - const tsLoader = loaders.getTypeScriptLoader( { + vi.mocked( getDebugLoader ).mockReturnValue( { + loader: 'ck-debug-loader', + options: { + debug: true + } + } ); + + const tsLoader = getTypeScriptLoader( { configFile: '/home/project/configs/tsconfig.json', includeDebugLoader: true, debugFlags: [ 'ENGINE' ] @@ -69,12 +83,14 @@ describe( 'getTypeScriptLoader()', () => { const debugLoader = tsLoader.use.find( item => item.loader.endsWith( 'ck-debug-loader' ) ); + expect( vi.mocked( getDebugLoader ) ).toHaveBeenCalledExactlyOnceWith( [ 'ENGINE' ] ); + expect( debugLoader ).to.be.an( 'object' ); + expect( debugLoader ).to.have.property( 'loader' ); + expect( debugLoader.loader ).to.equal( 'ck-debug-loader' ); expect( debugLoader ).to.have.property( 'options' ); expect( debugLoader.options ).to.be.an( 'object' ); - expect( debugLoader.options ).to.have.property( 'debugFlags' ); - expect( debugLoader.options.debugFlags ).to.be.an( 'array' ); - expect( debugLoader.options.debugFlags ).to.include( 'ENGINE' ); + expect( debugLoader.options ).to.have.property( 'debug', true ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/logger.js b/packages/ckeditor5-dev-utils/tests/logger.js deleted file mode 100644 index b057b25b5..000000000 --- a/packages/ckeditor5-dev-utils/tests/logger.js +++ /dev/null @@ -1,201 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const sinon = require( 'sinon' ); -const chai = require( 'chai' ); -const expect = chai.expect; -const logger = require( '../lib/logger' ); - -describe( 'logger', () => { - const logMessage = 'An example.'; - let sandbox, log; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - - it( 'provides an API for set verbosity level', () => { - expect( logger ).to.be.a( 'function' ); - } ); - - describe( 'verbosity = info', () => { - beforeEach( () => { - log = logger( 'info' ); - } ); - - describe( 'logger.info()', () => { - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.info( logMessage ); - - expect( consoleLog.calledOnce ).to.equal( true ); - expect( consoleLog.firstCall.args[ 0 ] ).to.equal( logMessage ); - - consoleLog.restore(); - } ); - } ); - - describe( 'logger.warning()', () => { - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.warning( logMessage ); - - expect( consoleLog.calledOnce ).to.equal( true ); - expect( consoleLog.firstCall.args[ 0 ] ).to.match( new RegExp( logMessage ) ); - - consoleLog.restore(); - } ); - } ); - - describe( 'logger.error()', () => { - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.error( logMessage ); - - expect( consoleLog.calledOnce ).to.equal( true ); - expect( consoleLog.firstCall.args[ 0 ] ).to.match( new RegExp( logMessage ) ); - - consoleLog.restore(); - } ); - } ); - } ); - - describe( 'verbosity = warning', () => { - beforeEach( () => { - log = logger( 'warning' ); - } ); - - describe( 'logger.info()', () => { - it( 'should not log any message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.info( logMessage ); - - expect( consoleLog.called ).to.equal( false ); - - consoleLog.restore(); - } ); - } ); - - describe( 'logger.warning()', () => { - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.warning( logMessage ); - - expect( consoleLog.calledOnce ).to.equal( true ); - expect( consoleLog.firstCall.args[ 0 ] ).to.match( new RegExp( logMessage ) ); - - consoleLog.restore(); - } ); - } ); - - describe( 'logger.error()', () => { - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.error( logMessage ); - - expect( consoleLog.calledOnce ).to.equal( true ); - expect( consoleLog.firstCall.args[ 0 ] ).to.match( new RegExp( logMessage ) ); - - consoleLog.restore(); - } ); - } ); - } ); - - describe( 'verbosity = error', () => { - beforeEach( () => { - log = logger( 'error' ); - } ); - - describe( 'logger.info()', () => { - it( 'should not log any message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.info( logMessage ); - - expect( consoleLog.called ).to.equal( false ); - - consoleLog.restore(); - } ); - } ); - - describe( 'logger.warning()', () => { - it( 'should not log any message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.warning( logMessage ); - - expect( consoleLog.called ).to.equal( false ); - - consoleLog.restore(); - } ); - } ); - - describe( 'logger.error()', () => { - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.error( logMessage ); - - expect( consoleLog.calledOnce ).to.equal( true ); - expect( consoleLog.firstCall.args[ 0 ] ).to.match( new RegExp( logMessage ) ); - - consoleLog.restore(); - } ); - } ); - } ); - - describe( 'uses default verbosity', () => { - beforeEach( () => { - log = logger(); - } ); - - describe( 'logger.info()', () => { - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - - log.info( logMessage ); - - expect( consoleLog.calledOnce ).to.equal( true ); - expect( consoleLog.firstCall.args[ 0 ] ).to.equal( logMessage ); - - consoleLog.restore(); - } ); - } ); - } ); - - describe( 'printing error', () => { - beforeEach( () => { - log = logger(); - } ); - - it( 'should log a message', () => { - const consoleLog = sandbox.stub( console, 'log' ); - const consoleDir = sandbox.stub( console, 'dir' ); - - const error = new Error(); - - log.error( logMessage, error ); - - expect( consoleDir.calledOnce ).to.equal( true ); - expect( consoleDir.firstCall.args[ 0 ] ).to.equal( error ); - expect( consoleDir.firstCall.args[ 1 ] ).to.deep.equal( { depth: null } ); - - consoleLog.restore(); - consoleDir.restore(); - } ); - } ); -} ); diff --git a/packages/ckeditor5-dev-utils/tests/logger/index.js b/packages/ckeditor5-dev-utils/tests/logger/index.js new file mode 100644 index 000000000..2ecdf99d4 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/logger/index.js @@ -0,0 +1,155 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import logger from '../../lib/logger/index.js'; + +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + dir: vi.fn() +} ); + +describe( 'logger()', () => { + const logMessage = 'An example.'; + let log; + + it( 'provides an API for set verbosity level', () => { + expect( logger ).to.be.a( 'function' ); + } ); + + describe( 'verbosity = info', () => { + beforeEach( () => { + log = logger( 'info' ); + } ); + + describe( 'logger.info()', () => { + it( 'should log a message', () => { + log.info( logMessage ); + + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( + expect.stringMatching( logMessage ) + ); + } ); + } ); + + describe( 'logger.warning()', () => { + it( 'should log a message', () => { + log.warning( logMessage ); + + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( + expect.stringMatching( logMessage ) + ); + } ); + } ); + + describe( 'logger.error()', () => { + it( 'should log a message', () => { + log.error( logMessage ); + + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( + expect.stringMatching( logMessage ) + ); + } ); + } ); + } ); + + describe( 'verbosity = warning', () => { + beforeEach( () => { + log = logger( 'warning' ); + } ); + + describe( 'logger.info()', () => { + it( 'should not log any message', () => { + log.info( logMessage ); + + expect( vi.mocked( console ).log ).not.toHaveBeenCalled(); + } ); + } ); + + describe( 'logger.warning()', () => { + it( 'should log a message', () => { + log.warning( logMessage ); + + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( + expect.stringMatching( logMessage ) + ); + } ); + } ); + + describe( 'logger.error()', () => { + it( 'should log a message', () => { + log.error( logMessage ); + + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( + expect.stringMatching( logMessage ) + ); + } ); + } ); + } ); + + describe( 'verbosity = error', () => { + beforeEach( () => { + log = logger( 'error' ); + } ); + + describe( 'logger.info()', () => { + it( 'should not log any message', () => { + log.info( logMessage ); + + expect( vi.mocked( console ).log ).not.toHaveBeenCalled(); + } ); + } ); + + describe( 'logger.warning()', () => { + it( 'should not log any message', () => { + log.warning( logMessage ); + + expect( vi.mocked( console ).log ).not.toHaveBeenCalled(); + } ); + } ); + + describe( 'logger.error()', () => { + it( 'should log a message', () => { + log.error( logMessage ); + + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( + expect.stringMatching( logMessage ) + ); + } ); + } ); + } ); + + describe( 'uses default verbosity', () => { + beforeEach( () => { + log = logger(); + } ); + + describe( 'logger.info()', () => { + it( 'should log a message', () => { + log.info( logMessage ); + + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( + expect.stringMatching( logMessage ) + ); + } ); + } ); + } ); + + describe( 'printing error', () => { + beforeEach( () => { + log = logger(); + } ); + + it( 'should log a message', () => { + const error = new Error(); + + log.error( logMessage, error ); + + expect( vi.mocked( console ).dir ).toHaveBeenCalledExactlyOnceWith( error, { depth: null } ); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/stream.js b/packages/ckeditor5-dev-utils/tests/stream.js deleted file mode 100644 index 8f5ae9a44..000000000 --- a/packages/ckeditor5-dev-utils/tests/stream.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const sinon = require( 'sinon' ); -const stream = require( 'stream' ); -const Vinyl = require( 'vinyl' ); - -describe( 'stream', () => { - const utils = require( '../lib/stream' ); - let sandbox; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - - describe( 'noop', () => { - it( 'should return PassTrough stream', () => { - const PassThrough = stream.PassThrough; - const ret = utils.noop(); - expect( ret instanceof PassThrough ).to.equal( true ); - } ); - - it( 'should return a duplex stream when given a callback and call that callback', () => { - const spy = sinon.spy(); - const ret = utils.noop( spy ); - - ret.write( 'foo' ); - - expect( spy.called ).to.equal( true ); - expect( ret.writable ).to.equal( true ); - expect( ret.readable ).to.equal( true ); - } ); - - it( 'should wait until a promise returned by the callback is resolved', ( ) => { - let resolved = false; - let resolve; - - const stream = utils.noop( () => { - return new Promise( r => { - resolve = r; - } ); - } ); - - stream - .pipe( - utils.noop( () => { - expect( resolved ).to.equal( true ); - } ) - ); - - stream.write( 'foo' ); - - resolved = true; - resolve(); - } ); - - it( 'should fail when a returned promise is rejected', done => { - const chunks = []; - const stream = utils.noop( chunk => { - return new Promise( ( resolve, reject ) => { - if ( chunk == 'foo' ) { - reject(); - } else { - resolve(); - } - } ); - } ); - - stream.pipe( utils.noop( chunk => { - chunks.push( chunk ); - } ) ); - - stream.on( 'end', () => { - expect( chunks.join() ).to.equal( 'bar' ); - done(); - } ); - - stream.write( 'foo' ); - stream.write( 'bar' ); - stream.end(); - } ); - } ); - - describe( 'isTestFile', () => { - function test( path, expected ) { - it( `returns ${ expected } for ${ path }`, () => { - const file = new Vinyl( { - cwd: './', - path, - contents: Buffer.from( '' ) - } ); - - expect( utils.isTestFile( file ) ).to.equal( expected ); - } ); - } - - test( 'tests/file.js', true ); - test( 'tests/foo/file.js', true ); - test( 'tests/tests.js', true ); - test( 'tests/_utils-tests/foo.js', true ); - - test( 'foo/file.js', false ); - test( 'foo/tests/file.js', false ); - test( 'tests/_foo/file.js', false ); - } ); -} ); diff --git a/packages/ckeditor5-dev-utils/tests/stream/index.js b/packages/ckeditor5-dev-utils/tests/stream/index.js new file mode 100644 index 000000000..f10b02008 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/stream/index.js @@ -0,0 +1,19 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import * as stream from '../../lib/stream/index.js'; +import noop from '../../lib/stream/noop.js'; + +vi.mock( '../../lib/stream/noop.js' ); + +describe( 'stream/index.js', () => { + describe( 'noop()', () => { + it( 'should be a function', () => { + expect( stream.noop ).to.be.a( 'function' ); + expect( stream.noop ).toEqual( noop ); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/stream/noop.js b/packages/ckeditor5-dev-utils/tests/stream/noop.js new file mode 100644 index 000000000..9db901ae8 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/stream/noop.js @@ -0,0 +1,78 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import stream from 'stream'; +import noop from '../../lib/stream/noop.js'; + +describe( 'noop()', () => { + it( 'should return PassTrough stream', () => { + const PassThrough = stream.PassThrough; + const ret = noop(); + expect( ret instanceof PassThrough ).to.equal( true ); + } ); + + it( 'should return a duplex stream when given a callback and call that callback', () => { + const spy = vi.fn(); + const ret = noop( spy ); + + ret.write( 'foo' ); + + expect( spy ).toHaveBeenCalledOnce(); + expect( ret.writable ).to.equal( true ); + expect( ret.readable ).to.equal( true ); + } ); + + it( 'should wait until a promise returned by the callback is resolved', () => { + let resolved = false; + let resolve; + + const stream = noop( () => { + return new Promise( r => { + resolve = r; + } ); + } ); + + stream + .pipe( + noop( () => { + expect( resolved ).to.equal( true ); + } ) + ); + + stream.write( 'foo' ); + + resolved = true; + resolve(); + } ); + + it( 'should fail when a returned promise is rejected', () => { + return new Promise( done => { + const chunks = []; + const stream = noop( chunk => { + return new Promise( ( resolve, reject ) => { + if ( chunk === 'foo' ) { + reject(); + } else { + resolve(); + } + } ); + } ); + + stream.pipe( noop( chunk => { + chunks.push( chunk ); + } ) ); + + stream.on( 'end', () => { + expect( chunks.join() ).to.equal( 'bar' ); + done(); + } ); + + stream.write( 'foo' ); + stream.write( 'bar' ); + stream.end(); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js b/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js index 8c6227ad6..737c36dbd 100644 --- a/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js +++ b/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js @@ -3,86 +3,72 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import postCssImport from 'postcss-import'; +import postCssMixins from 'postcss-mixins'; +import postCssNesting from 'postcss-nesting'; +import cssnano from 'cssnano'; +import themeLogger from '../../lib/styles/themelogger.js'; +import themeImporter from '../../lib/styles/themeimporter.js'; +import getPostCssConfig from '../../lib/styles/getpostcssconfig.js'; -const chai = require( 'chai' ); -const sinon = require( 'sinon' ); -const mockery = require( 'mockery' ); -const expect = chai.expect; - -describe( 'styles', () => { - let getPostCssConfig, stubs; +vi.mock( 'postcss-import' ); +vi.mock( 'postcss-mixins' ); +vi.mock( 'postcss-nesting' ); +vi.mock( 'cssnano' ); +vi.mock( '../../lib/styles/themelogger.js' ); +vi.mock( '../../lib/styles/themeimporter.js' ); +describe( 'getPostCssConfig()', () => { beforeEach( () => { - stubs = { - './themeimporter': sinon.stub().returns( 'postcss-ckeditor5-theme-importer' ), - 'postcss-import': sinon.stub().returns( 'postcss-import' ), - 'postcss-mixins': sinon.stub().returns( 'postcss-mixins' ), - 'postcss-nesting': sinon.stub().returns( 'postcss-nesting' ), - './themelogger': sinon.stub().returns( 'postcss-ckeditor5-theme-logger' ), - cssnano: sinon.stub().returns( 'cssnano' ) - }; - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - for ( const stub in stubs ) { - mockery.registerMock( stub, stubs[ stub ] ); - } - - getPostCssConfig = require( '../../lib/styles/getpostcssconfig' ); + vi.mocked( themeImporter ).mockReturnValue( 'postcss-ckeditor5-theme-importer' ); + vi.mocked( themeLogger ).mockReturnValue( 'postcss-ckeditor5-theme-logger' ); + vi.mocked( postCssImport ).mockReturnValue( 'postcss-import' ); + vi.mocked( postCssMixins ).mockReturnValue( 'postcss-mixins' ); + vi.mocked( postCssNesting ).mockReturnValue( 'postcss-nesting' ); + vi.mocked( cssnano ).mockReturnValue( 'cssnano' ); } ); - afterEach( () => { - mockery.disable(); + it( 'returns PostCSS plugins', () => { + expect( getPostCssConfig().plugins ).to.have.members( [ + 'postcss-import', + 'postcss-ckeditor5-theme-importer', + 'postcss-mixins', + 'postcss-nesting', + 'postcss-ckeditor5-theme-logger' + ] ); } ); - describe( 'getPostCssConfig()', () => { - it( 'returns PostCSS plugins', () => { - expect( getPostCssConfig().plugins ) - .to.have.members( [ - 'postcss-import', - 'postcss-ckeditor5-theme-importer', - 'postcss-mixins', - 'postcss-nesting', - 'postcss-ckeditor5-theme-logger' - ] ); - } ); - - it( 'passes options to the theme importer', () => { - getPostCssConfig( { - themeImporter: { - themePath: 'abc', - debug: true - } - } ); - - sinon.assert.calledWithExactly( stubs[ './themeimporter' ], { + it( 'passes options to the theme importer', () => { + getPostCssConfig( { + themeImporter: { themePath: 'abc', debug: true - } ); + } } ); - // https://github.com/ckeditor/ckeditor5/issues/11730 - it( 'passes options to postcss-nesting', () => { - getPostCssConfig(); - - sinon.assert.calledWithExactly( stubs[ 'postcss-nesting' ], { - noIsPseudoSelector: true - } ); + expect( vi.mocked( themeImporter ) ).toHaveBeenCalledExactlyOnceWith( { + themePath: 'abc', + debug: true } ); + } ); - it( 'supports #sourceMap option', () => { - expect( getPostCssConfig( { sourceMap: true } ).sourceMap ) - .to.equal( 'inline' ); - } ); + // https://github.com/ckeditor/ckeditor5/issues/11730 + it( 'passes options to postcss-nesting', () => { + getPostCssConfig(); - it( 'supports #minify option', () => { - expect( getPostCssConfig( { minify: true } ).plugins.pop() ) - .to.equal( 'cssnano' ); + expect( vi.mocked( postCssNesting ) ).toHaveBeenCalledExactlyOnceWith( { + noIsPseudoSelector: true } ); } ); + + it( 'supports #sourceMap option', () => { + expect( getPostCssConfig( { sourceMap: true } ).sourceMap ) + .to.equal( 'inline' ); + } ); + + it( 'supports #minify option', () => { + expect( getPostCssConfig( { minify: true } ).plugins.pop() ) + .to.equal( 'cssnano' ); + } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/styles/index.js b/packages/ckeditor5-dev-utils/tests/styles/index.js index 1f4317cac..2fb7a4ca3 100644 --- a/packages/ckeditor5-dev-utils/tests/styles/index.js +++ b/packages/ckeditor5-dev-utils/tests/styles/index.js @@ -3,33 +3,26 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it, vi } from 'vitest'; +import * as styles from '../../lib/styles/index.js'; +import getPostCssConfig from '../../lib/styles/getpostcssconfig.js'; +import themeImporter from '../../lib/styles/themeimporter.js'; -const chai = require( 'chai' ); -const expect = chai.expect; - -describe( 'styles', () => { - let tasks; - - beforeEach( () => { - tasks = require( '../../lib/styles/index' ); - } ); +vi.mock( '../../lib/styles/getpostcssconfig.js' ); +vi.mock( '../../lib/styles/themeimporter.js' ); +describe( 'styles/index.js', () => { describe( 'getPostCssConfig()', () => { it( 'should be a function', () => { - expect( tasks.getPostCssConfig ).to.be.a( 'function' ); + expect( styles.getPostCssConfig ).to.be.a( 'function' ); + expect( styles.getPostCssConfig ).toEqual( getPostCssConfig ); } ); } ); describe( 'themeImporter()', () => { it( 'should be a function', () => { - expect( tasks.themeImporter ).to.be.a( 'function' ); - } ); - } ); - - describe( 'themeLogger()', () => { - it( 'should be a function', () => { - expect( tasks.themeLogger ).to.be.a( 'function' ); + expect( styles.themeImporter ).to.be.a( 'function' ); + expect( styles.themeImporter ).toEqual( themeImporter ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/styles/utils/getpackagename.js b/packages/ckeditor5-dev-utils/tests/styles/utils/getpackagename.js index d2ca5e744..49e06c70f 100644 --- a/packages/ckeditor5-dev-utils/tests/styles/utils/getpackagename.js +++ b/packages/ckeditor5-dev-utils/tests/styles/utils/getpackagename.js @@ -3,80 +3,71 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it } from 'vitest'; +import getPackageName from '../../../lib/styles/utils/getpackagename.js'; -const { expect } = require( 'chai' ); +describe( 'getPackageName()', () => { + describe( 'Unix paths', () => { + it( 'returns package name for path which starts with package name (simple check)', () => { + checkPackage( '/work/space/ckeditor5-foo/tests/manual/foo.js', 'ckeditor5-foo' ); + } ); -describe( 'dev-tests/utils', () => { - let getPackageName; + it( 'returns package name for path which starts with package name (workspace directory looks like package name)', () => { + checkPackage( + '/Users/foo/ckeditor5-workspace/ckeditor5/packages/ckeditor5-foo/tests/manual/foo.js', + 'ckeditor5-foo' + ); + } ); - beforeEach( () => { - getPackageName = require( '../../../lib/styles/utils/getpackagename' ); - } ); + it( 'returns package name for path which starts with package name (nested dependencies)', () => { + checkPackage( + '/home/foo/ckeditor5/packages/ckeditor5-build-classic/node_modules/@ckeditor/ckeditor5-foo/tests/manual/foo.js', + 'ckeditor5-foo' + ); + } ); - describe( 'getPackageName()', () => { - describe( 'Unix paths', () => { - it( 'returns package name for path which starts with package name (simple check)', () => { - checkPackage( '/work/space/ckeditor5-foo/tests/manual/foo.js', 'ckeditor5-foo' ); - } ); + /* eslint-disable max-len */ + it( 'returns package name for path which starts with package name (combined workspace looks like package and nested dependencies)', () => { + checkPackage( + '/Users/foo/ckeditor5-workspace/ckeditor5/ckeditor5-build-classic/node_modules/@ckeditor/ckeditor5-foo/tests/manual/foo.js', + 'ckeditor5-foo' + ); + } ); + /* eslint-enable max-len */ + } ); - it( 'returns package name for path which starts with package name (workspace directory looks like package name)', () => { - checkPackage( - '/Users/foo/ckeditor5-workspace/ckeditor5/packages/ckeditor5-foo/tests/manual/foo.js', - 'ckeditor5-foo' - ); - } ); + describe( 'Windows paths', () => { + it( 'returns package name for path which starts with package name (simple check)', () => { + checkPackage( 'C:\\work\\space\\ckeditor5-foo\\tests\\manual\\foo.js', 'ckeditor5-foo' ); + } ); - it( 'returns package name for path which starts with package name (nested dependencies)', () => { - checkPackage( - '/home/foo/ckeditor5/packages/ckeditor5-build-classic/node_modules/@ckeditor/ckeditor5-foo/tests/manual/foo.js', - 'ckeditor5-foo' - ); - } ); + it( 'returns package name for path which starts with package name (workspace directory looks like package name)', () => { + checkPackage( + 'C:\\Document and settings\\foo\\ckeditor5-workspace\\ckeditor5\\packages\\ckeditor5-foo\\tests\\manual\\foo.js', + 'ckeditor5-foo' + ); + } ); + it( 'returns package name for path which starts with package name (nested dependencies)', () => { /* eslint-disable max-len */ - it( 'returns package name for path which starts with package name (combined workspace looks like package and nested dependencies)', () => { - checkPackage( - '/Users/foo/ckeditor5-workspace/ckeditor5/ckeditor5-build-classic/node_modules/@ckeditor/ckeditor5-foo/tests/manual/foo.js', - 'ckeditor5-foo' - ); - } ); + checkPackage( + 'C:\\Document and settings\\ckeditor5\\packages\\ckeditor5-build-classic\\node_modules\\@ckeditor\\ckeditor5-foo\\tests\\manual\\foo.js', + 'ckeditor5-foo' + ); /* eslint-enable max-len */ } ); - describe( 'Windows paths', () => { - it( 'returns package name for path which starts with package name (simple check)', () => { - checkPackage( 'C:\\work\\space\\ckeditor5-foo\\tests\\manual\\foo.js', 'ckeditor5-foo' ); - } ); - - it( 'returns package name for path which starts with package name (workspace directory looks like package name)', () => { - checkPackage( - 'C:\\Document and settings\\foo\\ckeditor5-workspace\\ckeditor5\\packages\\ckeditor5-foo\\tests\\manual\\foo.js', - 'ckeditor5-foo' - ); - } ); - - it( 'returns package name for path which starts with package name (nested dependencies)', () => { - /* eslint-disable max-len */ - checkPackage( - 'C:\\Document and settings\\ckeditor5\\packages\\ckeditor5-build-classic\\node_modules\\@ckeditor\\ckeditor5-foo\\tests\\manual\\foo.js', - 'ckeditor5-foo' - ); - /* eslint-enable max-len */ - } ); - - /* eslint-disable max-len */ - it( 'returns package name for path which starts with package name (combined workspace looks like package and nested dependencies)', () => { - checkPackage( - 'C:\\Users\\foo\\ckeditor5-workspace\\ckeditor5\\ckeditor5-build-classic\\node_modules\\@ckeditor\\ckeditor5-foo\\tests\\manual\\foo.js', - 'ckeditor5-foo' - ); - } ); - /* eslint-enable max-len */ + /* eslint-disable max-len */ + it( 'returns package name for path which starts with package name (combined workspace looks like package and nested dependencies)', () => { + checkPackage( + 'C:\\Users\\foo\\ckeditor5-workspace\\ckeditor5\\ckeditor5-build-classic\\node_modules\\@ckeditor\\ckeditor5-foo\\tests\\manual\\foo.js', + 'ckeditor5-foo' + ); } ); + /* eslint-enable max-len */ } ); - - function checkPackage( filePath, expectedPath ) { - expect( getPackageName( filePath ) ).to.equal( expectedPath ); - } } ); + +function checkPackage( filePath, expectedPath ) { + expect( getPackageName( filePath ) ).to.equal( expectedPath ); +} diff --git a/packages/ckeditor5-dev-utils/tests/tools.js b/packages/ckeditor5-dev-utils/tests/tools.js deleted file mode 100644 index 3c707febc..000000000 --- a/packages/ckeditor5-dev-utils/tests/tools.js +++ /dev/null @@ -1,258 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const sinon = require( 'sinon' ); -const expect = chai.expect; -const tools = require( '../lib/tools' ); -const path = require( 'path' ); -const mockery = require( 'mockery' ); - -describe( 'utils', () => { - let sandbox, infoSpy, errorSpy, loggerVerbosity; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( './logger', verbosity => { - loggerVerbosity = verbosity; - infoSpy = sinon.spy(); - errorSpy = sinon.spy(); - - return { - info: infoSpy, - error: errorSpy - }; - } ); - } ); - - afterEach( () => { - sandbox.restore(); - mockery.disable(); - } ); - - describe( 'tools', () => { - describe( 'createSpinner', () => { - it( 'should be defined', () => expect( tools.createSpinner ).to.be.a( 'function' ) ); - } ); - - describe( 'shExec', () => { - it( 'should be defined', () => expect( tools.shExec ).to.be.a( 'function' ) ); - - it( 'should execute command (default cwd)', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 0 } ); - const processStub = sandbox.stub( process, 'cwd' ).returns( '/default' ); - - tools.shExec( 'command' ); - - sinon.assert.calledOnce( processStub ); - sinon.assert.calledOnce( execStub ); - expect( execStub.firstCall.args[ 1 ] ).to.be.an( 'object' ); - expect( execStub.firstCall.args[ 1 ] ).to.contain.property( 'cwd', '/default' ); - } ); - - it( 'should execute command with specified cwd', () => { - const cwd = '/home/user'; - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 0 } ); - - tools.shExec( 'command', { cwd } ); - - sinon.assert.calledOnce( execStub ); - expect( execStub.firstCall.args[ 1 ] ).to.be.an( 'object' ); - expect( execStub.firstCall.args[ 1 ] ).to.contain.property( 'cwd', cwd ); - } ); - - it( 'should throw error on unsuccessful call', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 1 } ); - - expect( () => { - tools.shExec( 'command' ); - } ).to.throw(); - sinon.assert.calledOnce( execStub ); - } ); - - it( 'should output using log functions when exit code is equal to 0', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 0, stdout: 'out', stderr: 'err' } ); - - tools.shExec( 'command' ); - - expect( loggerVerbosity ).to.equal( 'info' ); - expect( execStub.calledOnce ).to.equal( true ); - expect( errorSpy.called ).to.equal( false ); - expect( infoSpy.calledTwice ).to.equal( true ); - expect( infoSpy.firstCall.args[ 0 ] ).to.match( /out/ ); - expect( infoSpy.secondCall.args[ 0 ] ).to.match( /err/ ); - } ); - - it( 'should output using log functions when exit code is not equal to 0', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 1, stdout: 'out', stderr: 'err' } ); - - expect( () => { - tools.shExec( 'command' ); - } ).to.throw(); - - expect( loggerVerbosity ).to.equal( 'info' ); - expect( infoSpy.called ).to.equal( false ); - expect( execStub.calledOnce ).to.equal( true ); - expect( errorSpy.calledTwice ).to.equal( true ); - expect( errorSpy.firstCall.args[ 0 ] ).to.match( /out/ ); - expect( errorSpy.secondCall.args[ 0 ] ).to.match( /err/ ); - } ); - - it( 'should not log if no output from executed command', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 0, stdout: '', stderr: '' } ); - - tools.shExec( 'command', { verbosity: 'error' } ); - - expect( loggerVerbosity ).to.equal( 'error' ); - expect( execStub.calledOnce ).to.equal( true ); - expect( infoSpy.calledOnce ).to.equal( false ); - expect( errorSpy.calledOnce ).to.equal( false ); - } ); - - it( 'should return a promise when executing a command in asynchronous mode', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).callsFake( ( command, options, callback ) => { - callback( 0 ); - } ); - - return tools.shExec( 'command', { async: true } ) - .then( () => { - sinon.assert.calledOnce( execStub ); - } ); - } ); - - it( 'should throw error on unsuccessful call in asynchronous mode', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).callsFake( ( command, options, callback ) => { - callback( 1 ); - } ); - - return tools.shExec( 'command', { async: true } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - () => { - sinon.assert.calledOnce( execStub ); - } - ); - } ); - - it( 'should output using log functions when exit code is equal to 0 in asynchronous mode', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).callsFake( ( command, options, callback ) => { - callback( 0, 'out', 'err' ); - } ); - - return tools.shExec( 'command', { async: true } ) - .then( () => { - expect( loggerVerbosity ).to.equal( 'info' ); - expect( execStub.calledOnce ).to.equal( true ); - expect( errorSpy.called ).to.equal( false ); - expect( infoSpy.calledTwice ).to.equal( true ); - expect( infoSpy.firstCall.args[ 0 ] ).to.match( /out/ ); - expect( infoSpy.secondCall.args[ 0 ] ).to.match( /err/ ); - } ); - } ); - - it( 'should output using log functions when exit code is not equal to 0 in asynchronous mode', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).callsFake( ( command, options, callback ) => { - callback( 1, 'out', 'err' ); - } ); - - return tools.shExec( 'command', { async: true } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - () => { - expect( loggerVerbosity ).to.equal( 'info' ); - expect( infoSpy.called ).to.equal( false ); - expect( execStub.calledOnce ).to.equal( true ); - expect( errorSpy.calledTwice ).to.equal( true ); - expect( errorSpy.firstCall.args[ 0 ] ).to.match( /out/ ); - expect( errorSpy.secondCall.args[ 0 ] ).to.match( /err/ ); - } - ); - } ); - - it( 'should not log if no output from executed command in asynchronous mode', () => { - const sh = require( 'shelljs' ); - const execStub = sandbox.stub( sh, 'exec' ).callsFake( ( command, options, callback ) => { - callback( 0, '', '' ); - } ); - - return tools.shExec( 'command', { verbosity: 'error', async: true } ) - .then( () => { - expect( loggerVerbosity ).to.equal( 'error' ); - expect( execStub.calledOnce ).to.equal( true ); - expect( infoSpy.calledOnce ).to.equal( false ); - expect( errorSpy.calledOnce ).to.equal( false ); - } ); - } ); - } ); - - describe( 'getDirectories', () => { - it( 'should be defined', () => expect( tools.getDirectories ).to.be.a( 'function' ) ); - - it( 'should get directories in specified path', () => { - const fs = require( 'fs' ); - const directories = [ 'dir1', 'dir2', 'dir3' ]; - const readdirSyncStub = sandbox.stub( fs, 'readdirSync' ).returns( directories ); - const statSyncStub = sandbox.stub( fs, 'statSync' ).returns( { - isDirectory: () => { - return true; - } - } ); - const dirPath = 'path'; - - tools.getDirectories( dirPath ); - - expect( readdirSyncStub.calledOnce ).to.equal( true ); - expect( statSyncStub.calledThrice ).to.equal( true ); - expect( statSyncStub.firstCall.args[ 0 ] ).to.equal( path.join( dirPath, directories[ 0 ] ) ); - expect( statSyncStub.secondCall.args[ 0 ] ).to.equal( path.join( dirPath, directories[ 1 ] ) ); - expect( statSyncStub.thirdCall.args[ 0 ] ).to.equal( path.join( dirPath, directories[ 2 ] ) ); - } ); - } ); - - describe( 'updateJSONFile', () => { - it( 'should be defined', () => expect( tools.updateJSONFile ).to.be.a( 'function' ) ); - it( 'should read, update and save JSON file', () => { - const path = 'path/to/file.json'; - const fs = require( 'fs' ); - const readFileStub = sandbox.stub( fs, 'readFileSync' ).returns( '{}' ); - const modifiedJSON = { modified: true }; - const writeFileStub = sandbox.stub( fs, 'writeFileSync' ); - - tools.updateJSONFile( path, () => { - return modifiedJSON; - } ); - - expect( readFileStub.calledOnce ).to.equal( true ); - expect( readFileStub.firstCall.args[ 0 ] ).to.equal( path ); - expect( writeFileStub.calledOnce ).to.equal( true ); - expect( writeFileStub.firstCall.args[ 0 ] ).to.equal( path ); - expect( writeFileStub.firstCall.args[ 1 ] ).to.equal( JSON.stringify( modifiedJSON, null, 2 ) + '\n' ); - } ); - } ); - } ); -} ); diff --git a/packages/ckeditor5-dev-utils/tests/tools/createspinner.js b/packages/ckeditor5-dev-utils/tests/tools/createspinner.js index 382a0baf5..fd4e848e8 100644 --- a/packages/ckeditor5-dev-utils/tests/tools/createspinner.js +++ b/packages/ckeditor5-dev-utils/tests/tools/createspinner.js @@ -3,54 +3,38 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const sinon = require( 'sinon' ); -const expect = require( 'chai' ).expect; -const mockery = require( 'mockery' ); - -describe( 'lib/utils/create-spinner', () => { - let createSpinner, clock, stubs; +import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; +import { show as cliCursorShow, hide as cliCursorHide } from 'cli-cursor'; +import isInteractive from 'is-interactive'; +import createSpinner from '../../lib/tools/createspinner.js'; +import readline from 'readline'; + +vi.mock( 'is-interactive' ); +vi.mock( 'cli-spinners', () => ( { + default: { + dots12: { + frames: [ '|', '/', '-', '\\' ], + interval: 5 + } + } +} ) ); +vi.mock( 'cli-cursor' ); +vi.mock( 'readline' ); + +vi.stubGlobal( 'console', { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} ); +describe( 'createSpinner()', () => { beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - clock = sinon.useFakeTimers(); - - stubs = { - isInteractive: sinon.stub(), - cliSpinners: { - dots12: { - frames: [ '|', '/', '-', '\\' ], - interval: 5 - } - }, - cliCursor: { - show: sinon.stub(), - hide: sinon.stub() - }, - readline: { - clearLine: sinon.stub(), - cursorTo: sinon.stub() - } - }; - - mockery.registerMock( 'is-interactive', stubs.isInteractive ); - mockery.registerMock( 'cli-spinners', stubs.cliSpinners ); - mockery.registerMock( 'cli-cursor', stubs.cliCursor ); - mockery.registerMock( 'readline', stubs.readline ); - - createSpinner = require( '../../lib/tools/createspinner' ); + vi.useFakeTimers(); + vi.setSystemTime( new Date( '2023-06-15 12:00:00' ) ); } ); afterEach( () => { - sinon.restore(); - clock.restore(); - mockery.disable(); + vi.useRealTimers(); } ); it( 'should be a function', () => { @@ -75,44 +59,25 @@ describe( 'lib/utils/create-spinner', () => { describe( 'type: spinner', () => { describe( '#start', () => { beforeEach( () => { - stubs.isInteractive.returns( true ); + vi.mocked( isInteractive ).mockReturnValue( true ); } ); it( 'prints the specified title if spinner should be disabled', () => { const spinner = createSpinner( 'Foo.', { isDisabled: true } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '📍 Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '📍 Foo.' ); } ); it( 'prints the specified title if spinner cannot be created if CLI is not interactive', () => { - stubs.isInteractive.returns( false ); - - const spinner = createSpinner( 'Foo.' ); - const consoleStub = sinon.stub( console, 'log' ); - - spinner.start(); - - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '📍 Foo.' ); - } ); + vi.mocked( isInteractive ).mockReturnValue( false ); - it( 'uses "setInterval" for creating a loop', () => { const spinner = createSpinner( 'Foo.' ); spinner.start(); - const timer = Object.values( clock.timers ).shift(); - - expect( timer ).to.be.an( 'object' ); - expect( timer.type ).to.equal( 'Interval' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '📍 Foo.' ); } ); it( 'prints always spinner in the last line', () => { @@ -120,39 +85,42 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - const writeStub = sinon.stub( process.stdout, 'write' ); + const writeStub = vi.spyOn( process.stdout, 'write' ).mockImplementation( () => {} ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 1 ); - expect( writeStub.getCall( 0 ).args[ 0 ] ).to.equal( '| Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 1 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 2 ); - expect( writeStub.getCall( 1 ).args[ 0 ] ).to.equal( '/ Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 2 ); + expect( writeStub ).toHaveBeenLastCalledWith( '/ Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 3 ); - expect( writeStub.getCall( 2 ).args[ 0 ] ).to.equal( '- Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 3 ); + expect( writeStub ).toHaveBeenLastCalledWith( '- Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 4 ); - expect( writeStub.getCall( 3 ).args[ 0 ] ).to.equal( '\\ Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 4 ); + expect( writeStub ).toHaveBeenLastCalledWith( '\\ Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 5 ); - expect( writeStub.getCall( 4 ).args[ 0 ] ).to.equal( '| Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 5 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| Foo.' ); // It does not clear the last line for an initial spin. - expect( stubs.readline.clearLine.callCount ).to.equal( 4 ); - expect( stubs.readline.cursorTo.callCount ).to.equal( 4 ); + expect( vi.mocked( readline ).clearLine ).toHaveBeenCalledTimes( 4 ); + expect( vi.mocked( readline ).cursorTo ).toHaveBeenCalledTimes( 4 ); - expect( stubs.readline.clearLine.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.clearLine.firstCall.args[ 1 ] ).to.equal( 1 ); + expect( vi.mocked( readline ).clearLine ).toHaveBeenCalledWith( process.stdout, 1 ); + expect( vi.mocked( readline ).cursorTo ).toHaveBeenCalledWith( process.stdout, 0 ); + } ); - expect( stubs.readline.cursorTo.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.cursorTo.firstCall.args[ 1 ] ).to.equal( 0 ); + it( 'uses "setInterval" for creating a loop', () => { + const spinner = createSpinner( 'Foo.', { total: 10 } ); + + spinner.start(); - writeStub.restore(); + expect( vi.getTimerCount() ).toEqual( 1 ); } ); it( 'hides a cursor when starting spinning', () => { @@ -160,7 +128,7 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - expect( stubs.cliCursor.hide.calledOnce ).to.equal( true ); + expect( vi.mocked( cliCursorHide ) ).toHaveBeenCalledOnce(); } ); it( 'allows indenting messages by specifying the "options.indentLevel" option', () => { @@ -168,168 +136,130 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - const writeStub = sinon.stub( process.stdout, 'write' ); + const writeStub = vi.spyOn( process.stdout, 'write' ).mockImplementation( () => {} ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 1 ); - expect( writeStub.getCall( 0 ).args[ 0 ] ).to.equal( ' | Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 1 ); + expect( writeStub ).toHaveBeenLastCalledWith( ' | Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 2 ); - expect( writeStub.getCall( 1 ).args[ 0 ] ).to.equal( ' / Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 2 ); + expect( writeStub ).toHaveBeenLastCalledWith( ' / Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 3 ); - expect( writeStub.getCall( 2 ).args[ 0 ] ).to.equal( ' - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 3 ); + expect( writeStub ).toHaveBeenLastCalledWith( ' - Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 4 ); - expect( writeStub.getCall( 3 ).args[ 0 ] ).to.equal( ' \\ Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 4 ); + expect( writeStub ).toHaveBeenLastCalledWith( ' \\ Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 5 ); - expect( writeStub.getCall( 4 ).args[ 0 ] ).to.equal( ' | Foo.' ); - - writeStub.restore(); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 5 ); + expect( writeStub ).toHaveBeenLastCalledWith( ' | Foo.' ); } ); } ); describe( '#finish', () => { beforeEach( () => { - stubs.isInteractive.returns( true ); + vi.mocked( isInteractive ).mockReturnValue( true ); } ); it( 'does nothing if spinner should be disabled', () => { const spinner = createSpinner( 'Foo.', { isDisabled: true } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.finish(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( false ); + expect( vi.mocked( console ).log ).not.toHaveBeenCalled(); } ); it( 'does nothing if spinner cannot be created if CLI is not interactive', () => { - stubs.isInteractive.returns( false ); + vi.mocked( isInteractive ).mockReturnValue( false ); const spinner = createSpinner( 'Foo.' ); - const consoleStub = sinon.stub( console, 'log' ); spinner.finish(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( false ); + expect( vi.mocked( console ).log ).not.toHaveBeenCalled(); } ); it( 'clears the interval when finished', () => { const spinner = createSpinner( 'Foo.' ); - const consoleStub = sinon.stub( console, 'log' ); + + expect( vi.getTimerCount() ).toEqual( 0 ); spinner.start(); - const timer = Object.values( clock.timers ).shift(); - expect( timer ).to.be.an( 'object' ); + expect( vi.getTimerCount() ).toEqual( 1 ); spinner.finish(); - const newTimer = Object.values( clock.timers ).shift(); - - expect( timer ).to.be.an( 'object' ); - expect( newTimer ).to.be.undefined; - - consoleStub.restore(); + expect( vi.getTimerCount() ).toEqual( 0 ); } ); it( 'prints the specified title with a pin when finished', () => { const spinner = createSpinner( 'Foo.' ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); spinner.finish(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '📍 Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '📍 Foo.' ); } ); it( 'shows a cursor when finished spinning', () => { const spinner = createSpinner( 'Foo.' ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); spinner.finish(); - consoleStub.restore(); - expect( stubs.cliCursor.show.calledOnce ).to.equal( true ); + expect( vi.mocked( cliCursorShow ) ).toHaveBeenCalledOnce(); } ); it( 'allows indenting messages by specifying the "options.indentLevel" option', () => { const spinner = createSpinner( 'Foo.', { indentLevel: 1 } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); spinner.finish(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( ' 📍 Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( ' 📍 Foo.' ); } ); it( 'prints the specified emoji when created a spinner if it finished', () => { const spinner = createSpinner( 'Foo.', { emoji: '👉' } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); spinner.finish(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '👉 Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '👉 Foo.' ); } ); it( 'prints the specified title if spinner cannot be created if CLI is not interactive', () => { - stubs.isInteractive.returns( false ); + vi.mocked( isInteractive ).mockReturnValue( true ); const spinner = createSpinner( 'Foo.', { emoji: '👉' } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); + spinner.finish(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '👉 Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '👉 Foo.' ); } ); it( 'allows overriding the emoji (use default emoji when creating a spinner)', () => { const spinner = createSpinner( 'Foo.' ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); spinner.finish( { emoji: '❌' } ); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '❌ Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '❌ Foo.' ); } ); it( 'allows overriding the emoji (passed an emoji when creating a spinner)', () => { const spinner = createSpinner( 'Foo.', { emoji: '👉' } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); spinner.finish( { emoji: '❌' } ); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '❌ Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '❌ Foo.' ); } ); } ); @@ -339,28 +269,25 @@ describe( 'lib/utils/create-spinner', () => { expect( () => { spinner.increase(); - } ).to.throw( Error, 'The \'#increase()\' method is available only when using the counter spinner.' ); + } ).toThrow( 'The \'#increase()\' method is available only when using the counter spinner.' ); } ); } ); } ); + describe( 'type: counter', () => { beforeEach( () => { - stubs.isInteractive.returns( true ); + vi.mocked( isInteractive ).mockReturnValue( true ); } ); describe( '#start', () => { it( 'prints the specified title if spinner cannot be created if CLI is not interactive', () => { - stubs.isInteractive.returns( false ); + vi.mocked( isInteractive ).mockReturnValue( false ); const spinner = createSpinner( 'Foo.', { total: 10 } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '📍 Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '📍 Foo.' ); } ); it( 'uses "setInterval" for creating a loop', () => { @@ -368,10 +295,7 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - const timer = Object.values( clock.timers ).shift(); - - expect( timer ).to.be.an( 'object' ); - expect( timer.type ).to.equal( 'Interval' ); + expect( vi.getTimerCount() ).toEqual( 1 ); } ); it( 'prints always spinner in the last line', () => { @@ -379,39 +303,34 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - const writeStub = sinon.stub( process.stdout, 'write' ); + const writeStub = vi.spyOn( process.stdout, 'write' ).mockImplementation( () => {} ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 1 ); - expect( writeStub.getCall( 0 ).args[ 0 ] ).to.equal( '| Foo. Status: 0/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 1 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| Foo. Status: 0/10.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 2 ); - expect( writeStub.getCall( 1 ).args[ 0 ] ).to.equal( '/ Foo. Status: 0/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 2 ); + expect( writeStub ).toHaveBeenLastCalledWith( '/ Foo. Status: 0/10.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 3 ); - expect( writeStub.getCall( 2 ).args[ 0 ] ).to.equal( '- Foo. Status: 0/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 3 ); + expect( writeStub ).toHaveBeenLastCalledWith( '- Foo. Status: 0/10.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 4 ); - expect( writeStub.getCall( 3 ).args[ 0 ] ).to.equal( '\\ Foo. Status: 0/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 4 ); + expect( writeStub ).toHaveBeenLastCalledWith( '\\ Foo. Status: 0/10.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 5 ); - expect( writeStub.getCall( 4 ).args[ 0 ] ).to.equal( '| Foo. Status: 0/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 5 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| Foo. Status: 0/10.' ); // It does not clear the last line for an initial spin. - expect( stubs.readline.clearLine.callCount ).to.equal( 4 ); - expect( stubs.readline.cursorTo.callCount ).to.equal( 4 ); + expect( vi.mocked( readline ).clearLine ).toHaveBeenCalledTimes( 4 ); + expect( vi.mocked( readline ).cursorTo ).toHaveBeenCalledTimes( 4 ); - expect( stubs.readline.clearLine.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.clearLine.firstCall.args[ 1 ] ).to.equal( 1 ); - - expect( stubs.readline.cursorTo.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.cursorTo.firstCall.args[ 1 ] ).to.equal( 0 ); - - writeStub.restore(); + expect( vi.mocked( readline ).clearLine ).toHaveBeenCalledWith( process.stdout, 1 ); + expect( vi.mocked( readline ).cursorTo ).toHaveBeenCalledWith( process.stdout, 0 ); } ); it( 'allows defining a custom progress status (as a string)', () => { @@ -419,39 +338,27 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - const writeStub = sinon.stub( process.stdout, 'write' ); - - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 1 ); - expect( writeStub.getCall( 0 ).args[ 0 ] ).to.equal( '| 0 (10) - Foo.' ); + const writeStub = vi.spyOn( process.stdout, 'write' ).mockImplementation( () => {} ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 2 ); - expect( writeStub.getCall( 1 ).args[ 0 ] ).to.equal( '/ 0 (10) - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 1 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| 0 (10) - Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 3 ); - expect( writeStub.getCall( 2 ).args[ 0 ] ).to.equal( '- 0 (10) - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 2 ); + expect( writeStub ).toHaveBeenLastCalledWith( '/ 0 (10) - Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 4 ); - expect( writeStub.getCall( 3 ).args[ 0 ] ).to.equal( '\\ 0 (10) - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 3 ); + expect( writeStub ).toHaveBeenLastCalledWith( '- 0 (10) - Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 5 ); - expect( writeStub.getCall( 4 ).args[ 0 ] ).to.equal( '| 0 (10) - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 4 ); + expect( writeStub ).toHaveBeenLastCalledWith( '\\ 0 (10) - Foo.' ); - // It does not clear the last line for an initial spin. - expect( stubs.readline.clearLine.callCount ).to.equal( 4 ); - expect( stubs.readline.cursorTo.callCount ).to.equal( 4 ); - - expect( stubs.readline.clearLine.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.clearLine.firstCall.args[ 1 ] ).to.equal( 1 ); - - expect( stubs.readline.cursorTo.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.cursorTo.firstCall.args[ 1 ] ).to.equal( 0 ); - - writeStub.restore(); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 5 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| 0 (10) - Foo.' ); } ); it( 'allows defining a custom progress status (as a callback)', () => { @@ -464,54 +371,38 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - const writeStub = sinon.stub( process.stdout, 'write' ); - - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 1 ); - expect( writeStub.getCall( 0 ).args[ 0 ] ).to.equal( '| 0 (10) - Foo.' ); - - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 2 ); - expect( writeStub.getCall( 1 ).args[ 0 ] ).to.equal( '/ 0 (10) - Foo.' ); + const writeStub = vi.spyOn( process.stdout, 'write' ).mockImplementation( () => {} ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 3 ); - expect( writeStub.getCall( 2 ).args[ 0 ] ).to.equal( '- 0 (10) - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 1 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| 0 (10) - Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 4 ); - expect( writeStub.getCall( 3 ).args[ 0 ] ).to.equal( '\\ 0 (10) - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 2 ); + expect( writeStub ).toHaveBeenLastCalledWith( '/ 0 (10) - Foo.' ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 5 ); - expect( writeStub.getCall( 4 ).args[ 0 ] ).to.equal( '| 0 (10) - Foo.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 3 ); + expect( writeStub ).toHaveBeenLastCalledWith( '- 0 (10) - Foo.' ); - // It does not clear the last line for an initial spin. - expect( stubs.readline.clearLine.callCount ).to.equal( 4 ); - expect( stubs.readline.cursorTo.callCount ).to.equal( 4 ); - - expect( stubs.readline.clearLine.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.clearLine.firstCall.args[ 1 ] ).to.equal( 1 ); - - expect( stubs.readline.cursorTo.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.cursorTo.firstCall.args[ 1 ] ).to.equal( 0 ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 4 ); + expect( writeStub ).toHaveBeenLastCalledWith( '\\ 0 (10) - Foo.' ); - writeStub.restore(); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 5 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| 0 (10) - Foo.' ); } ); } ); describe( '#finish', () => { it( 'prints the specified title with a pin when finished', () => { const spinner = createSpinner( 'Foo.', { total: 10 } ); - const consoleStub = sinon.stub( console, 'log' ); spinner.start(); spinner.finish(); - consoleStub.restore(); - - expect( consoleStub.calledOnce ).to.equal( true ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( '📍 Foo.' ); + expect( vi.mocked( console ).log ).toHaveBeenCalledExactlyOnceWith( '📍 Foo.' ); } ); } ); @@ -521,47 +412,42 @@ describe( 'lib/utils/create-spinner', () => { spinner.start(); - const writeStub = sinon.stub( process.stdout, 'write' ); + const writeStub = vi.spyOn( process.stdout, 'write' ).mockImplementation( () => {} ); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 1 ); - expect( writeStub.getCall( 0 ).args[ 0 ] ).to.equal( '| Foo. Status: 0/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 1 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| Foo. Status: 0/10.' ); spinner.increase(); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 2 ); - expect( writeStub.getCall( 1 ).args[ 0 ] ).to.equal( '/ Foo. Status: 1/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 2 ); + expect( writeStub ).toHaveBeenLastCalledWith( '/ Foo. Status: 1/10.' ); spinner.increase(); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 3 ); - expect( writeStub.getCall( 2 ).args[ 0 ] ).to.equal( '- Foo. Status: 2/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 3 ); + expect( writeStub ).toHaveBeenLastCalledWith( '- Foo. Status: 2/10.' ); spinner.increase(); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 4 ); - expect( writeStub.getCall( 3 ).args[ 0 ] ).to.equal( '\\ Foo. Status: 3/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 4 ); + expect( writeStub ).toHaveBeenLastCalledWith( '\\ Foo. Status: 3/10.' ); spinner.increase(); - clock.tick( 5 ); - expect( writeStub.callCount ).to.equal( 5 ); - expect( writeStub.getCall( 4 ).args[ 0 ] ).to.equal( '| Foo. Status: 4/10.' ); + vi.advanceTimersByTime( 5 ); + expect( writeStub ).toHaveBeenCalledTimes( 5 ); + expect( writeStub ).toHaveBeenLastCalledWith( '| Foo. Status: 4/10.' ); // It does not clear the last line for an initial spin. - expect( stubs.readline.clearLine.callCount ).to.equal( 4 ); - expect( stubs.readline.cursorTo.callCount ).to.equal( 4 ); - - expect( stubs.readline.clearLine.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.clearLine.firstCall.args[ 1 ] ).to.equal( 1 ); - - expect( stubs.readline.cursorTo.firstCall.args[ 0 ] ).to.equal( process.stdout ); - expect( stubs.readline.cursorTo.firstCall.args[ 1 ] ).to.equal( 0 ); + expect( vi.mocked( readline ).clearLine ).toHaveBeenCalledTimes( 4 ); + expect( vi.mocked( readline ).cursorTo ).toHaveBeenCalledTimes( 4 ); - writeStub.restore(); + expect( vi.mocked( readline ).clearLine ).toHaveBeenCalledWith( process.stdout, 1 ); + expect( vi.mocked( readline ).cursorTo ).toHaveBeenCalledWith( process.stdout, 0 ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-utils/tests/tools/getdirectories.js b/packages/ckeditor5-dev-utils/tests/tools/getdirectories.js new file mode 100644 index 000000000..646f92824 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/tools/getdirectories.js @@ -0,0 +1,34 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; +import path from 'path'; +import { describe, expect, it, vi } from 'vitest'; +import getDirectories from '../../lib/tools/getdirectories.js'; + +vi.mock( 'fs' ); + +describe( 'getDirectories()', () => { + it( 'should get directories in specified path', () => { + const directories = [ 'dir1', 'dir2', 'dir3' ]; + + vi.mocked( fs ).readdirSync.mockReturnValue( directories ); + vi.mocked( fs ).statSync.mockReturnValue( { + isDirectory: () => { + return true; + } + } ); + + const dirPath = 'path'; + + getDirectories( dirPath ); + + expect( vi.mocked( fs ).readdirSync ).toHaveBeenCalledExactlyOnceWith( dirPath ); + expect( vi.mocked( fs ).statSync ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs ).statSync ).toHaveBeenCalledWith( path.join( dirPath, directories[ 0 ] ) ); + expect( vi.mocked( fs ).statSync ).toHaveBeenCalledWith( path.join( dirPath, directories[ 1 ] ) ); + expect( vi.mocked( fs ).statSync ).toHaveBeenCalledWith( path.join( dirPath, directories[ 2 ] ) ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/tools/index.js b/packages/ckeditor5-dev-utils/tests/tools/index.js new file mode 100644 index 000000000..37cde1c37 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/tools/index.js @@ -0,0 +1,51 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import * as tools from '../../lib/tools/index.js'; +import shExec from '../../lib/tools/shexec.js'; +import createSpinner from '../../lib/tools/createspinner.js'; +import getDirectories from '../../lib/tools/getdirectories.js'; +import updateJSONFile from '../../lib/tools/updatejsonfile.js'; + +vi.mock( '../../lib/tools/shexec.js' ); +vi.mock( '../../lib/tools/createspinner.js' ); +vi.mock( '../../lib/tools/getdirectories.js' ); +vi.mock( '../../lib/tools/updatejsonfile.js' ); + +describe( 'tools/index.js', () => { + describe( 'createSpinner()', () => { + it( 'should be a function', () => { + expect( tools.createSpinner ).to.be.a( 'function' ); + expect( tools.createSpinner ).toEqual( createSpinner ); + } ); + } ); + + describe( 'getDirectories()', () => { + it( 'should be a function', () => { + expect( tools.getDirectories ).to.be.a( 'function' ); + expect( tools.getDirectories ).toEqual( getDirectories ); + } ); + } ); + + describe( 'shExec()', () => { + it( 'should be a function', () => { + expect( tools.shExec ).to.be.a( 'function' ); + expect( tools.shExec ).toEqual( shExec ); + } ); + } ); + + describe( 'updateJSONFile()', () => { + it( 'should be a function', () => { + expect( tools.updateJSONFile ).to.be.a( 'function' ); + expect( tools.updateJSONFile ).toEqual( updateJSONFile ); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/tools/shexec.js b/packages/ckeditor5-dev-utils/tests/tools/shexec.js new file mode 100644 index 000000000..6817392e2 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/tools/shexec.js @@ -0,0 +1,171 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import sh from 'shelljs'; +import shExec from '../../lib/tools/shexec.js'; +import logger from '../../lib/logger/index.js'; + +vi.mock( 'shelljs' ); +vi.mock( '../../lib/logger/index.js' ); + +describe( 'shExec()', () => { + let stubs; + + beforeEach( () => { + stubs = { + infoSpy: vi.fn(), + errorSpy: vi.fn() + }; + + vi.spyOn( process, 'cwd' ).mockReturnValue( '/default' ); + + vi.mocked( logger ).mockReturnValue( { + info: stubs.infoSpy, + error: stubs.errorSpy + } ); + } ); + + describe( 'sync', () => { + it( 'should execute a specified command using the default cwd', () => { + vi.mocked( sh ).exec.mockReturnValue( { code: 0 } ); + + shExec( 'command' ); + + expect( vi.mocked( process ).cwd ).toHaveBeenCalledOnce(); + expect( vi.mocked( sh ).exec ).toHaveBeenCalledExactlyOnceWith( + 'command', + expect.objectContaining( { + cwd: '/default' + } ) + ); + } ); + + it( 'should execute command with specified cwd', () => { + const cwd = '/home/user'; + + vi.mocked( sh ).exec.mockReturnValue( { code: 0 } ); + + shExec( 'command', { cwd } ); + + expect( vi.mocked( sh ).exec ).toHaveBeenCalledExactlyOnceWith( + 'command', + expect.objectContaining( { + cwd: '/home/user' + } ) + ); + } ); + + it( 'should throw error on unsuccessful call', () => { + vi.mocked( sh ).exec.mockReturnValue( { code: 1 } ); + + expect( () => { + shExec( 'command' ); + } ).to.throw(); + + expect( vi.mocked( sh ).exec ).toHaveBeenCalledOnce(); + } ); + + it( 'should output using log functions when exit code is equal to 0', () => { + vi.mocked( sh ).exec.mockReturnValue( { code: 0, stdout: 'out', stderr: 'err' } ); + + shExec( 'command' ); + + expect( vi.mocked( sh ).exec ).toHaveBeenCalledOnce(); + expect( vi.mocked( logger ) ).toHaveBeenCalledExactlyOnceWith( 'info' ); + expect( stubs.errorSpy ).not.toHaveBeenCalled(); + expect( stubs.infoSpy ).toHaveBeenCalledTimes( 2 ); + expect( stubs.infoSpy ).toHaveBeenCalledWith( expect.stringContaining( 'out' ) ); + expect( stubs.infoSpy ).toHaveBeenCalledWith( expect.stringContaining( 'err' ) ); + } ); + + it( 'should output using log functions when exit code is not equal to 0', () => { + vi.mocked( sh ).exec.mockReturnValue( { code: 1, stdout: 'out', stderr: 'err' } ); + + expect( () => { + shExec( 'command' ); + } ).to.throw(); + + expect( vi.mocked( sh ).exec ).toHaveBeenCalledOnce(); + expect( vi.mocked( logger ) ).toHaveBeenCalledExactlyOnceWith( 'info' ); + expect( stubs.infoSpy ).not.toHaveBeenCalled(); + expect( stubs.errorSpy ).toHaveBeenCalledTimes( 2 ); + expect( stubs.errorSpy ).toHaveBeenCalledWith( expect.stringContaining( 'out' ) ); + expect( stubs.errorSpy ).toHaveBeenCalledWith( expect.stringContaining( 'err' ) ); + } ); + + it( 'should not log if no output from executed command', () => { + vi.mocked( sh ).exec.mockReturnValue( { code: 0, stdout: '', stderr: '' } ); + + shExec( 'command', { verbosity: 'error' } ); + + expect( vi.mocked( logger ) ).toHaveBeenCalledExactlyOnceWith( 'error' ); + expect( stubs.infoSpy ).not.toHaveBeenCalled(); + expect( stubs.errorSpy ).not.toHaveBeenCalled(); + } ); + } ); + + describe( 'async', () => { + it( 'should return a promise when executing a command in asynchronous mode', async () => { + vi.mocked( sh ).exec.mockImplementation( ( command, options, callback ) => { + callback( 0 ); + } ); + + await shExec( 'command', { async: true } ); + expect( vi.mocked( sh ).exec ).toHaveBeenCalledOnce(); + } ); + + it( 'should throw error on unsuccessful call in asynchronous mode', async () => { + vi.mocked( sh ).exec.mockImplementation( ( command, options, callback ) => { + callback( 1 ); + } ); + + await expect( shExec( 'command', { async: true } ) ).rejects.toThrow(); + expect( vi.mocked( sh ).exec ).toHaveBeenCalledOnce(); + } ); + + it( 'should output using log functions when exit code is equal to 0 in asynchronous mode', async () => { + vi.mocked( sh ).exec.mockImplementation( ( command, options, callback ) => { + callback( 0, 'out', 'err' ); + } ); + + await shExec( 'command', { async: true } ); + + expect( vi.mocked( sh ).exec ).toHaveBeenCalledOnce(); + expect( vi.mocked( logger ) ).toHaveBeenCalledExactlyOnceWith( 'info' ); + expect( stubs.errorSpy ).not.toHaveBeenCalled(); + expect( stubs.infoSpy ).toHaveBeenCalledTimes( 2 ); + expect( stubs.infoSpy ).toHaveBeenCalledWith( expect.stringContaining( 'out' ) ); + expect( stubs.infoSpy ).toHaveBeenCalledWith( expect.stringContaining( 'err' ) ); + } ); + + it( 'should output using log functions when exit code is not equal to 0 in asynchronous mode', async () => { + vi.mocked( sh ).exec.mockImplementation( ( command, options, callback ) => { + callback( 1, 'out', 'err' ); + } ); + + await expect( shExec( 'command', { async: true } ) ).rejects.toThrow(); + + expect( vi.mocked( sh ).exec ).toHaveBeenCalledOnce(); + expect( vi.mocked( logger ) ).toHaveBeenCalledExactlyOnceWith( 'info' ); + expect( stubs.infoSpy ).not.toHaveBeenCalled(); + expect( stubs.errorSpy ).toHaveBeenCalledTimes( 2 ); + expect( stubs.errorSpy ).toHaveBeenCalledWith( expect.stringContaining( 'out' ) ); + expect( stubs.errorSpy ).toHaveBeenCalledWith( expect.stringContaining( 'err' ) ); + } ); + + it( 'should not log if no output from executed command in asynchronous mode', async () => { + vi.mocked( sh ).exec.mockImplementation( ( command, options, callback ) => { + callback( 0, '', '' ); + } ); + + await shExec( 'command', { verbosity: 'error', async: true } ); + + expect( vi.mocked( logger ) ).toHaveBeenCalledExactlyOnceWith( 'error' ); + expect( stubs.infoSpy ).not.toHaveBeenCalled(); + expect( stubs.errorSpy ).not.toHaveBeenCalled(); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-utils/tests/tools/updatejsonfile.js b/packages/ckeditor5-dev-utils/tests/tools/updatejsonfile.js new file mode 100644 index 000000000..a78a7a4f0 --- /dev/null +++ b/packages/ckeditor5-dev-utils/tests/tools/updatejsonfile.js @@ -0,0 +1,30 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import fs from 'fs'; +import { describe, expect, it, vi } from 'vitest'; +import updateJSONFile from '../../lib/tools/updatejsonfile.js'; + +vi.mock( 'fs' ); + +describe( 'updateJSONFile()', () => { + it( 'should read, update and save JSON file', () => { + vi.mocked( fs ).readFileSync.mockReturnValue( '{}' ); + + const path = 'path/to/file.json'; + const modifiedJSON = { modified: true }; + + updateJSONFile( path, () => { + return modifiedJSON; + } ); + + expect( vi.mocked( fs ).readFileSync ).toHaveBeenCalledExactlyOnceWith( path, 'utf-8' ); + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + path, + JSON.stringify( modifiedJSON, null, 2 ) + '\n', + 'utf-8' + ); + } ); +} ); From 976f541fbe28c019b08ab9a0931b2c71e98a786c Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 12 Sep 2024 13:42:27 +0200 Subject: [PATCH 084/172] Fixed invalid re-exports. --- packages/ckeditor5-dev-bump-year/lib/index.js | 6 +----- .../ckeditor5-dev-dependency-checker/lib/index.js | 6 +----- packages/ckeditor5-dev-web-crawler/lib/index.js | 14 +++----------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/packages/ckeditor5-dev-bump-year/lib/index.js b/packages/ckeditor5-dev-bump-year/lib/index.js index 7cac1b92e..26fc3942f 100644 --- a/packages/ckeditor5-dev-bump-year/lib/index.js +++ b/packages/ckeditor5-dev-bump-year/lib/index.js @@ -3,8 +3,4 @@ * For licensing, see LICENSE.md. */ -import bumpYear from './bumpyear.js'; - -export default { - bumpYear -}; +export { default as bumpYear } from './bumpyear.js'; diff --git a/packages/ckeditor5-dev-dependency-checker/lib/index.js b/packages/ckeditor5-dev-dependency-checker/lib/index.js index 464670608..6d0a89b7b 100644 --- a/packages/ckeditor5-dev-dependency-checker/lib/index.js +++ b/packages/ckeditor5-dev-dependency-checker/lib/index.js @@ -3,8 +3,4 @@ * For licensing, see LICENSE.md. */ -import checkDependencies from './checkdependencies.js'; - -export default { - checkDependencies -}; +export { default as checkDependencies } from './checkdependencies.js'; diff --git a/packages/ckeditor5-dev-web-crawler/lib/index.js b/packages/ckeditor5-dev-web-crawler/lib/index.js index 4a5e28e0e..766b5ad17 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/index.js +++ b/packages/ckeditor5-dev-web-crawler/lib/index.js @@ -7,14 +7,6 @@ /* eslint-env node */ -import runCrawler from './runcrawler.js'; -import { getBaseUrl, isUrlValid, toArray } from './utils.js'; -import { DEFAULT_CONCURRENCY } from './constants.js'; - -export default { - DEFAULT_CONCURRENCY, - runCrawler, - getBaseUrl, - isUrlValid, - toArray -}; +export { default as runCrawler } from './runcrawler.js'; +export { getBaseUrl, isUrlValid, toArray } from './utils.js'; +export { DEFAULT_CONCURRENCY } from './constants.js'; From e76791495ce0a5e862b1d04009aa58fb30693527 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 12 Sep 2024 13:57:50 +0200 Subject: [PATCH 085/172] Added missing mocks. --- .../tests/utils/getpackagespaths.js | 3 ++- .../tests/builds/getdllpluginwebpackconfig.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js index baeb5a958..77f8a7a2c 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getpackagespaths.js @@ -10,7 +10,8 @@ import getPackagesPaths from '../../lib/utils/getpackagespaths.js'; vi.mock( 'path', () => ( { default: { - join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ) + join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ), + dirname: vi.fn() } } ) ); vi.mock( '@ckeditor/ckeditor5-dev-utils' ); diff --git a/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js b/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js index c6ff9bd02..4187c0f46 100644 --- a/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js +++ b/packages/ckeditor5-dev-utils/tests/builds/getdllpluginwebpackconfig.js @@ -39,7 +39,8 @@ vi.mock( '../../lib/bundler/index.js' ); vi.mock( 'fs-extra' ); vi.mock( 'path', () => ( { default: { - join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ) + join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ), + dirname: vi.fn() } } ) ); vi.mock( '@ckeditor/ckeditor5-dev-translations', () => ( { From e7ef25b573449303943a38d659c5b98a2016aaff Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 12 Sep 2024 14:10:45 +0200 Subject: [PATCH 086/172] Fixed logger re-exports to avoid BC. --- packages/ckeditor5-dev-utils/lib/index.js | 2 +- packages/ckeditor5-dev-utils/tests/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-utils/lib/index.js b/packages/ckeditor5-dev-utils/lib/index.js index 2e1f29838..c3360defe 100644 --- a/packages/ckeditor5-dev-utils/lib/index.js +++ b/packages/ckeditor5-dev-utils/lib/index.js @@ -3,10 +3,10 @@ * For licensing, see LICENSE.md. */ +export { default as logger } from './logger/index.js'; export * as builds from './builds/index.js'; export * as bundler from './bundler/index.js'; export * as loaders from './loaders/index.js'; -export * as logger from './logger/index.js'; export * as stream from './stream/index.js'; export * as styles from './styles/index.js'; export * as tools from './tools/index.js'; diff --git a/packages/ckeditor5-dev-utils/tests/index.js b/packages/ckeditor5-dev-utils/tests/index.js index 00eea34bf..f3887f2cd 100644 --- a/packages/ckeditor5-dev-utils/tests/index.js +++ b/packages/ckeditor5-dev-utils/tests/index.js @@ -4,10 +4,10 @@ */ import { describe, expect, it, vi } from 'vitest'; +import logger from '../lib/logger/index.js'; import * as packageUtils from '../lib/index.js'; import * as bundler from '../lib/bundler/index.js'; import * as loaders from '../lib/loaders/index.js'; -import * as logger from '../lib/logger/index.js'; import * as builds from '../lib/builds/index.js'; import * as stream from '../lib/stream/index.js'; import * as styles from '../lib/styles/index.js'; From 9efd9db5cced5ad4d68028138ceb3eb94f299637 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 12 Sep 2024 14:23:36 +0200 Subject: [PATCH 087/172] Build-tools: Added a missing dependency in a mocked package. --- packages/ckeditor5-dev-build-tools/tests/build/build.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-build-tools/tests/build/build.test.ts b/packages/ckeditor5-dev-build-tools/tests/build/build.test.ts index 85f597361..d91a25f68 100644 --- a/packages/ckeditor5-dev-build-tools/tests/build/build.test.ts +++ b/packages/ckeditor5-dev-build-tools/tests/build/build.test.ts @@ -64,7 +64,8 @@ async function mockCommercialDependencies() { () => ( { name: 'ckeditor5-premium-features', dependencies: { - '@ckeditor/ckeditor5-ai': '*' + '@ckeditor/ckeditor5-ai': '*', + 'ckeditor5-collaboration': '*' } } ) ); From 4fa72f7b43d1c17bb5a90d0299d2e5b5cc8230d6 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 12 Sep 2024 18:18:52 +0200 Subject: [PATCH 088/172] Make release scripts ready for ESM. --- .eslintrc.js => .eslintrc.cjs | 1 + package.json | 1 + scripts/changelog.js | 6 +-- scripts/ci/is-project-ready-to-release.js | 7 +--- scripts/postinstall.js | 46 +++++++++++++---------- scripts/preparepackages.js | 18 ++++----- scripts/publishpackages.js | 15 +++----- scripts/runtest.js | 14 +++---- scripts/utils/constants.js | 14 ++++--- scripts/utils/getlistroptions.js | 6 +-- scripts/utils/parsearguments.js | 11 +++--- scripts/utils/runbuildcommand.js | 17 +++------ 12 files changed, 73 insertions(+), 83 deletions(-) rename .eslintrc.js => .eslintrc.cjs (98%) diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 98% rename from .eslintrc.js rename to .eslintrc.cjs index a0c5542d9..626d46230 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -36,6 +36,7 @@ module.exports = { { files: [ // TODO: add packages as they are migrated to ESM. + './scripts/**/*', './packages/ckeditor5-dev-utils/**/*', './packages/ckeditor5-dev-translations/**/*', './packages/ckeditor5-dev-release-tools/**/*', diff --git a/package.json b/package.json index cabf2e784..11c9d2be8 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "engines": { "node": ">=18.0.0" }, + "type": "module", "devDependencies": { "@ckeditor/ckeditor5-dev-ci": "^43.0.0", "@ckeditor/ckeditor5-dev-release-tools": "^43.0.0", diff --git a/scripts/changelog.js b/scripts/changelog.js index af403d932..8ff5952be 100755 --- a/scripts/changelog.js +++ b/scripts/changelog.js @@ -5,10 +5,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { generateChangelogForMonoRepository } = require( '@ckeditor/ckeditor5-dev-release-tools' ); -const parseArguments = require( './utils/parsearguments' ); +import { generateChangelogForMonoRepository } from '@ckeditor/ckeditor5-dev-release-tools'; +import parseArguments from './utils/parsearguments.js'; const cliArguments = parseArguments( process.argv.slice( 2 ) ); diff --git a/scripts/ci/is-project-ready-to-release.js b/scripts/ci/is-project-ready-to-release.js index 5f7af4b3c..3d9cf254d 100644 --- a/scripts/ci/is-project-ready-to-release.js +++ b/scripts/ci/is-project-ready-to-release.js @@ -7,15 +7,12 @@ /* eslint-env node */ -'use strict'; - -const releaseTools = require( '@ckeditor/ckeditor5-dev-release-tools' ); -const { name: packageName } = require( '@ckeditor/ckeditor5-dev-release-tools/package.json' ); +import releaseTools from '@ckeditor/ckeditor5-dev-release-tools'; const changelogVersion = releaseTools.getLastFromChangelog(); const npmTag = releaseTools.getNpmTagFromVersion( changelogVersion ); -releaseTools.isVersionPublishableForTag( packageName, changelogVersion, npmTag ) +releaseTools.isVersionPublishableForTag( '@ckeditor/ckeditor5-dev-release-tools', changelogVersion, npmTag ) .then( result => { if ( !result ) { console.error( `The proposed changelog (${ changelogVersion }) version is not higher than the already published one.` ); diff --git a/scripts/postinstall.js b/scripts/postinstall.js index b02721bdd..4335df430 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -3,28 +3,34 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /* eslint-env node */ -const path = require( 'path' ); -const fs = require( 'fs' ); -const { execSync } = require( 'child_process' ); +import path from 'path'; +import fs from 'fs'; +import { execSync } from 'child_process'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); const ROOT_DIRECTORY = path.join( __dirname, '..' ); -// When installing a repository as a dependency, the `.git` directory does not exist. -// In such a case, husky should not attach its hooks as npm treats it as a package, not a git repository. -if ( fs.existsSync( path.join( ROOT_DIRECTORY, '.git' ) ) ) { - require( 'husky' ).install(); - - execSync( 'npm run postinstall', { - cwd: path.join( ROOT_DIRECTORY, 'packages', 'ckeditor5-dev-tests' ), - stdio: 'inherit' - } ); - - execSync( 'npm run build', { - cwd: path.join( ROOT_DIRECTORY, 'packages', 'ckeditor5-dev-build-tools' ), - stdio: 'inherit' - } ); -} +( async () => { + // When installing a repository as a dependency, the `.git` directory does not exist. + // In such a case, husky should not attach its hooks as npm treats it as a package, not a git repository. + if ( fs.existsSync( path.join( ROOT_DIRECTORY, '.git' ) ) ) { + const husky = ( await import( 'husky' ) ).default; + + husky.install(); + + execSync( 'npm run postinstall', { + cwd: path.join( ROOT_DIRECTORY, 'packages', 'ckeditor5-dev-tests' ), + stdio: 'inherit' + } ); + + execSync( 'npm run build', { + cwd: path.join( ROOT_DIRECTORY, 'packages', 'ckeditor5-dev-build-tools' ), + stdio: 'inherit' + } ); + } +} )(); diff --git a/scripts/preparepackages.js b/scripts/preparepackages.js index 5ebee8453..a6607c080 100644 --- a/scripts/preparepackages.js +++ b/scripts/preparepackages.js @@ -7,16 +7,14 @@ /* eslint-env node */ -'use strict'; - -const upath = require( 'upath' ); -const { Listr } = require( 'listr2' ); -const { globSync } = require( 'glob' ); -const releaseTools = require( '@ckeditor/ckeditor5-dev-release-tools' ); -const parseArguments = require( './utils/parsearguments' ); -const getListrOptions = require( './utils/getlistroptions' ); -const runBuildCommand = require( './utils/runbuildcommand' ); -const { CKEDITOR5_DEV_ROOT, PACKAGES_DIRECTORY, RELEASE_DIRECTORY } = require( './utils/constants' ); +import upath from 'upath'; +import { Listr } from 'listr2'; +import { globSync } from 'glob'; +import * as releaseTools from '@ckeditor/ckeditor5-dev-release-tools'; +import parseArguments from './utils/parsearguments.js'; +import getListrOptions from './utils/getlistroptions.js'; +import runBuildCommand from './utils/runbuildcommand.js'; +import { CKEDITOR5_DEV_ROOT, PACKAGES_DIRECTORY, RELEASE_DIRECTORY } from './utils/constants.js'; const cliArguments = parseArguments( process.argv.slice( 2 ) ); const latestVersion = releaseTools.getLastFromChangelog(); diff --git a/scripts/publishpackages.js b/scripts/publishpackages.js index 44851ae91..75fa01eaa 100644 --- a/scripts/publishpackages.js +++ b/scripts/publishpackages.js @@ -7,14 +7,11 @@ /* eslint-env node */ -'use strict'; - -const { Listr } = require( 'listr2' ); -const releaseTools = require( '@ckeditor/ckeditor5-dev-release-tools' ); -const { provideToken } = require( '@ckeditor/ckeditor5-dev-release-tools/lib/utils/cli' ); -const parseArguments = require( './utils/parsearguments' ); -const getListrOptions = require( './utils/getlistroptions' ); -const { RELEASE_DIRECTORY } = require( './utils/constants' ); +import { Listr } from 'listr2'; +import * as releaseTools from '@ckeditor/ckeditor5-dev-release-tools'; +import parseArguments from './utils/parsearguments.js'; +import getListrOptions from './utils/getlistroptions.js'; +import { RELEASE_DIRECTORY } from './utils/constants.js'; const cliArguments = parseArguments( process.argv.slice( 2 ) ); const latestVersion = releaseTools.getLastFromChangelog(); @@ -92,7 +89,7 @@ const tasks = new Listr( [ if ( process.env.CKE5_RELEASE_TOKEN ) { githubToken = process.env.CKE5_RELEASE_TOKEN; } else { - githubToken = await provideToken(); + githubToken = await releaseTools.provideToken(); } await tasks.run(); diff --git a/scripts/runtest.js b/scripts/runtest.js index 2b9f11970..37cc060e9 100644 --- a/scripts/runtest.js +++ b/scripts/runtest.js @@ -7,14 +7,12 @@ /* eslint-env node */ -'use strict'; - -const path = require( 'path' ); -const { execSync } = require( 'child_process' ); -const fs = require( 'fs-extra' ); -const { globSync } = require( 'glob' ); -const minimist = require( 'minimist' ); -const chalk = require( 'chalk' ); +import path from 'path'; +import { execSync } from 'child_process'; +import fs from 'fs-extra'; +import { globSync } from 'glob'; +import minimist from 'minimist'; +import chalk from 'chalk'; main(); diff --git a/scripts/utils/constants.js b/scripts/utils/constants.js index 3f5f2a0e9..64846ae46 100644 --- a/scripts/utils/constants.js +++ b/scripts/utils/constants.js @@ -3,10 +3,12 @@ * For licensing, see LICENSE.md. */ -const upath = require( 'upath' ); +import upath from 'upath'; +import { fileURLToPath } from 'url'; -module.exports = { - PACKAGES_DIRECTORY: 'packages', - RELEASE_DIRECTORY: 'release', - CKEDITOR5_DEV_ROOT: upath.join( __dirname, '..', '..' ) -}; +const __filename = fileURLToPath( import.meta.url ); +const __dirname = upath.dirname( __filename ); + +export const PACKAGES_DIRECTORY = 'packages'; +export const RELEASE_DIRECTORY = 'release'; +export const CKEDITOR5_DEV_ROOT = upath.join( __dirname, '..', '..' ); diff --git a/scripts/utils/getlistroptions.js b/scripts/utils/getlistroptions.js index 10f018105..9fae9db69 100644 --- a/scripts/utils/getlistroptions.js +++ b/scripts/utils/getlistroptions.js @@ -5,14 +5,12 @@ /* eslint-env node */ -'use strict'; - /** * @param {ReleaseOptions} cliArguments * @returns {Object} */ -module.exports = function getListrOptions( cliArguments ) { +export default function getListrOptions( cliArguments ) { return { renderer: cliArguments.verbose ? 'verbose' : 'default' }; -}; +} diff --git a/scripts/utils/parsearguments.js b/scripts/utils/parsearguments.js index 5ad862594..54e79d220 100644 --- a/scripts/utils/parsearguments.js +++ b/scripts/utils/parsearguments.js @@ -5,15 +5,14 @@ /* eslint-env node */ -'use strict'; - -const minimist = require( 'minimist' ); +import minimist from 'minimist'; +import os from 'os'; /** * @param {Array.} cliArguments * @returns {ReleaseOptions} options */ -module.exports = function parseArguments( cliArguments ) { +export default function parseArguments( cliArguments ) { const config = { boolean: [ 'verbose', @@ -33,7 +32,7 @@ module.exports = function parseArguments( cliArguments ) { ], default: { - concurrency: require( 'os' ).cpus().length / 2, + concurrency: os.cpus().length / 2, packages: null, ci: false, verbose: false, @@ -60,7 +59,7 @@ module.exports = function parseArguments( cliArguments ) { } return options; -}; +} /** * @typedef {Object} ReleaseOptions diff --git a/scripts/utils/runbuildcommand.js b/scripts/utils/runbuildcommand.js index 3b4427186..19926f4e1 100644 --- a/scripts/utils/runbuildcommand.js +++ b/scripts/utils/runbuildcommand.js @@ -3,21 +3,16 @@ * For licensing, see LICENSE.md. */ -/* eslint-env node */ - -'use strict'; - /** * @param {String} packagePath * @returns {Promise} */ -module.exports = async function runBuildCommand( packagePath ) { - const path = require( 'upath' ); - const { tools } = require( '@ckeditor/ckeditor5-dev-utils' ); +export default async function runBuildCommand( packagePath ) { + const path = ( await import( 'upath' ) ).default; + const { readJson } = ( await import( 'fs-extra' ) ).default; + const { tools } = await import( '@ckeditor/ckeditor5-dev-utils' ); - const packageJson = require( - path.join( packagePath, 'package.json' ) - ); + const packageJson = await readJson( path.join( packagePath, 'package.json' ) ); if ( !packageJson.scripts?.build ) { return; @@ -28,5 +23,5 @@ module.exports = async function runBuildCommand( packagePath ) { verbosity: 'error', async: true } ); -}; +} From d2fe47072b198ec3093d2e3c9c8888bd5380ab07 Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Fri, 13 Sep 2024 20:21:00 +0200 Subject: [PATCH 089/172] Converted more tests. --- .eslintrc.js | 1 + .../ckeditor5-dev-transifex/lib/download.js | 4 +- .../ckeditor5-dev-transifex/lib/upload.js | 8 +- .../ckeditor5-dev-transifex/tests/download.js | 469 ++++++++++-------- .../ckeditor5-dev-transifex/tests/upload.js | 116 ++--- 5 files changed, 324 insertions(+), 274 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index d84357e75..758755007 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,6 +37,7 @@ module.exports = { files: [ // TODO: add packages as they are migrated to ESM. './packages/ckeditor5-dev-stale-bot/**/*', + './packages/ckeditor5-dev-transifex/**/*', './packages/ckeditor5-dev-ci/**/*', './packages/ckeditor5-dev-docs/**/*' ], diff --git a/packages/ckeditor5-dev-transifex/lib/download.js b/packages/ckeditor5-dev-transifex/lib/download.js index ac893e7b6..7dae5d340 100644 --- a/packages/ckeditor5-dev-transifex/lib/download.js +++ b/packages/ckeditor5-dev-transifex/lib/download.js @@ -12,7 +12,7 @@ import transifexService from './transifexservice.js'; import { verifyProperties, createLogger } from './utils.js'; import languageCodeMap from './languagecodemap.json'; -const logger = createLogger(); +let logger; /** * Downloads translations from the Transifex for each localizable package. It creates `*.po` files out of the translations and replaces old @@ -32,6 +32,8 @@ const logger = createLogger(); * @param {Boolean} [config.simplifyLicenseHeader=false] Whether to skip adding the contribute guide URL in the output `*.po` files. */ export default async function downloadTranslations( config ) { + logger = createLogger(); + verifyProperties( config, [ 'organizationName', 'projectName', 'token', 'packages', 'cwd' ] ); transifexService.init( config.token ); diff --git a/packages/ckeditor5-dev-transifex/lib/upload.js b/packages/ckeditor5-dev-transifex/lib/upload.js index 916359d02..2f0f7dd20 100644 --- a/packages/ckeditor5-dev-transifex/lib/upload.js +++ b/packages/ckeditor5-dev-transifex/lib/upload.js @@ -13,7 +13,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; const RESOURCE_REGEXP = /r:(?[a-z0-9_-]+)$/i; -const TRANSIFEX_RESOURCE_ERRORS = {}; +let TRANSIFEX_RESOURCE_ERRORS; /** * Uploads translations to the Transifex. @@ -37,6 +37,8 @@ const TRANSIFEX_RESOURCE_ERRORS = {}; * @returns {Promise} */ export default async function upload( config ) { + TRANSIFEX_RESOURCE_ERRORS = {}; + verifyProperties( config, [ 'token', 'organizationName', 'projectName', 'cwd', 'packages' ] ); const logger = createLogger(); @@ -51,7 +53,9 @@ export default async function upload( config ) { logger.warning( 'Found the file containing a list of packages that failed during the last script execution.' ); logger.warning( 'The script will process only packages listed in the file instead of all passed as "config.packages".' ); - failedPackages = Object.keys( await import( pathToFailedUploads ) ); + failedPackages = Object.keys( + ( await import( pathToFailedUploads ) ).default + ); } logger.progress( 'Fetching project information...' ); diff --git a/packages/ckeditor5-dev-transifex/tests/download.js b/packages/ckeditor5-dev-transifex/tests/download.js index 43ac8ebc7..6adc7f290 100644 --- a/packages/ckeditor5-dev-transifex/tests/download.js +++ b/packages/ckeditor5-dev-transifex/tests/download.js @@ -4,138 +4,189 @@ */ import path from 'path'; -import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; +import { describe, expect, it, vi, beforeEach } from 'vitest'; +import download from '../lib/download.js'; + +const { + fsOutputFileSyncMock, + fsRemoveSyncMock, + fsExistsSyncMock, + fsReadJsonSyncMock, + fsWriteJsonSyncMock, + chalkGrayMock, + chalkUnderlineMock, + toolsCreateSpinnerMock, + cleanPoFileContentMock, + createDictionaryFromPoFileContentMock, + transifexInitMock, + transifexGetResourceNameMock, + transifexGetLanguageCodeMock, + transifexGetProjectDataMock, + transifexGetTranslationsMock, + utilsVerifyPropertiesMock, + utilsCreateLoggerMock +} = vi.hoisted( () => { + return { + fsOutputFileSyncMock: vi.fn(), + fsRemoveSyncMock: vi.fn(), + fsExistsSyncMock: vi.fn(), + fsReadJsonSyncMock: vi.fn(), + fsWriteJsonSyncMock: vi.fn(), + chalkGrayMock: vi.fn(), + chalkUnderlineMock: vi.fn(), + toolsCreateSpinnerMock: vi.fn(), + cleanPoFileContentMock: vi.fn(), + createDictionaryFromPoFileContentMock: vi.fn(), + transifexInitMock: vi.fn(), + transifexGetResourceNameMock: vi.fn(), + transifexGetLanguageCodeMock: vi.fn(), + transifexGetProjectDataMock: vi.fn(), + transifexGetTranslationsMock: vi.fn(), + utilsVerifyPropertiesMock: vi.fn(), + utilsCreateLoggerMock: vi.fn() + }; +} ); -describe.skip( 'dev-transifex/download()', () => { - let stubs, mocks, download; +vi.mock( 'fs-extra', () => { + return { + default: { + outputFileSync: fsOutputFileSyncMock, + removeSync: fsRemoveSyncMock, + existsSync: fsExistsSyncMock, + readJsonSync: fsReadJsonSyncMock, + writeJsonSync: fsWriteJsonSyncMock + } + }; +} ); - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); +vi.mock( 'chalk', () => { + return { + default: { + gray: chalkGrayMock, + underline: chalkUnderlineMock + } + }; +} ); - stubs = { - logger: { - progress: sinon.stub(), - info: sinon.stub(), - warning: sinon.stub(), - error: sinon.stub(), - _log: sinon.stub() - }, +vi.mock( '@ckeditor/ckeditor5-dev-utils', () => { + return { + tools: { + createSpinner: toolsCreateSpinnerMock + } + }; +} ); - fs: { - outputFileSync: sinon.stub(), +vi.mock( '@ckeditor/ckeditor5-dev-translations', () => { + return { + cleanPoFileContent: cleanPoFileContentMock, + createDictionaryFromPoFileContent: createDictionaryFromPoFileContentMock + }; +} ); - removeSync: sinon.stub(), +vi.mock( '../lib/transifexservice.js', () => { + return { + default: { + init: transifexInitMock, + getResourceName: transifexGetResourceNameMock, + getLanguageCode: transifexGetLanguageCodeMock, + getProjectData: transifexGetProjectDataMock, + getTranslations: transifexGetTranslationsMock + } + }; +} ); - existsSync: sinon.stub() - .withArgs( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ) - .callsFake( () => Boolean( mocks.oldFailedDownloads ) ), +vi.mock( '../lib/utils.js', () => { + return { + verifyProperties: utilsVerifyPropertiesMock, + createLogger: utilsCreateLoggerMock + }; +} ); - readJsonSync: sinon.stub() - .withArgs( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ) - .callsFake( () => mocks.oldFailedDownloads ), +vi.mock( '../lib/languagecodemap.json', () => { + return { + default: { + ne_NP: 'ne' + } + }; +} ); - writeJsonSync: sinon.stub() - }, +describe( 'dev-transifex/download()', () => { + let mocks; + let loggerProgressMock, loggerInfoMock, loggerWarningMock, loggerErrorMock, loggerLogMock; + let spinnerStartMock, spinnerFinishMock; - translationUtils: { - createDictionaryFromPoFileContent: sinon.stub().callsFake( fileContent => mocks.fileContents[ fileContent ] ), - cleanPoFileContent: sinon.stub().callsFake( fileContent => fileContent ) - }, + beforeEach( () => { + loggerProgressMock = vi.fn(); + loggerInfoMock = vi.fn(); + loggerWarningMock = vi.fn(); + loggerErrorMock = vi.fn(); + loggerErrorMock = vi.fn(); + + vi.mocked( utilsCreateLoggerMock ).mockImplementation( () => { + return { + progress: loggerProgressMock, + info: loggerInfoMock, + warning: loggerWarningMock, + error: loggerErrorMock, + _log: loggerLogMock + }; + } ); - chalk: { - underline: sinon.stub().callsFake( msg => msg ), - gray: sinon.stub().callsFake( msg => msg ) - }, + spinnerStartMock = vi.fn(); + spinnerFinishMock = vi.fn(); - tools: { - createSpinner: sinon.stub(), - spinnerStart: sinon.stub(), - spinnerFinish: sinon.stub() - }, + vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + start: spinnerStartMock, + finish: spinnerFinishMock + } ); - transifexService: { - init: sinon.stub(), - - getResourceName: sinon.stub().callsFake( resource => resource.attributes.slug ), - - getLanguageCode: sinon.stub().callsFake( language => language.attributes.code ), - - getProjectData: sinon.stub().callsFake( ( organizationName, projectName, localizablePackageNames ) => { - const projectData = { - resources: mocks.resources.filter( resource => localizablePackageNames.includes( resource.attributes.slug ) ), - languages: mocks.languages - }; - - return Promise.resolve( projectData ); - } ), - - getTranslations: sinon.stub().callsFake( ( resource, languages ) => { - const translationData = { - translations: new Map( - languages.map( language => [ - language.attributes.code, - mocks.translations[ resource.attributes.slug ][ language.attributes.code ] - ] ) - ), - failedDownloads: mocks.newFailedDownloads ? - mocks.newFailedDownloads.filter( item => { - const isResourceNameMatched = item.resourceName === resource.attributes.slug; - const isLanguageCodeMatched = languages.find( language => item.languageCode === language.attributes.code ); - - return isResourceNameMatched && isLanguageCodeMatched; - } ) : - [] - }; - - return Promise.resolve( translationData ); - } ) - }, + // existsSync: sinon.stub() + // .withArgs( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ) + // .callsFake( () => Boolean( mocks.oldFailedDownloads ) ), - utils: { - verifyProperties: sinon.stub(), - createLogger: sinon.stub() - } - }; + // readJsonSync: sinon.stub() + // .withArgs( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ) + // .callsFake( () => mocks.oldFailedDownloads ), - stubs.tools.createSpinner.returns( { - start: stubs.tools.spinnerStart, - finish: stubs.tools.spinnerFinish - } ); + vi.mocked( fsExistsSyncMock ).mockImplementation( () => Boolean( mocks.oldFailedDownloads ) ); + vi.mocked( fsReadJsonSyncMock ).mockImplementation( () => mocks.oldFailedDownloads ); - stubs.utils.createLogger.returns( { - progress: stubs.logger.progress, - info: stubs.logger.info, - warning: stubs.logger.warning, - error: stubs.logger.error, - _log: sinon.stub() - } ); + vi.mocked( createDictionaryFromPoFileContentMock ).mockImplementation( fileContent => mocks.fileContents[ fileContent ] ); + vi.mocked( cleanPoFileContentMock ).mockImplementation( fileContent => fileContent ); - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - tools: stubs.tools - } ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-translations', { - cleanPoFileContent: stubs.translationUtils.cleanPoFileContent, - createDictionaryFromPoFileContent: stubs.translationUtils.createDictionaryFromPoFileContent - } ); + vi.mocked( chalkUnderlineMock ).mockImplementation( string => string ); + vi.mocked( chalkGrayMock ).mockImplementation( string => string ); - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( 'chalk', stubs.chalk ); - mockery.registerMock( './transifexservice', stubs.transifexService ); - mockery.registerMock( './utils', stubs.utils ); - mockery.registerMock( './languagecodemap.json', { ne_NP: 'ne' } ); + vi.mocked( transifexGetResourceNameMock ).mockImplementation( resource => resource.attributes.slug ); + vi.mocked( transifexGetLanguageCodeMock ).mockImplementation( language => language.attributes.code ); + vi.mocked( transifexGetProjectDataMock ).mockImplementation( ( organizationName, projectName, localizablePackageNames ) => { + const projectData = { + resources: mocks.resources.filter( resource => localizablePackageNames.includes( resource.attributes.slug ) ), + languages: mocks.languages + }; - download = require( '../lib/download' ); - } ); + return Promise.resolve( projectData ); + } ); + vi.mocked( transifexGetTranslationsMock ).mockImplementation( ( resource, languages ) => { + const translationData = { + translations: new Map( + languages.map( language => [ + language.attributes.code, + mocks.translations[ resource.attributes.slug ][ language.attributes.code ] + ] ) + ), + failedDownloads: mocks.newFailedDownloads ? + mocks.newFailedDownloads.filter( item => { + const isResourceNameMatched = item.resourceName === resource.attributes.slug; + const isLanguageCodeMatched = languages.find( language => item.languageCode === language.attributes.code ); + + return isResourceNameMatched && isLanguageCodeMatched; + } ) : + [] + }; - afterEach( () => { - sinon.restore(); - mockery.deregisterAll(); - mockery.disable(); + return Promise.resolve( translationData ); + } ); } ); it( 'should fail if properties verification failed', () => { @@ -147,25 +198,22 @@ describe.skip( 'dev-transifex/download()', () => { token: 'secretToken' }; - stubs.utils.verifyProperties.throws( error ); + vi.mocked( utilsVerifyPropertiesMock ).mockImplementation( () => { + throw new Error( error ); + } ); return download( config ) .then( () => { throw new Error( 'Expected to be rejected.' ); }, - err => { - expect( err ).to.equal( error ); - - expect( stubs.utils.verifyProperties.callCount ).to.equal( 1 ); - expect( stubs.utils.verifyProperties.firstCall.args[ 0 ] ).to.deep.equal( config ); - expect( stubs.utils.verifyProperties.firstCall.args[ 1 ] ).to.deep.equal( [ - 'organizationName', - 'projectName', - 'token', - 'packages', - 'cwd' - ] ); + caughtError => { + expect( caughtError.message.endsWith( error.message ) ).toEqual( true ); + + expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledWith( + config, [ 'organizationName', 'projectName', 'token', 'packages', 'cwd' ] + ); } ); } ); @@ -198,21 +246,21 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.calledTwice( stubs.fs.removeSync ); - sinon.assert.calledOnce( stubs.fs.outputFileSync ); + expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( - stubs.fs.removeSync.firstCall, + expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenNthCalledWith( + 1, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations' ) ); - - sinon.assert.calledWithExactly( - stubs.fs.removeSync.secondCall, + expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenNthCalledWith( + 2, path.normalize( '/workspace/.transifex-failed-downloads.json' ) ); - expect( stubs.fs.removeSync.firstCall.calledBefore( stubs.fs.outputFileSync.firstCall ) ).to.be.true; - expect( stubs.fs.removeSync.secondCall.calledAfter( stubs.fs.outputFileSync.firstCall ) ).to.be.true; + // TODO: https://github.com/vitest-dev/vitest/issues/2287 - do we want to add jest-extended just to test mock execution order? + // expect( stubs.fs.removeSync.firstCall.calledBefore( stubs.fs.outputFileSync.firstCall ) ).to.be.true; + // expect( stubs.fs.removeSync.secondCall.calledAfter( stubs.fs.outputFileSync.firstCall ) ).to.be.true; } ); it( 'should download translations for non-empty resources', async () => { @@ -254,34 +302,44 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.callCount( stubs.fs.outputFileSync, 3 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync.firstCall, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/pl.po' ), 'ckeditor5-core-pl-content' ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync.secondCall, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/de.po' ), 'ckeditor5-core-de-content' ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync.thirdCall, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 3, path.normalize( '/workspace/bar/ckeditor5-ui/lang/translations/pl.po' ), 'ckeditor5-ui-pl-content' ); - sinon.assert.callCount( stubs.logger.progress, 3 ); - sinon.assert.calledWithExactly( stubs.logger.progress.firstCall, 'Fetching project information...' ); - sinon.assert.calledWithExactly( stubs.logger.progress.secondCall, 'Downloading all translations...' ); - sinon.assert.calledWithExactly( stubs.logger.progress.thirdCall, 'Saved all translations.' ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( + 1, 'Fetching project information...' + ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( + 2, 'Downloading all translations...' + ); + expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( + 3, 'Saved all translations.' + ); - sinon.assert.callCount( stubs.logger.info, 2 ); - sinon.assert.calledWithExactly( stubs.logger.info.firstCall, ' Saved 2 "*.po" file(s).' ); - sinon.assert.calledWithExactly( stubs.logger.info.secondCall, ' Saved 1 "*.po" file(s).' ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenNthCalledWith( + 1, ' Saved 2 "*.po" file(s).' + ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenNthCalledWith( + 2, ' Saved 1 "*.po" file(s).' + ); } ); it( 'should download translations for non-empty resources only for specified packages', async () => { @@ -322,10 +380,9 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.callCount( stubs.fs.outputFileSync, 1 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( path.normalize( '/workspace/bar/ckeditor5-ui/lang/translations/pl.po' ), 'ckeditor5-ui-pl-content' ); @@ -370,18 +427,18 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.callCount( stubs.tools.createSpinner, 2 ); - sinon.assert.callCount( stubs.tools.spinnerStart, 2 ); - sinon.assert.callCount( stubs.tools.spinnerFinish, 2 ); + expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( spinnerStartMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( spinnerFinishMock ) ).toHaveBeenCalledTimes( 2 ); - sinon.assert.calledWithExactly( - stubs.tools.createSpinner.firstCall, + expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + 1, 'Processing "ckeditor5-core"...', { indentLevel: 1, emoji: '👉' } ); - sinon.assert.calledWithExactly( - stubs.tools.createSpinner.secondCall, + expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + 2, 'Processing "ckeditor5-ui"...', { indentLevel: 1, emoji: '👉' } ); @@ -405,7 +462,7 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.notCalled( stubs.fs.outputFileSync ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should save failed downloads', async () => { @@ -450,37 +507,36 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.calledOnce( stubs.fs.writeJsonSync ); + expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( - stubs.fs.writeJsonSync, + expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledWith( path.normalize( '/workspace/.transifex-failed-downloads.json' ), [ { resourceName: 'ckeditor5-ui', languages: [ { code: 'de', errorMessage: 'An example error.' } ] } ], { spaces: 2 } ); - sinon.assert.callCount( stubs.logger.info, 2 ); - sinon.assert.calledWithExactly( stubs.logger.info.firstCall, ' Saved 2 "*.po" file(s).' ); - sinon.assert.calledWithExactly( stubs.logger.info.secondCall, ' Saved 2 "*.po" file(s). 1 requests failed.' ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenNthCalledWith( 1, ' Saved 2 "*.po" file(s).' ); + expect( vi.mocked( loggerInfoMock ) ).toHaveBeenNthCalledWith( 2, ' Saved 2 "*.po" file(s). 1 requests failed.' ); - sinon.assert.callCount( stubs.logger.warning, 3 ); - sinon.assert.calledWithExactly( - stubs.logger.warning.firstCall, + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 1, 'Not all translations were downloaded due to errors in Transifex API.' ); - sinon.assert.calledWithExactly( - stubs.logger.warning.secondCall, + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 2, `Review the "${ path.normalize( '/workspace/.transifex-failed-downloads.json' ) }" file for more details.` ); - sinon.assert.calledWithExactly( - stubs.logger.warning.thirdCall, + expect( vi.mocked( loggerWarningMock ) ).toHaveBeenNthCalledWith( + 3, 'Re-running the script will process only packages specified in the file.' ); - sinon.assert.callCount( stubs.tools.spinnerFinish, 2 ); + expect( vi.mocked( spinnerFinishMock ) ).toHaveBeenCalledTimes( 2 ); // First call: OK. Second call: error. - sinon.assert.calledWithExactly( stubs.tools.spinnerFinish ); - sinon.assert.calledWithExactly( stubs.tools.spinnerFinish, { emoji: '❌' } ); + expect( vi.mocked( spinnerFinishMock ) ).toHaveBeenNthCalledWith( 1 ); + expect( vi.mocked( spinnerFinishMock ) ).toHaveBeenNthCalledWith( 2, { emoji: '❌' } ); } ); it( 'should use the language code from the "languagecodemap.json" if it exists, or the default language code otherwise', async () => { @@ -517,22 +573,22 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.callCount( stubs.fs.outputFileSync, 3 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync.firstCall, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/pl.po' ), 'ckeditor5-core-pl-content' ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync.secondCall, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/en_AU.po' ), 'ckeditor5-core-en_AU-content' ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync.thirdCall, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 3, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/ne.po' ), 'ckeditor5-core-ne_NP-content' ); @@ -541,7 +597,7 @@ describe.skip( 'dev-transifex/download()', () => { it( 'should fail with an error when the transifex service responses with an error', async () => { const error = new Error( 'An example error.' ); - stubs.transifexService.getProjectData.rejects( error ); + vi.mocked( transifexGetProjectDataMock ).mockRejectedValue( error ); try { await download( { @@ -558,7 +614,7 @@ describe.skip( 'dev-transifex/download()', () => { expect( err ).to.equal( error ); } - expect( stubs.transifexService.getProjectData.called ).to.equal( true ); + expect( vi.mocked( transifexGetProjectDataMock ) ).toHaveBeenCalled(); } ); it( 'should pass the "simplifyLicenseHeader" flag to the "cleanPoFileContent()" function when set to `true`', async () => { @@ -591,10 +647,9 @@ describe.skip( 'dev-transifex/download()', () => { simplifyLicenseHeader: true } ); - sinon.assert.calledOnce( stubs.translationUtils.cleanPoFileContent ); + expect( vi.mocked( cleanPoFileContentMock ) ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( - stubs.translationUtils.cleanPoFileContent, + expect( vi.mocked( cleanPoFileContentMock ) ).toHaveBeenCalledWith( 'ckeditor5-core-pl-content', { simplifyLicenseHeader: true @@ -634,15 +689,15 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.calledOnce( stubs.fs.outputFileSync ); - sinon.assert.calledOnce( stubs.fs.removeSync ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( - stubs.fs.removeSync, + expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenCalledWith( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ); - expect( stubs.fs.removeSync.calledAfter( stubs.fs.outputFileSync ) ).to.be.true; + // TODO + // expect( stubs.fs.removeSync.calledAfter( stubs.fs.outputFileSync ) ).to.be.true; } ); it( 'should download translations for existing resources but only for previously failed ones', async () => { @@ -688,28 +743,27 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.callCount( stubs.fs.outputFileSync, 1 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/pl.po' ), 'ckeditor5-core-pl-content' ); - sinon.assert.callCount( stubs.logger.warning, 2 ); - sinon.assert.calledWithExactly( - stubs.logger.warning.firstCall, + expect( loggerWarningMock ).toHaveBeenCalledTimes( 2 ); + expect( loggerWarningMock ).toHaveBeenNthCalledWith( + 1, 'Found the file containing a list of packages that failed during the last script execution.' ); - sinon.assert.calledWithExactly( - stubs.logger.warning.secondCall, + expect( loggerWarningMock ).toHaveBeenNthCalledWith( + 2, 'The script will process only packages listed in the file instead of all passed as "config.packages".' ); - sinon.assert.callCount( stubs.logger.progress, 3 ); - sinon.assert.calledWithExactly( stubs.logger.progress.firstCall, 'Fetching project information...' ); - sinon.assert.calledWithExactly( stubs.logger.progress.secondCall, 'Downloading only translations that failed previously...' ); - sinon.assert.calledWithExactly( stubs.logger.progress.thirdCall, 'Saved all translations.' ); + expect( loggerProgressMock ).toHaveBeenCalledTimes( 3 ); + expect( loggerProgressMock ).toHaveBeenNthCalledWith( 1, 'Fetching project information...' ); + expect( loggerProgressMock ).toHaveBeenNthCalledWith( 2, 'Downloading only translations that failed previously...' ); + expect( loggerProgressMock ).toHaveBeenNthCalledWith( 3, 'Saved all translations.' ); } ); it( 'should update ".transifex-failed-downloads.json" file if there are still some failed downloads', async () => { @@ -758,10 +812,9 @@ describe.skip( 'dev-transifex/download()', () => { ] ) } ); - sinon.assert.calledOnce( stubs.fs.writeJsonSync ); + expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( - stubs.fs.writeJsonSync, + expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledWith( path.normalize( '/workspace/.transifex-failed-downloads.json' ), [ { resourceName: 'ckeditor5-core', languages: [ { code: 'de', errorMessage: 'An example error.' } ] } ], { spaces: 2 } diff --git a/packages/ckeditor5-dev-transifex/tests/upload.js b/packages/ckeditor5-dev-transifex/tests/upload.js index da898a1df..458c2d95f 100644 --- a/packages/ckeditor5-dev-transifex/tests/upload.js +++ b/packages/ckeditor5-dev-transifex/tests/upload.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi, beforeEach } from 'vitest'; +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; import upload from '../lib/upload.js'; const { @@ -150,9 +150,7 @@ describe( 'dev-transifex/upload()', () => { loggerErrorMock = vi.fn(); loggerErrorMock = vi.fn(); - vi.mocked( fsLstatMock ).mockImplementation( async () => { - return Promise.reject(); - } ); + vi.mocked( fsLstatMock ).mockRejectedValue(); vi.mocked( utilsCreateLoggerMock ).mockImplementation( () => { return { @@ -165,6 +163,10 @@ describe( 'dev-transifex/upload()', () => { } ); } ); + afterEach( () => { + vi.resetAllMocks(); + } ); + it( 'should reject a promise if required properties are not specified', () => { const error = new Error( 'The specified object misses the following properties: packages.' ); const config = { @@ -431,13 +433,7 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( fsLstatMock ).mockImplementation( async path => { - if ( path === '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ) { - return Promise.resolves(); - } - - return Promise.reject(); - } ); + vi.mocked( fsLstatMock ).mockResolvedValueOnce(); vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [] @@ -491,7 +487,7 @@ describe( 'dev-transifex/upload()', () => { } ); } ); - it.skip( 'should process packages specified in the ".transifex-failed-uploads.json" file', async () => { + it( 'should process packages specified in the ".transifex-failed-uploads.json" file', async () => { await upload( config ); expect( vi.mocked( loggerWarningMock ) ).toHaveBeenCalledTimes( 2 ); @@ -508,14 +504,14 @@ describe( 'dev-transifex/upload()', () => { expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledTimes( 2 ); } ); - it.skip( 'should remove the ".transifex-failed-uploads.json" file if finished with no errors', async () => { + it( 'should remove the ".transifex-failed-uploads.json" file if finished with no errors', async () => { await upload( config ); expect( vi.mocked( fsUnlinkMock ) ).toHaveBeenCalledTimes( 1 ); expect( vi.mocked( fsUnlinkMock ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ); } ); - it.skip( 'should store an error in the ".transifex-failed-uploads.json" file (cannot create a resource)', async () => { + it( 'should store an error in the ".transifex-failed-uploads.json" file (cannot create a resource)', async () => { const firstSpinner = { start: vi.fn(), finish: vi.fn() @@ -556,11 +552,12 @@ describe( 'dev-transifex/upload()', () => { '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', JSON.stringify( { 'ckeditor5-non-existing-01': [ 'Resource with this Slug and Project already exists.' ] - } ) + }, null, 2 ) + '\n', + 'utf-8' ); } ); - it.skip( 'should store an error in the ".transifex-failed-uploads.json" file (cannot upload a translation)', async () => { + it( 'should store an error in the ".transifex-failed-uploads.json" file (cannot upload a translation)', async () => { const firstSpinner = { start: vi.fn(), finish: vi.fn() @@ -577,13 +574,7 @@ describe( 'dev-transifex/upload()', () => { ] }; - vi.mocked( transifexServiceCreateSourceFileMock ).mockImplementation( options => { - if ( options.resourceName === 'ckeditor5-non-existing-01' ) { - return Promise.reject( error ); - } - - return Promise.resolve( 'uuid-11' ); - } ); + vi.mocked( transifexServiceCreateSourceFileMock ).mockRejectedValueOnce( error ); await upload( config ); @@ -598,53 +589,52 @@ describe( 'dev-transifex/upload()', () => { 5, 'Re-running the script will process only packages specified in the file.' ); - expect( firstSpinner.finish ).toHaveBeenCalledTimes( 1 ); - expect( firstSpinner.finish ).toHaveBeenCalledWith( { emoji: '❌' } ); - expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledTimes( 1 ); expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', JSON.stringify( { 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] - } ) + }, null, 2 ) + '\n', + 'utf-8' ); + + expect( firstSpinner.finish ).toHaveBeenCalledTimes( 1 ); + expect( firstSpinner.finish ).toHaveBeenCalledWith( { emoji: '❌' } ); } ); - it.skip( 'should store an error in the ".transifex-failed-uploads.json" file (cannot get a status of upload)', async () => { - // const error = { - // message: 'JsonApiError: 409', - // errors: [ - // { - // detail: 'Object not found. It may have been deleted or not been created yet.' - // } - // ] - // }; + it( 'should store an error in the ".transifex-failed-uploads.json" file (cannot get a status of upload)', async () => { + const error = { + message: 'JsonApiError: 409', + errors: [ + { + detail: 'Object not found. It may have been deleted or not been created yet.' + } + ] + }; - // TODO - // stubs.transifexService.getResourceUploadDetails.withArgs( 'uuid-01' ).rejects( error ); + vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockRejectedValueOnce( error ); await upload( config ); - // expect( stubs.logger.warning.callCount ).to.equal( 5 ); - // expect( stubs.logger.warning.getCall( 2 ).args[ 0 ] ).to.equal( - // 'Not all translations were uploaded due to errors in Transifex API.' - // ); - // expect( stubs.logger.warning.getCall( 3 ).args[ 0 ] ).to.equal( - // 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' - // ); - // expect( stubs.logger.warning.getCall( 4 ).args[ 0 ] ).to.equal( - // 'Re-running the script will process only packages specified in the file.' - // ); - - // expect( stubs.fs.writeFile.callCount ).to.equal( 1 ); - // expect( stubs.fs.writeFile.firstCall.args[ 0 ] ).to.equal( - // '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' - // ); - - // const storedErrors = JSON.parse( stubs.fs.writeFile.firstCall.args[ 1 ] ); - // expect( storedErrors ).to.deep.equal( { - // 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] - // } ); + expect( loggerWarningMock ).toHaveBeenCalledTimes( 5 ); + expect( loggerWarningMock ).toHaveBeenNthCalledWith( + 3, 'Not all translations were uploaded due to errors in Transifex API.' + ); + expect( loggerWarningMock ).toHaveBeenNthCalledWith( + 4, 'Review the "/home/ckeditor5-with-errors/.transifex-failed-uploads.json" file for more details.' + ); + expect( loggerWarningMock ).toHaveBeenNthCalledWith( + 5, 'Re-running the script will process only packages specified in the file.' + ); + + expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledWith( + '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', + JSON.stringify( { + 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] + }, null, 2 ) + '\n', + 'utf-8' + ); } ); } ); @@ -687,13 +677,13 @@ describe( 'dev-transifex/upload()', () => { vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); vi.mocked( transifexServiceGetResourceUploadDetailsMock ) - .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 3, 0, 0 ) ) - .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-02', 0, 0, 0 ) ) - .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-03', 1, 0, 0 ) ) .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) ) - .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-12', 0, 1, 1 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-14', 0, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-03', 1, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 3, 0, 0 ) ) .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-13', 2, 0, 0 ) ) - .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-14', 0, 0, 0 ) ); + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-02', 0, 0, 0 ) ) + .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-12', 0, 1, 1 ) ); vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { start: vi.fn(), From 142744f12b09bc61fca057a7e2895e46e2c21f4d Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Sun, 15 Sep 2024 17:15:44 +0200 Subject: [PATCH 090/172] Migrated more tests. --- .../lib/createpotfiles.js | 9 +- .../tests/createpotfiles.js | 881 +++++++++++++----- 2 files changed, 652 insertions(+), 238 deletions(-) diff --git a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js index eaabab1dc..15d7dc89a 100644 --- a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js @@ -6,12 +6,12 @@ import path from 'path'; import fs from 'fs-extra'; import del from 'del'; -import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import { logger as utilsLogger } from '@ckeditor/ckeditor5-dev-utils'; import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; import { verifyProperties } from './utils.js'; -const defaultLogger = logger(); -const langContextSuffix = path.join( 'lang', 'contexts.json' ); +let defaultLogger; +let langContextSuffix; const corePackageName = 'ckeditor5-core'; /** @@ -29,6 +29,9 @@ const corePackageName = 'ckeditor5-core'; * @param {Logger} [options.logger] A logger. */ export default function createPotFiles( options ) { + defaultLogger = utilsLogger(); + langContextSuffix = path.join( 'lang', 'contexts.json' ); + verifyProperties( options, [ 'sourceFiles', 'packagePaths', 'corePackagePath', 'translationsDirectory' ] ); const { diff --git a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js index e247ad8ca..8e6321695 100644 --- a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js @@ -3,48 +3,78 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; -import { posix } from 'path'; +import { describe, expect, it, vi, beforeEach } from 'vitest'; +import createPotFiles from '../lib/createpotfiles.js'; + +const { + pathJoinMock, + fsExistsSyncMock, + fsOutputFileSyncMock, + fsReadFileSyncMock, + delSyncMock, + findMessagesMock, + utilsVerifyPropertiesMock +} = vi.hoisted( () => { + return { + pathJoinMock: vi.fn(), + fsExistsSyncMock: vi.fn(), + fsOutputFileSyncMock: vi.fn(), + fsReadFileSyncMock: vi.fn(), + delSyncMock: vi.fn(), + findMessagesMock: vi.fn(), + utilsVerifyPropertiesMock: vi.fn() + }; +} ); + +vi.mock( 'path', () => { + return { + default: { + join: pathJoinMock + } + }; +} ); -describe.skip( 'dev-transifex/createPotFiles()', () => { - let stubs; - let createPotFiles; +vi.mock( 'fs-extra', () => { + return { + default: { + existsSync: fsExistsSyncMock, + outputFileSync: fsOutputFileSyncMock, + readFileSync: fsReadFileSyncMock + } + }; +} ); + +vi.mock( 'del', () => { + return { + default: { + sync: delSyncMock + } + }; +} ); + +vi.mock( '@ckeditor/ckeditor5-dev-translations', () => { + return { + findMessages: findMessagesMock + }; +} ); + +vi.mock( '../lib/utils.js', () => { + return { + verifyProperties: utilsVerifyPropertiesMock + }; +} ); + +describe( 'dev-transifex/createPotFiles()', () => { + let loggerMocks; beforeEach( () => { - stubs = { - logger: { - info: sinon.stub(), - warning: sinon.stub(), - error: sinon.stub() - }, - - translations: { - findMessages: sinon.stub() - }, - - del: { - sync: sinon.stub() - }, - - fs: { - readFileSync: sinon.stub(), - outputFileSync: sinon.stub(), - existsSync: sinon.stub() - } + loggerMocks = { + info: vi.fn(), + warning: vi.fn(), + error: vi.fn() }; - createPotFiles = proxyquire( '../lib/createpotfiles', { - 'del': stubs.del, - 'fs-extra': stubs.fs, - '@ckeditor/ckeditor5-dev-translations': { - findMessages: stubs.translations.findMessages - }, - 'path': posix - } ); - } ); - - afterEach( () => { - sinon.restore(); + vi.mocked( pathJoinMock ).mockImplementation( ( ...args ) => args.join( '/' ) ); } ); it( 'should not create any POT file if no package is passed', () => { @@ -53,207 +83,403 @@ describe.skip( 'dev-transifex/createPotFiles()', () => { packagePaths: [], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.notCalled( stubs.fs.outputFileSync ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should delete the build directory before creating POT files', () => { - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { foo_id: 'foo_context' } ); - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ { string: 'foo', id: 'foo_id' } ] ); - createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.del.sync ); - sinon.assert.calledWithExactly( stubs.del.sync, '/cwd/build/.transifex' ); - sinon.assert.callOrder( stubs.del.sync, stubs.fs.outputFileSync ); + expect( vi.mocked( delSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( delSyncMock ) ).toHaveBeenCalledWith( '/cwd/build/.transifex' ); } ); it( 'should create a POT file entry for one message with a corresponding context', () => { - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { foo_id: 'foo_context' } ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.fs.outputFileSync ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, - '/cwd/build/.transifex/ckeditor5-foo/en.pot', - `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); -msgctxt "foo_context" -msgid "foo_id" -msgstr "foo" -` + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + '/cwd/build/.transifex/ckeditor5-foo/en.pot', + [ + `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, + '', + 'msgctxt "foo_context"', + 'msgid "foo_id"', + 'msgstr "foo"', + '' + ].join( '\n' ) ); } ); it( 'should warn if the message context is missing', () => { - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.logger.error ); - sinon.assert.calledWithExactly( - stubs.logger.error, + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); + + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); + + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + + expect( loggerMocks.error ).toHaveBeenCalledTimes( 1 ); + expect( loggerMocks.error ).toHaveBeenCalledWith( 'Context for the message id is missing (\'foo_id\' from packages/ckeditor5-foo/src/foo.js).' ); - sinon.assert.notCalled( stubs.fs.outputFileSync ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); // Mark the process as failed in case of the error. - expect( process.exitCode ).to.equal( 1 ); + expect( process.exitCode ).toEqual( 1 ); } ); it( 'should create a POT file entry for every defined package', () => { - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { foo_id: 'foo_context' } ); - createFakeContextFile( 'packages/ckeditor5-bar/lang/contexts.json', { bar_id: 'bar_context' } ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'bar_id': 'bar_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-bar/src/bar.js_content' ); - createFakeSourceFileWithMessages( 'packages/ckeditor5-bar/src/bar.js', [ - { string: 'bar', id: 'bar_id' } - ] ); + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); + } ); + + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'bar', id: 'bar_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js', 'packages/ckeditor5-bar/src/bar.js' ], packagePaths: [ 'packages/ckeditor5-foo', 'packages/ckeditor5-bar' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledTwice( stubs.fs.outputFileSync ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-bar/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 3, 'packages/ckeditor5-core/lang/contexts.json' + ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, - '/cwd/build/.transifex/ckeditor5-foo/en.pot', - `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 4 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-bar/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 3, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 4, 'packages/ckeditor5-bar/src/bar.js', 'utf-8' + ); -msgctxt "foo_context" -msgid "foo_id" -msgstr "foo" -` ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + 1, + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + 2, + 'packages/ckeditor5-bar/src/bar.js_content', + 'packages/ckeditor5-bar/src/bar.js', + expect.any( Function ), + expect.any( Function ) + ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, + '/cwd/build/.transifex/ckeditor5-foo/en.pot', + [ + `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, + '', + 'msgctxt "foo_context"', + 'msgid "foo_id"', + 'msgstr "foo"', + '' + ].join( '\n' ) + ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, '/cwd/build/.transifex/ckeditor5-bar/en.pot', - `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. - -msgctxt "bar_context" -msgid "bar_id" -msgstr "bar" -` + [ + `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, + '', + 'msgctxt "bar_context"', + 'msgid "bar_id"', + 'msgstr "bar"', + '' + ].join( '\n' ) ); } ); it( 'should create one POT file entry from multiple files in the same package', () => { - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { - foo_id: 'foo_context', - bar_id: 'bar_context' + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'bar_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/bar.js_content' ); + + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); } ); - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'bar', id: 'bar_id' } + ]; - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/bar.js', [ - { string: 'bar', id: 'bar_id' } - ] ); + messages.forEach( message => onFoundMessage( message ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js', 'packages/ckeditor5-foo/src/bar.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.fs.outputFileSync ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, - '/cwd/build/.transifex/ckeditor5-foo/en.pot', - `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 3, 'packages/ckeditor5-foo/src/bar.js', 'utf-8' + ); -msgctxt "foo_context" -msgid "foo_id" -msgstr "foo" + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + 1, + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + 2, + 'packages/ckeditor5-foo/src/bar.js_content', + 'packages/ckeditor5-foo/src/bar.js', + expect.any( Function ), + expect.any( Function ) + ); -msgctxt "bar_context" -msgid "bar_id" -msgstr "bar" -` + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + '/cwd/build/.transifex/ckeditor5-foo/en.pot', + [ + `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, + '', + 'msgctxt "foo_context"', + 'msgid "foo_id"', + 'msgstr "foo"', + '', + 'msgctxt "bar_context"', + 'msgid "bar_id"', + 'msgstr "bar"', + '' + ].join( '\n' ) ); } ); it( 'should create a POT entry filled with plural forms for message that contains has defined plural forms', () => { - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { - foo_id: 'foo_context' - } ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id', plural: 'foo_plural' } - ] ); + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id', plural: 'foo_plural' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.fs.outputFileSync ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); + + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, - '/cwd/build/.transifex/ckeditor5-foo/en.pot', - `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); -msgctxt "foo_context" -msgid "foo_id" -msgid_plural "foo_plural" -msgstr[0] "foo" -msgstr[1] "foo_plural" -` + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + '/cwd/build/.transifex/ckeditor5-foo/en.pot', + [ + `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, + '', + 'msgctxt "foo_context"', + 'msgid "foo_id"', + 'msgid_plural "foo_plural"', + 'msgstr[0] "foo"', + 'msgstr[1] "foo_plural"', + '' + ].join( '\n' ) ); } ); it( 'should load the core context file once and use its contexts', () => { - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - createFakeContextFile( 'packages/ckeditor5-core/lang/contexts.json', { - foo_id: 'foo_context' + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); } ); createPotFiles( { @@ -261,32 +487,62 @@ msgstr[1] "foo_plural" packagePaths: [ 'packages/ckeditor5-foo', 'packages/ckeditor5-core' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.notCalled( stubs.logger.error ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); - sinon.assert.calledOnce( stubs.fs.outputFileSync ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, - '/cwd/build/.transifex/ckeditor5-core/en.pot', - `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved. + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + + expect( loggerMocks.error ).toHaveBeenCalledTimes( 0 ); -msgctxt "foo_context" -msgid "foo_id" -msgstr "foo" -` + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + '/cwd/build/.transifex/ckeditor5-core/en.pot', + [ + `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, + '', + 'msgctxt "foo_context"', + 'msgid "foo_id"', + 'msgstr "foo"', + '' + ].join( '\n' ) ); } ); it( 'should not create a POT file for the context file if that was not added to the list of packages', () => { - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; - createFakeContextFile( 'packages/ckeditor5-core/lang/contexts.json', { - foo_id: 'foo_context' + messages.forEach( message => onFoundMessage( message ) ); } ); createPotFiles( { @@ -294,71 +550,161 @@ msgstr "foo" packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.notCalled( stubs.logger.error ); - sinon.assert.notCalled( stubs.fs.outputFileSync ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); + + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); + + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + + expect( loggerMocks.error ).toHaveBeenCalledTimes( 0 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should log an error if the file contains a message that cannot be parsed', () => { - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [], [ 'parse_error' ] ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage, onErrorFound ) => { + const errors = [ + 'parse_error' + ]; + + errors.forEach( error => onErrorFound( error ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.logger.error ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); - sinon.assert.calledWithExactly( - stubs.logger.error, - 'parse_error' + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + + expect( loggerMocks.error ).toHaveBeenCalledTimes( 1 ); + expect( loggerMocks.error ).toHaveBeenCalledWith( 'parse_error' ); + // Mark the process as failed in case of the error. - expect( process.exitCode ).to.equal( 1 ); + expect( process.exitCode ).toEqual( 1 ); } ); it( 'should log an error if two context files contain contexts the same id', () => { - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context1' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context2' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { foo_id: 'foo_context1' } ); - createFakeContextFile( 'packages/ckeditor5-core/lang/contexts.json', { foo_id: 'foo_context2' } ); + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.logger.error ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); + + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 3, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); + + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); - sinon.assert.calledWithExactly( - stubs.logger.error, + expect( loggerMocks.error ).toHaveBeenCalledTimes( 1 ); + expect( loggerMocks.error ).toHaveBeenCalledWith( 'Context is duplicated for the id: \'foo_id\' in ' + 'packages/ckeditor5-core/lang/contexts.json and packages/ckeditor5-foo/lang/contexts.json.' ); // Mark the process as failed in case of the error. - expect( process.exitCode ).to.equal( 1 ); + expect( process.exitCode ).toEqual( 1 ); } ); it( 'should log an error if a context is unused', () => { - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { - foo_id: 'foo_context', - bar_id: 'foo_context' + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); } ); createPotFiles( { @@ -366,13 +712,35 @@ msgstr "foo" packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger + logger: loggerMocks } ); - sinon.assert.calledOnce( stubs.logger.error ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); - sinon.assert.calledWithExactly( - stubs.logger.error, + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); + + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + + expect( loggerMocks.error ).toHaveBeenCalledTimes( 1 ); + expect( loggerMocks.error ).toHaveBeenCalledWith( 'Unused context: \'bar_id\' in ckeditor5-foo/lang/contexts.json' ); @@ -381,28 +749,35 @@ msgstr "foo" } ); it( 'should fail with an error describing missing properties if the required were not passed to the function', () => { + vi.mocked( utilsVerifyPropertiesMock ).mockImplementationOnce( ( options, requiredProperties ) => { + throw new Error( `The specified object misses the following properties: ${ requiredProperties.join( ', ' ) }.` ); + } ); + try { createPotFiles( {} ); + + throw new Error( 'Expected to throw.' ); } catch ( err ) { - expect( err.message ).to.equal( + expect( err.message ).toEqual( 'The specified object misses the following properties: sourceFiles, packagePaths, corePackagePath, translationsDirectory.' ); } } ); it( 'should not log an error if a context from the core package is unused when ignoreUnusedCorePackageContexts=true', () => { - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { - foo_id: 'foo_context', - bar_id: 'foo_context' - } ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'custom_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - // This context is not used anywhere and the test would fail if the `ignoreUnusedCorePackageContexts` flag is set to `false`. - createFakeContextFile( 'packages/ckeditor5-core/lang/contexts.json', { - custom_id: 'foo_context' + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); } ); createPotFiles( { @@ -410,67 +785,103 @@ msgstr "foo" packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger, + logger: loggerMocks, ignoreUnusedCorePackageContexts: true } ); - sinon.assert.calledOnce( stubs.logger.error ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' + ); - sinon.assert.calledWithExactly( - stubs.logger.error, + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 3, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); + + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); + + expect( loggerMocks.error ).toHaveBeenCalledTimes( 1 ); + expect( loggerMocks.error ).toHaveBeenCalledWith( 'Unused context: \'bar_id\' in ckeditor5-foo/lang/contexts.json' ); // Mark the process as failed in case of the error. - expect( process.exitCode ).to.equal( 1 ); + expect( process.exitCode ).toEqual( 1 ); } ); it( 'should not add the license header in the created a POT file entry when skipLicenseHeader=true', () => { - createFakeContextFile( 'packages/ckeditor5-foo/lang/contexts.json', { foo_id: 'foo_context' } ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); - createFakeSourceFileWithMessages( 'packages/ckeditor5-foo/src/foo.js', [ - { string: 'foo', id: 'foo_id' } - ] ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + + vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + const messages = [ + { string: 'foo', id: 'foo_id' } + ]; + + messages.forEach( message => onFoundMessage( message ) ); + } ); createPotFiles( { sourceFiles: [ 'packages/ckeditor5-foo/src/foo.js' ], packagePaths: [ 'packages/ckeditor5-foo' ], corePackagePath: 'packages/ckeditor5-core', translationsDirectory: '/cwd/build/.transifex', - logger: stubs.logger, + logger: loggerMocks, skipLicenseHeader: true } ); - sinon.assert.calledOnce( stubs.fs.outputFileSync ); - - sinon.assert.calledWithExactly( - stubs.fs.outputFileSync, - '/cwd/build/.transifex/ckeditor5-foo/en.pot', - `msgctxt "foo_context" -msgid "foo_id" -msgstr "foo" -` + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json' + ); + expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-core/lang/contexts.json' ); - } ); - - function createFakeSourceFileWithMessages( file, messages, errors = [] ) { - const content = file + '_content'; - - stubs.fs.readFileSync - .withArgs( file ).returns( content ); - stubs.translations.findMessages - .withArgs( content ).callsFake( ( fileContent, filePath, onFoundMessage, onErrorFound ) => { - messages.forEach( message => onFoundMessage( message ) ); - errors.forEach( error => onErrorFound( error ) ); - } ); - } + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' + ); + expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' + ); - function createFakeContextFile( pathToContext, content ) { - stubs.fs.readFileSync - .withArgs( pathToContext ).returns( JSON.stringify( content ) ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + 'packages/ckeditor5-foo/src/foo.js_content', + 'packages/ckeditor5-foo/src/foo.js', + expect.any( Function ), + expect.any( Function ) + ); - stubs.fs.existsSync - .withArgs( pathToContext ).returns( true ); - } + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + '/cwd/build/.transifex/ckeditor5-foo/en.pot', + [ + 'msgctxt "foo_context"', + 'msgid "foo_id"', + 'msgstr "foo"', + '' + ].join( '\n' ) + ); + } ); } ); From 5dc5ce2fbbbbbbb76ee9882c95f30f91c23047ad Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Sun, 15 Sep 2024 20:07:10 +0200 Subject: [PATCH 091/172] Finished package migration. --- packages/ckeditor5-dev-transifex/lib/index.js | 21 +- .../lib/transifexservice.js | 2 +- .../ckeditor5-dev-transifex/tests/download.js | 7 - .../tests/transifexservice.js | 631 +++++++++--------- 4 files changed, 328 insertions(+), 333 deletions(-) diff --git a/packages/ckeditor5-dev-transifex/lib/index.js b/packages/ckeditor5-dev-transifex/lib/index.js index 2bf38063c..dd0e43ef9 100644 --- a/packages/ckeditor5-dev-transifex/lib/index.js +++ b/packages/ckeditor5-dev-transifex/lib/index.js @@ -3,18 +3,9 @@ * For licensing, see LICENSE.md. */ -import createPotFiles from './createpotfiles.js'; -import uploadPotFiles from './upload.js'; -import downloadTranslations from './download.js'; -import getToken from './gettoken.js'; -import transifexService from './transifexservice.js'; -import transifexUtils from './utils.js'; - -export default { - createPotFiles, - uploadPotFiles, - downloadTranslations, - getToken, - transifexService, - transifexUtils -}; +export { default as createPotFiles } from './createpotfiles.js'; +export { default as uploadPotFiles } from './upload.js'; +export { default as downloadTranslations } from './download.js'; +export { default as getToken } from './gettoken.js'; +export { default as transifexService } from './transifexservice.js'; +export { default as transifexUtils } from './utils.js'; diff --git a/packages/ckeditor5-dev-transifex/lib/transifexservice.js b/packages/ckeditor5-dev-transifex/lib/transifexservice.js index 1266839cf..d0da35248 100644 --- a/packages/ckeditor5-dev-transifex/lib/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/lib/transifexservice.js @@ -3,8 +3,8 @@ * For licensing, see LICENSE.md. */ -import { transifexApi } from '@transifex/api'; import fetch from 'node-fetch'; +import { transifexApi } from '@transifex/api'; const MAX_REQUEST_ATTEMPTS = 10; const REQUEST_RETRY_TIMEOUT = 3000; // In milliseconds. diff --git a/packages/ckeditor5-dev-transifex/tests/download.js b/packages/ckeditor5-dev-transifex/tests/download.js index 6adc7f290..7fc0356b2 100644 --- a/packages/ckeditor5-dev-transifex/tests/download.js +++ b/packages/ckeditor5-dev-transifex/tests/download.js @@ -257,10 +257,6 @@ describe( 'dev-transifex/download()', () => { 2, path.normalize( '/workspace/.transifex-failed-downloads.json' ) ); - - // TODO: https://github.com/vitest-dev/vitest/issues/2287 - do we want to add jest-extended just to test mock execution order? - // expect( stubs.fs.removeSync.firstCall.calledBefore( stubs.fs.outputFileSync.firstCall ) ).to.be.true; - // expect( stubs.fs.removeSync.secondCall.calledAfter( stubs.fs.outputFileSync.firstCall ) ).to.be.true; } ); it( 'should download translations for non-empty resources', async () => { @@ -695,9 +691,6 @@ describe( 'dev-transifex/download()', () => { expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenCalledWith( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ); - - // TODO - // expect( stubs.fs.removeSync.calledAfter( stubs.fs.outputFileSync ) ).to.be.true; } ); it( 'should download translations for existing resources but only for previously failed ones', async () => { diff --git a/packages/ckeditor5-dev-transifex/tests/transifexservice.js b/packages/ckeditor5-dev-transifex/tests/transifexservice.js index 85a906eea..30f3f4aff 100644 --- a/packages/ckeditor5-dev-transifex/tests/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/tests/transifexservice.js @@ -4,131 +4,132 @@ */ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; +import transifexService from '../lib/transifexservice.js'; + +const { + nodeFetchMock, + transifexApiMock +} = vi.hoisted( () => { + return { + nodeFetchMock: vi.fn(), + transifexApiMock: {} + }; +} ); -describe.skip( 'dev-transifex/transifex-service', () => { - let stubs, mocks, transifexService; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - getOrganizations: sinon.stub().callsFake( () => Promise.resolve( { - fetch: stubs.fetchOrganization - } ) ), +vi.mock( 'node-fetch', () => { + return { + default: nodeFetchMock + }; +} ); - fetchOrganization: sinon.stub().callsFake( () => Promise.resolve( { - get: stubs.getProjects - } ) ), +vi.mock( '@transifex/api', () => { + return { + transifexApi: transifexApiMock + }; +} ); - getProjects: sinon.stub().callsFake( () => Promise.resolve( { - fetch: stubs.fetchProject - } ) ), +describe( 'dev-transifex/transifex-service', () => { + let testData; + + let createResourceMock; + let createResourceStringsAsyncUploadMock; + let dataResourceTranslationsMock; + let fetchOrganizationMock; + let fetchProjectMock; + let fetchResourceTranslationsMock; + let filterResourceTranslationsMock; + let getNextResourceTranslationsMock; + let getOrganizationsMock; + let getProjectsMock; + let getResourceStringsAsyncUploadMock; + let includeResourceTranslationsMock; - fetchProject: sinon.stub().callsFake( resourceType => Promise.resolve( { - async* all() { - for ( const item of mocks[ resourceType ] ) { - yield item; - } + beforeEach( () => { + createResourceMock = vi.fn(); + createResourceStringsAsyncUploadMock = vi.fn(); + fetchResourceTranslationsMock = vi.fn(); + filterResourceTranslationsMock = vi.fn(); + getNextResourceTranslationsMock = vi.fn(); + getResourceStringsAsyncUploadMock = vi.fn(); + includeResourceTranslationsMock = vi.fn(); + + getOrganizationsMock = vi.fn().mockImplementation( () => Promise.resolve( { + fetch: fetchOrganizationMock + } ) ); + + fetchOrganizationMock = vi.fn().mockImplementation( () => Promise.resolve( { + get: getProjectsMock + } ) ); + + getProjectsMock = vi.fn().mockImplementation( () => Promise.resolve( { + fetch: fetchProjectMock + } ) ); + + fetchProjectMock = vi.fn().mockImplementation( resourceType => Promise.resolve( { + async* all() { + for ( const item of testData[ resourceType ] ) { + yield item; } - } ) ), + } + } ) ); - createResource: sinon.stub(), - createResourceStringsAsyncUpload: sinon.stub(), - getResourceStringsAsyncUpload: sinon.stub(), + transifexApiMock.setup = vi.fn().mockImplementation( ( { auth } ) => { + transifexApiMock.auth = vi.fn().mockReturnValue( { Authorization: `Bearer ${ auth }` } ); + } ); - filterResourceTranslations: sinon.stub(), - includeResourceTranslations: sinon.stub(), - fetchResourceTranslations: sinon.stub(), - getNextResourceTranslations: sinon.stub(), - dataResourceTranslations: [], + transifexApiMock.Organization = { + get: ( ...args ) => getOrganizationsMock( ...args ) + }; - transifexApi: { - setup: sinon.stub().callsFake( ( { auth } ) => { - stubs.transifexApi.auth = sinon.stub().returns( { Authorization: `Bearer ${ auth }` } ); - } ), + transifexApiMock.Resource = { + create: ( ...args ) => createResourceMock( ...args ) + }; - Organization: { - get: ( ...args ) => stubs.getOrganizations( ...args ) - }, + transifexApiMock.ResourceStringsAsyncUpload = { + create: ( ...args ) => createResourceStringsAsyncUploadMock( ...args ), + get: ( ...args ) => getResourceStringsAsyncUploadMock( ...args ) + }; - Resource: { - create: ( ...args ) => stubs.createResource( ...args ) - }, + transifexApiMock.ResourceStringsAsyncDownload = resourceAsyncDownloadMockFactory(); + transifexApiMock.ResourceTranslationsAsyncDownload = resourceAsyncDownloadMockFactory(); - ResourceStringsAsyncUpload: { - create: ( ...args ) => stubs.createResourceStringsAsyncUpload( ...args ), - get: ( ...args ) => stubs.getResourceStringsAsyncUpload( ...args ) - }, + transifexApiMock.ResourceTranslation = { + filter: ( ...args ) => { + filterResourceTranslationsMock( ...args ); - ...[ 'ResourceStringsAsyncDownload', 'ResourceTranslationsAsyncDownload' ].reduce( ( result, methodName ) => { - result[ methodName ] = { - create: sinon.stub().callsFake( ( { attributes, relationships, type } ) => { - const resourceName = relationships.resource.attributes.slug; - const languageCode = relationships.language ? relationships.language.attributes.code : 'en'; - - return Promise.resolve( { - attributes, - type, - links: { - self: `https://example.com/${ resourceName }/${ languageCode }` - }, - related: relationships - } ); - } ) - }; - - return result; - }, {} ), - - ResourceTranslation: { - filter: ( ...args ) => { - stubs.filterResourceTranslations( ...args ); + return { + include: ( ...args ) => { + includeResourceTranslationsMock( ...args ); return { - include: ( ...args ) => { - stubs.includeResourceTranslations( ...args ); - - return { - fetch: ( ...args ) => stubs.fetchResourceTranslations( ...args ), - get data() { - return stubs.dataResourceTranslations; - }, - get next() { - return !!stubs.getNextResourceTranslations; - }, - getNext: () => stubs.getNextResourceTranslations() - }; - } + fetch: ( ...args ) => fetchResourceTranslationsMock( ...args ), + get data() { + return dataResourceTranslationsMock; + }, + get next() { + return !!getNextResourceTranslationsMock; + }, + getNext: () => getNextResourceTranslationsMock() }; } - } - }, - - fetch: sinon.stub() + }; + } }; - - mockery.registerMock( '@transifex/api', { transifexApi: stubs.transifexApi } ); - mockery.registerMock( 'node-fetch', stubs.fetch ); - - transifexService = require( '../lib/transifexservice' ); } ); afterEach( () => { - sinon.restore(); - mockery.deregisterAll(); - mockery.disable(); + vi.useRealTimers(); + + // Restoring mock of Transifex API. + Object.keys( transifexApiMock ).forEach( mockedKey => delete transifexApiMock[ mockedKey ] ); } ); describe( 'init()', () => { it( 'should pass the token to the Transifex API', () => { transifexService.init( 'secretToken' ); - expect( stubs.transifexApi.auth ).to.be.a( 'function' ); - expect( stubs.transifexApi.auth() ).to.deep.equal( { Authorization: 'Bearer secretToken' } ); + expect( transifexApiMock.auth ).toBeInstanceOf( Function ); + expect( transifexApiMock.auth() ).toEqual( { Authorization: 'Bearer secretToken' } ); } ); it( 'should pass the token to the Transifex API only once', () => { @@ -136,16 +137,16 @@ describe.skip( 'dev-transifex/transifex-service', () => { transifexService.init( 'anotherSecretToken' ); transifexService.init( 'evenBetterSecretToken' ); - sinon.assert.calledOnce( stubs.transifexApi.setup ); + expect( transifexApiMock.setup ).toHaveBeenCalledTimes( 1 ); - expect( stubs.transifexApi.auth ).to.be.a( 'function' ); - expect( stubs.transifexApi.auth() ).to.deep.equal( { Authorization: 'Bearer secretToken' } ); + expect( transifexApiMock.auth ).toBeInstanceOf( Function ); + expect( transifexApiMock.auth() ).toEqual( { Authorization: 'Bearer secretToken' } ); } ); } ); describe( 'getProjectData()', () => { it( 'should return resources and languages, with English language as the source one', async () => { - mocks = { + testData = { resources: [ { attributes: { slug: 'ckeditor5-core' } }, { attributes: { slug: 'ckeditor5-ui' } } @@ -160,25 +161,25 @@ describe.skip( 'dev-transifex/transifex-service', () => { 'ckeditor-organization', 'ckeditor5-project', [ 'ckeditor5-core', 'ckeditor5-ui' ] ); - sinon.assert.calledOnce( stubs.getOrganizations ); - sinon.assert.calledWithExactly( stubs.getOrganizations, { slug: 'ckeditor-organization' } ); + expect( getOrganizationsMock ).toHaveBeenCalledTimes( 1 ); + expect( getOrganizationsMock ).toHaveBeenCalledWith( { slug: 'ckeditor-organization' } ); - sinon.assert.calledOnce( stubs.fetchOrganization ); - sinon.assert.calledWithExactly( stubs.fetchOrganization, 'projects' ); + expect( fetchOrganizationMock ).toHaveBeenCalledTimes( 1 ); + expect( fetchOrganizationMock ).toHaveBeenCalledWith( 'projects' ); - sinon.assert.calledOnce( stubs.getProjects ); - sinon.assert.calledWithExactly( stubs.getProjects, { slug: 'ckeditor5-project' } ); + expect( getProjectsMock ).toHaveBeenCalledTimes( 1 ); + expect( getProjectsMock ).toHaveBeenCalledWith( { slug: 'ckeditor5-project' } ); - sinon.assert.calledTwice( stubs.fetchProject ); - sinon.assert.calledWithExactly( stubs.fetchProject.firstCall, 'resources' ); - sinon.assert.calledWithExactly( stubs.fetchProject.secondCall, 'languages' ); + expect( fetchProjectMock ).toHaveBeenCalledTimes( 2 ); + expect( fetchProjectMock ).toHaveBeenNthCalledWith( 1, 'resources' ); + expect( fetchProjectMock ).toHaveBeenNthCalledWith( 2, 'languages' ); - expect( resources ).to.deep.equal( [ + expect( resources ).toEqual( [ { attributes: { slug: 'ckeditor5-core' } }, { attributes: { slug: 'ckeditor5-ui' } } ] ); - expect( languages ).to.deep.equal( [ + expect( languages ).toEqual( [ { attributes: { code: 'en' } }, { attributes: { code: 'pl' } }, { attributes: { code: 'de' } } @@ -186,7 +187,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { } ); it( 'should return only the available resources that were requested', async () => { - mocks = { + testData = { resources: [ { attributes: { slug: 'ckeditor5-core' } }, { attributes: { slug: 'ckeditor5-ui' } } @@ -201,11 +202,11 @@ describe.skip( 'dev-transifex/transifex-service', () => { 'ckeditor-organization', 'ckeditor5-project', [ 'ckeditor5-core', 'ckeditor5-non-existing' ] ); - expect( resources ).to.deep.equal( [ + expect( resources ).toEqual( [ { attributes: { slug: 'ckeditor5-core' } } ] ); - expect( languages ).to.deep.equal( [ + expect( languages ).toEqual( [ { attributes: { code: 'en' } }, { attributes: { code: 'pl' } }, { attributes: { code: 'de' } } @@ -215,7 +216,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { describe( 'getTranslations()', () => { beforeEach( () => { - mocks = { + testData = { resources: [ { attributes: { slug: 'ckeditor5-core' } }, { attributes: { slug: 'ckeditor5-ui' } } @@ -239,14 +240,14 @@ describe.skip( 'dev-transifex/transifex-service', () => { } ); it( 'should return requested translations if no retries are needed', async () => { - stubs.fetch.callsFake( url => Promise.resolve( { + vi.mocked( nodeFetchMock ).mockImplementation( url => Promise.resolve( { ok: true, redirected: true, - text: () => Promise.resolve( mocks.translations[ url ] ) + text: () => Promise.resolve( testData.translations[ url ] ) } ) ); - const resource = mocks.resources[ 0 ]; - const languages = [ ...mocks.languages ]; + const resource = testData.resources[ 0 ]; + const languages = [ ...testData.languages ]; const { translations, failedDownloads } = await transifexService.getTranslations( resource, languages ); const attributes = { @@ -256,9 +257,9 @@ describe.skip( 'dev-transifex/transifex-service', () => { pseudo: false }; - sinon.assert.calledOnce( stubs.transifexApi.ResourceStringsAsyncDownload.create ); + expect( transifexApiMock.ResourceStringsAsyncDownload.create ).toHaveBeenCalledTimes( 1 ); - sinon.assert.calledWithExactly( stubs.transifexApi.ResourceStringsAsyncDownload.create, { + expect( transifexApiMock.ResourceStringsAsyncDownload.create ).toHaveBeenCalledWith( { attributes, relationships: { resource @@ -266,9 +267,9 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_strings_async_downloads' } ); - sinon.assert.calledTwice( stubs.transifexApi.ResourceTranslationsAsyncDownload.create ); + expect( transifexApiMock.ResourceTranslationsAsyncDownload.create ).toHaveBeenCalledTimes( 2 ); - sinon.assert.calledWithExactly( stubs.transifexApi.ResourceTranslationsAsyncDownload.create.firstCall, { + expect( transifexApiMock.ResourceTranslationsAsyncDownload.create ).toHaveBeenNthCalledWith( 1, { attributes, relationships: { resource, @@ -277,7 +278,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_translations_async_downloads' } ); - sinon.assert.calledWithExactly( stubs.transifexApi.ResourceTranslationsAsyncDownload.create.secondCall, { + expect( transifexApiMock.ResourceTranslationsAsyncDownload.create ).toHaveBeenNthCalledWith( 2, { attributes, relationships: { resource, @@ -286,95 +287,91 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_translations_async_downloads' } ); - sinon.assert.calledThrice( stubs.fetch ); + expect( nodeFetchMock ).toHaveBeenCalledTimes( 3 ); - sinon.assert.calledWithExactly( stubs.fetch.firstCall, 'https://example.com/ckeditor5-core/en', { + expect( nodeFetchMock ).toHaveBeenNthCalledWith( 1, 'https://example.com/ckeditor5-core/en', { headers: { Authorization: 'Bearer secretToken' } } ); - sinon.assert.calledWithExactly( stubs.fetch.secondCall, 'https://example.com/ckeditor5-core/pl', { + expect( nodeFetchMock ).toHaveBeenNthCalledWith( 2, 'https://example.com/ckeditor5-core/pl', { headers: { Authorization: 'Bearer secretToken' } } ); - sinon.assert.calledWithExactly( stubs.fetch.thirdCall, 'https://example.com/ckeditor5-core/de', { + expect( nodeFetchMock ).toHaveBeenNthCalledWith( 3, 'https://example.com/ckeditor5-core/de', { headers: { Authorization: 'Bearer secretToken' } } ); - expect( [ ...translations.entries() ] ).to.deep.equal( [ + expect( [ ...translations.entries() ] ).toEqual( [ [ 'en', 'ckeditor5-core-en-content' ], [ 'pl', 'ckeditor5-core-pl-content' ], [ 'de', 'ckeditor5-core-de-content' ] ] ); - expect( failedDownloads ).to.deep.equal( [] ); + expect( failedDownloads ).toEqual( [] ); } ); it( 'should return requested translations after multiple different download retries', async () => { - const clock = sinon.useFakeTimers(); + vi.useFakeTimers(); - const redirectFetch = () => Promise.resolve( { - ok: true, - redirected: false - } ); + const languageCallsBeforeResolving = { + en: 9, + pl: 4, + de: 7 + }; - const resolveFetch = url => Promise.resolve( { - ok: true, - redirected: true, - text: () => Promise.resolve( mocks.translations[ url ] ) + nodeFetchMock.mockImplementation( url => { + const language = url.split( '/' ).pop(); + + if ( languageCallsBeforeResolving[ language ] > 0 ) { + languageCallsBeforeResolving[ language ]--; + + return Promise.resolve( { + ok: true, + redirected: false + } ); + } + + return Promise.resolve( { + ok: true, + redirected: true, + text: () => Promise.resolve( testData.translations[ url ] ) + } ); } ); - stubs.fetch - .withArgs( 'https://example.com/ckeditor5-core/en' ) - .callsFake( redirectFetch ) - .onCall( 9 ) - .callsFake( resolveFetch ); - - stubs.fetch - .withArgs( 'https://example.com/ckeditor5-core/pl' ) - .callsFake( redirectFetch ) - .onCall( 4 ) - .callsFake( resolveFetch ); - - stubs.fetch - .withArgs( 'https://example.com/ckeditor5-core/de' ) - .callsFake( redirectFetch ) - .onCall( 7 ) - .callsFake( resolveFetch ); - - const resource = mocks.resources[ 0 ]; - const languages = [ ...mocks.languages ]; + const resource = testData.resources[ 0 ]; + const languages = [ ...testData.languages ]; const translationsPromise = transifexService.getTranslations( resource, languages ); - await clock.tickAsync( 30000 ); + await vi.advanceTimersByTimeAsync( 30000 ); const { translations, failedDownloads } = await translationsPromise; - sinon.assert.callCount( stubs.fetch, 23 ); + expect( nodeFetchMock ).toHaveBeenCalledTimes( 23 ); - expect( [ ...translations.entries() ] ).to.deep.equal( [ + expect( [ ...translations.entries() ] ).toEqual( [ [ 'en', 'ckeditor5-core-en-content' ], [ 'pl', 'ckeditor5-core-pl-content' ], [ 'de', 'ckeditor5-core-de-content' ] ] ); - expect( failedDownloads ).to.deep.equal( [] ); + expect( failedDownloads ).toEqual( [] ); } ); it( 'should return failed requests if all file downloads failed', async () => { - stubs.fetch.resolves( { + vi.mocked( nodeFetchMock ).mockResolvedValue( { ok: false, status: 500, statusText: 'Internal Server Error' } ); - const resource = mocks.resources[ 0 ]; - const languages = [ ...mocks.languages ]; + const resource = testData.resources[ 0 ]; + const languages = [ ...testData.languages ]; const { translations, failedDownloads } = await transifexService.getTranslations( resource, languages ); const expectedFailedDownloads = [ 'en', 'pl', 'de' ].map( languageCode => ( { @@ -383,23 +380,23 @@ describe.skip( 'dev-transifex/transifex-service', () => { errorMessage: 'Failed to download the translation file. Received response: 500 Internal Server Error' } ) ); - expect( failedDownloads ).to.deep.equal( expectedFailedDownloads ); - expect( [ ...translations.entries() ] ).to.deep.equal( [] ); + expect( failedDownloads ).toEqual( expectedFailedDownloads ); + expect( [ ...translations.entries() ] ).toEqual( [] ); } ); it( 'should return failed requests if the retry limit has been reached for all requests', async () => { - const clock = sinon.useFakeTimers(); + vi.useFakeTimers(); - stubs.fetch.resolves( { + vi.mocked( nodeFetchMock ).mockResolvedValue( { ok: true, redirected: false } ); - const resource = mocks.resources[ 0 ]; - const languages = [ ...mocks.languages ]; + const resource = testData.resources[ 0 ]; + const languages = [ ...testData.languages ]; const translationsPromise = transifexService.getTranslations( resource, languages ); - await clock.tickAsync( 30000 ); + await vi.advanceTimersByTimeAsync( 30000 ); const { translations, failedDownloads } = await translationsPromise; @@ -410,21 +407,21 @@ describe.skip( 'dev-transifex/transifex-service', () => { 'Requested file is not ready yet, but the limit of file download attempts has been reached.' } ) ); - expect( failedDownloads ).to.deep.equal( expectedFailedDownloads ); - expect( [ ...translations.entries() ] ).to.deep.equal( [] ); + expect( failedDownloads ).toEqual( expectedFailedDownloads ); + expect( [ ...translations.entries() ] ).toEqual( [] ); } ); it( 'should return failed requests if it is not possible to create all initial download requests', async () => { - const clock = sinon.useFakeTimers(); + vi.useFakeTimers(); - stubs.transifexApi.ResourceStringsAsyncDownload.create.rejects(); - stubs.transifexApi.ResourceTranslationsAsyncDownload.create.rejects(); + transifexApiMock.ResourceStringsAsyncDownload.create.mockRejectedValue(); + transifexApiMock.ResourceTranslationsAsyncDownload.create.mockRejectedValue(); - const resource = mocks.resources[ 0 ]; - const languages = [ ...mocks.languages ]; + const resource = testData.resources[ 0 ]; + const languages = [ ...testData.languages ]; const translationsPromise = transifexService.getTranslations( resource, languages ); - await clock.tickAsync( 30000 ); + await vi.advanceTimersByTimeAsync( 30000 ); const { translations, failedDownloads } = await translationsPromise; @@ -434,67 +431,69 @@ describe.skip( 'dev-transifex/transifex-service', () => { errorMessage: 'Failed to create download request.' } ) ); - expect( failedDownloads ).to.deep.equal( expectedFailedDownloads ); - expect( [ ...translations.entries() ] ).to.deep.equal( [] ); + expect( failedDownloads ).toEqual( expectedFailedDownloads ); + expect( [ ...translations.entries() ] ).toEqual( [] ); } ); it( 'should return requested translations and failed downloads in multiple different download scenarios', async () => { - const clock = sinon.useFakeTimers(); + vi.useFakeTimers(); - const redirectFetch = () => Promise.resolve( { - ok: true, - redirected: false - } ); + transifexApiMock.ResourceStringsAsyncDownload.create.mockRejectedValue(); - const resolveFetch = url => Promise.resolve( { - ok: true, - redirected: true, - text: () => Promise.resolve( mocks.translations[ url ] ) - } ); + transifexApiMock.ResourceTranslationsAsyncDownload.create + .mockRejectedValueOnce() + .mockRejectedValueOnce() + .mockRejectedValueOnce(); - const rejectFetch = () => Promise.resolve( { - ok: false, - status: 500, - statusText: 'Internal Server Error' + const languageCallsBeforeResolving = { + pl: 4, + de: 8 + }; + + nodeFetchMock.mockImplementation( url => { + const language = url.split( '/' ).pop(); + + if ( languageCallsBeforeResolving[ language ] > 0 ) { + languageCallsBeforeResolving[ language ]--; + + return Promise.resolve( { + ok: true, + redirected: false + } ); + } + + if ( language === 'pl' ) { + return Promise.resolve( { + ok: true, + redirected: true, + text: () => Promise.resolve( testData.translations[ url ] ) + } ); + } + + if ( language === 'de' ) { + return Promise.resolve( { + ok: false, + status: 500, + statusText: 'Internal Server Error' + } ); + } } ); - stubs.transifexApi.ResourceStringsAsyncDownload.create.rejects(); - - stubs.transifexApi.ResourceTranslationsAsyncDownload.create - .onCall( 0 ) - .rejects() - .onCall( 1 ) - .rejects() - .onCall( 2 ) - .rejects(); - - stubs.fetch - .withArgs( 'https://example.com/ckeditor5-core/pl' ) - .callsFake( redirectFetch ) - .onCall( 4 ) - .callsFake( resolveFetch ); - - stubs.fetch - .withArgs( 'https://example.com/ckeditor5-core/de' ) - .callsFake( redirectFetch ) - .onCall( 8 ) - .callsFake( rejectFetch ); - - const resource = mocks.resources[ 0 ]; - const languages = [ ...mocks.languages ]; + const resource = testData.resources[ 0 ]; + const languages = [ ...testData.languages ]; const translationsPromise = transifexService.getTranslations( resource, languages ); - await clock.tickAsync( 60000 ); + await vi.advanceTimersByTimeAsync( 60000 ); const { translations, failedDownloads } = await translationsPromise; - sinon.assert.callCount( stubs.fetch, 14 ); + expect( nodeFetchMock ).toHaveBeenCalledTimes( 14 ); - expect( [ ...translations.entries() ] ).to.deep.equal( [ + expect( [ ...translations.entries() ] ).toEqual( [ [ 'pl', 'ckeditor5-core-pl-content' ] ] ); - expect( failedDownloads ).to.deep.equal( [ + expect( failedDownloads ).toEqual( [ { resourceName: 'ckeditor5-core', languageCode: 'en', @@ -511,9 +510,9 @@ describe.skip( 'dev-transifex/transifex-service', () => { describe( 'getResourceTranslations', () => { it( 'should return all found translations', () => { - stubs.getNextResourceTranslations = null; - stubs.fetchResourceTranslations.callsFake( () => { - stubs.dataResourceTranslations = [ + getNextResourceTranslationsMock = null; + fetchResourceTranslationsMock.mockImplementation( () => { + dataResourceTranslationsMock = [ { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:680k83DmCPu9AkGVwDvVQqCvsJkg93AC:l:en' }, { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:MbFEbBcsOk43LryccpBHPyeMYBW6G5FV:l:en' }, { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:tQ8xmNQ706zjL3hiqEsttqUoneZJtV4Q:l:en' } @@ -524,22 +523,22 @@ describe.skip( 'dev-transifex/transifex-service', () => { return transifexService.getResourceTranslations( 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo', 'l:en' ) .then( result => { - expect( result ).to.deep.equal( [ + expect( result ).toEqual( [ { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:680k83DmCPu9AkGVwDvVQqCvsJkg93AC:l:en' }, { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:MbFEbBcsOk43LryccpBHPyeMYBW6G5FV:l:en' }, { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:tQ8xmNQ706zjL3hiqEsttqUoneZJtV4Q:l:en' } ] ); - expect( stubs.filterResourceTranslations.callCount ).to.equal( 1 ); - expect( stubs.filterResourceTranslations.firstCall.args[ 0 ] ).to.deep.equal( { + expect( filterResourceTranslationsMock ).toHaveBeenCalledTimes( 1 ); + expect( filterResourceTranslationsMock ).toHaveBeenCalledWith( { resource: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo', language: 'l:en' } ); - expect( stubs.includeResourceTranslations.callCount ).to.equal( 1 ); - expect( stubs.includeResourceTranslations.firstCall.args[ 0 ] ).to.equal( 'resource_string' ); + expect( includeResourceTranslationsMock ).toHaveBeenCalledTimes( 1 ); + expect( includeResourceTranslationsMock ).toHaveBeenCalledWith( 'resource_string' ); - expect( stubs.fetchResourceTranslations.callCount ).to.equal( 1 ); + expect( fetchResourceTranslationsMock ).toHaveBeenCalledTimes( 1 ); } ); } ); @@ -550,47 +549,47 @@ describe.skip( 'dev-transifex/transifex-service', () => { { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:tQ8xmNQ706zjL3hiqEsttqUoneZJtV4Q:l:en' } ]; - stubs.getNextResourceTranslations.callsFake( () => { - stubs.dataResourceTranslations = [ availableTranslations.shift() ]; + getNextResourceTranslationsMock.mockImplementation( () => { + dataResourceTranslationsMock = [ availableTranslations.shift() ]; return Promise.resolve( { - data: stubs.dataResourceTranslations, + data: dataResourceTranslationsMock, next: availableTranslations.length > 0, - getNext: stubs.getNextResourceTranslations + getNext: getNextResourceTranslationsMock } ); } ); - stubs.fetchResourceTranslations.callsFake( () => { - stubs.dataResourceTranslations = [ availableTranslations.shift() ]; + fetchResourceTranslationsMock.mockImplementation( () => { + dataResourceTranslationsMock = [ availableTranslations.shift() ]; return Promise.resolve(); } ); return transifexService.getResourceTranslations( 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo', 'l:en' ) .then( result => { - expect( result ).to.deep.equal( [ + expect( result ).toEqual( [ { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:680k83DmCPu9AkGVwDvVQqCvsJkg93AC:l:en' }, { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:MbFEbBcsOk43LryccpBHPyeMYBW6G5FV:l:en' }, { id: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo:s:tQ8xmNQ706zjL3hiqEsttqUoneZJtV4Q:l:en' } ] ); - expect( stubs.filterResourceTranslations.callCount ).to.equal( 1 ); - expect( stubs.filterResourceTranslations.firstCall.args[ 0 ] ).to.deep.equal( { + expect( filterResourceTranslationsMock ).toHaveBeenCalledTimes( 1 ); + expect( filterResourceTranslationsMock ).toHaveBeenCalledWith( { resource: 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo', language: 'l:en' } ); - expect( stubs.includeResourceTranslations.callCount ).to.equal( 1 ); - expect( stubs.includeResourceTranslations.firstCall.args[ 0 ] ).to.equal( 'resource_string' ); + expect( includeResourceTranslationsMock ).toHaveBeenCalledTimes( 1 ); + expect( includeResourceTranslationsMock ).toHaveBeenCalledWith( 'resource_string' ); - expect( stubs.fetchResourceTranslations.callCount ).to.equal( 1 ); + expect( fetchResourceTranslationsMock ).toHaveBeenCalledTimes( 1 ); } ); } ); it( 'should reject a promise if Transifex API rejected', async () => { const apiError = new Error( 'JsonApiError: 418, I\'m a teapot' ); - stubs.fetchResourceTranslations.rejects( apiError ); + fetchResourceTranslationsMock.mockRejectedValue( apiError ); return transifexService.getResourceTranslations( 'o:ckeditor:p:ckeditor5:r:ckeditor5-foo', 'l:en' ) .then( @@ -598,7 +597,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { throw new Error( 'Expected to be rejected.' ); }, error => { - expect( apiError ).to.equal( error ); + expect( apiError ).toEqual( error ); } ); } ); @@ -608,7 +607,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { it( 'should extract the resource name from the resource instance', () => { const resource = { attributes: { slug: 'ckeditor5-core' } }; - expect( transifexService.getResourceName( resource ) ).to.equal( 'ckeditor5-core' ); + expect( transifexService.getResourceName( resource ) ).toEqual( 'ckeditor5-core' ); } ); } ); @@ -616,7 +615,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { it( 'should extract the language code from the language instance', () => { const language = { attributes: { code: 'pl' } }; - expect( transifexService.getLanguageCode( language ) ).to.equal( 'pl' ); + expect( transifexService.getLanguageCode( language ) ).toEqual( 'pl' ); } ); } ); @@ -624,13 +623,13 @@ describe.skip( 'dev-transifex/transifex-service', () => { it( 'should return false if the language instance is not the source language', () => { const language = { attributes: { code: 'pl' } }; - expect( transifexService.isSourceLanguage( language ) ).to.be.false; + expect( transifexService.isSourceLanguage( language ) ).toEqual( false ); } ); it( 'should return true if the language instance is the source language', () => { const language = { attributes: { code: 'en' } }; - expect( transifexService.isSourceLanguage( language ) ).to.be.true; + expect( transifexService.isSourceLanguage( language ) ).toEqual( true ); } ); } ); @@ -663,12 +662,12 @@ describe.skip( 'dev-transifex/transifex-service', () => { } }; - stubs.createResource.resolves( apiResponse ); + createResourceMock.mockResolvedValue( apiResponse ); return transifexService.createResource( { organizationName, projectName, resourceName } ) .then( response => { - expect( stubs.createResource.callCount ).to.equal( 1 ); - expect( stubs.createResource.firstCall.args[ 0 ] ).to.deep.equal( { + expect( createResourceMock ).toHaveBeenCalledTimes( 1 ); + expect( createResourceMock ).toHaveBeenCalledWith( { name: 'ckeditor5-foo', relationships: { i18n_format: { @@ -687,7 +686,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { slug: 'ckeditor5-foo' } ); - expect( response ).to.equal( apiResponse ); + expect( response ).toEqual( apiResponse ); } ); } ); @@ -698,7 +697,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { const apiError = new Error( 'JsonApiError: 418, I\'m a teapot' ); - stubs.createResource.rejects( apiError ); + createResourceMock.mockRejectedValue( apiError ); return transifexService.createResource( { organizationName, projectName, resourceName } ) .then( @@ -706,7 +705,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( apiError ).to.equal( err ); + expect( apiError ).toEqual( err ); } ); } ); @@ -724,12 +723,12 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_strings_async_uploads' }; - stubs.createResourceStringsAsyncUpload.resolves( apiResponse ); + createResourceStringsAsyncUploadMock.mockResolvedValue( apiResponse ); return transifexService.createSourceFile( { organizationName, projectName, resourceName, content } ) .then( response => { - expect( stubs.createResourceStringsAsyncUpload.callCount ).to.equal( 1 ); - expect( stubs.createResourceStringsAsyncUpload.firstCall.args[ 0 ] ).to.deep.equal( { + expect( createResourceStringsAsyncUploadMock ).toHaveBeenCalledTimes( 1 ); + expect( createResourceStringsAsyncUploadMock ).toHaveBeenCalledWith( { attributes: { content: '# ckeditor5-foo', content_encoding: 'text' @@ -745,7 +744,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_strings_async_uploads' } ); - expect( response ).to.equal( apiResponse.id ); + expect( response ).toEqual( apiResponse.id ); } ); } ); @@ -757,7 +756,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { const apiError = new Error( 'JsonApiError: 418, I\'m a teapot' ); - stubs.createResourceStringsAsyncUpload.rejects( apiError ); + createResourceStringsAsyncUploadMock.mockRejectedValue( apiError ); return transifexService.createSourceFile( { organizationName, projectName, resourceName, content } ) .then( @@ -765,21 +764,15 @@ describe.skip( 'dev-transifex/transifex-service', () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( apiError ).to.equal( err ); + expect( apiError ).toEqual( err ); } ); } ); } ); describe( 'getResourceUploadDetails', () => { - let clock; - beforeEach( () => { - clock = sinon.useFakeTimers(); - } ); - - afterEach( () => { - clock.restore(); + vi.useFakeTimers(); } ); it( 'should return a promise with resolved upload details (Transifex processed the upload)', async () => { @@ -791,17 +784,17 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_strings_async_uploads' }; - stubs.getResourceStringsAsyncUpload.resolves( apiResponse ); + getResourceStringsAsyncUploadMock.mockResolvedValue( apiResponse ); const promise = transifexService.getResourceUploadDetails( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); - expect( promise ).to.be.a( 'promise' ); - expect( stubs.getResourceStringsAsyncUpload.callCount ).to.equal( 1 ); - expect( stubs.getResourceStringsAsyncUpload.firstCall.args[ 0 ] ).to.equal( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); + expect( promise ).toBeInstanceOf( Promise ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenCalledTimes( 1 ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenCalledWith( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); const result = await promise; - expect( result ).to.equal( apiResponse ); + expect( result ).toEqual( apiResponse ); } ); it( 'should return a promise that resolves after 3000ms (Transifex processed the upload 1s, status=pending)', async () => { @@ -813,22 +806,22 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_strings_async_uploads' }; - stubs.getResourceStringsAsyncUpload.onFirstCall().resolves( { + getResourceStringsAsyncUploadMock.mockResolvedValueOnce( { attributes: { status: 'pending' } } ); - stubs.getResourceStringsAsyncUpload.onSecondCall().resolves( apiResponse ); + getResourceStringsAsyncUploadMock.mockResolvedValueOnce( apiResponse ); const promise = transifexService.getResourceUploadDetails( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); - expect( promise ).to.be.a( 'promise' ); - expect( stubs.getResourceStringsAsyncUpload.callCount ).to.equal( 1 ); - expect( stubs.getResourceStringsAsyncUpload.firstCall.args[ 0 ] ).to.equal( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); + expect( promise ).toBeInstanceOf( Promise ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenCalledTimes( 1 ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenNthCalledWith( 1, '4abfc726-6a27-4c33-9d99-e5254c8df748' ); - await clock.tickAsync( 3000 ); - expect( stubs.getResourceStringsAsyncUpload.callCount ).to.equal( 2 ); - expect( stubs.getResourceStringsAsyncUpload.secondCall.args[ 0 ] ).to.equal( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); + await vi.advanceTimersByTimeAsync( 3000 ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenCalledTimes( 2 ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenNthCalledWith( 2, '4abfc726-6a27-4c33-9d99-e5254c8df748' ); - expect( await promise ).to.equal( apiResponse ); + expect( await promise ).toEqual( apiResponse ); } ); it( 'should return a promise that resolves after 3000ms (Transifex processed the upload 1s, status=processing)', async () => { @@ -840,28 +833,28 @@ describe.skip( 'dev-transifex/transifex-service', () => { type: 'resource_strings_async_uploads' }; - stubs.getResourceStringsAsyncUpload.onFirstCall().resolves( { + getResourceStringsAsyncUploadMock.mockResolvedValueOnce( { attributes: { status: 'processing' } } ); - stubs.getResourceStringsAsyncUpload.onSecondCall().resolves( apiResponse ); + getResourceStringsAsyncUploadMock.mockResolvedValueOnce( apiResponse ); const promise = transifexService.getResourceUploadDetails( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); - expect( promise ).to.be.a( 'promise' ); - expect( stubs.getResourceStringsAsyncUpload.callCount ).to.equal( 1 ); - expect( stubs.getResourceStringsAsyncUpload.firstCall.args[ 0 ] ).to.equal( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); + expect( promise ).toBeInstanceOf( Promise ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenCalledTimes( 1 ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenNthCalledWith( 1, '4abfc726-6a27-4c33-9d99-e5254c8df748' ); - await clock.tickAsync( 3000 ); - expect( stubs.getResourceStringsAsyncUpload.callCount ).to.equal( 2 ); - expect( stubs.getResourceStringsAsyncUpload.secondCall.args[ 0 ] ).to.equal( '4abfc726-6a27-4c33-9d99-e5254c8df748' ); + await vi.advanceTimersByTimeAsync( 3000 ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenCalledTimes( 2 ); + expect( getResourceStringsAsyncUploadMock ).toHaveBeenNthCalledWith( 2, '4abfc726-6a27-4c33-9d99-e5254c8df748' ); - expect( await promise ).to.equal( apiResponse ); + expect( await promise ).toEqual( apiResponse ); } ); it( 'should return a promise that rejects if Transifex returned an error (no-delay)', async () => { const apiResponse = new Error( 'JsonApiError' ); - stubs.getResourceStringsAsyncUpload.rejects( apiResponse ); + getResourceStringsAsyncUploadMock.mockRejectedValue( apiResponse ); return transifexService.getResourceUploadDetails( '4abfc726-6a27-4c33-9d99-e5254c8df748' ) .then( @@ -869,7 +862,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( apiResponse ); + expect( err ).toEqual( apiResponse ); } ); } ); @@ -877,10 +870,10 @@ describe.skip( 'dev-transifex/transifex-service', () => { it( 'should return a promise that rejects if Transifex returned an error (delay)', async () => { const apiResponse = new Error( 'JsonApiError' ); - stubs.getResourceStringsAsyncUpload.onFirstCall().resolves( { + getResourceStringsAsyncUploadMock.mockResolvedValueOnce( { attributes: { status: 'processing' } } ); - stubs.getResourceStringsAsyncUpload.onSecondCall().rejects( apiResponse ); + getResourceStringsAsyncUploadMock.mockRejectedValueOnce( apiResponse ); const promise = transifexService.getResourceUploadDetails( '4abfc726-6a27-4c33-9d99-e5254c8df748' ) .then( @@ -888,13 +881,13 @@ describe.skip( 'dev-transifex/transifex-service', () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.equal( apiResponse ); + expect( err ).toEqual( apiResponse ); } ); - expect( promise ).to.be.a( 'promise' ); + expect( promise ).toBeInstanceOf( Promise ); - await clock.tickAsync( 3000 ); + await vi.advanceTimersByTimeAsync( 3000 ); return promise; } ); @@ -902,7 +895,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { it( 'should return a promise that rejects if reached the maximum number of requests to Transifex', async () => { // 10 is equal to the `MAX_REQUEST_ATTEMPTS` constant. for ( let i = 0; i < 10; ++i ) { - stubs.getResourceStringsAsyncUpload.onCall( i ).resolves( { + getResourceStringsAsyncUploadMock.mockResolvedValueOnce( { attributes: { status: 'processing' } } ); } @@ -913,7 +906,7 @@ describe.skip( 'dev-transifex/transifex-service', () => { throw new Error( 'Expected to be rejected.' ); }, err => { - expect( err ).to.deep.equal( { + expect( err ).toEqual( { errors: [ { detail: 'Failed to retrieve the upload details.' @@ -923,17 +916,35 @@ describe.skip( 'dev-transifex/transifex-service', () => { } ); - expect( promise ).to.be.a( 'promise' ); + expect( promise ).toBeInstanceOf( Promise ); for ( let i = 0; i < 9; ++i ) { expect( - stubs.getResourceStringsAsyncUpload.callCount, `getResourceStringsAsyncUpload, call: ${ i + 1 }` - ).to.equal( i + 1 ); + getResourceStringsAsyncUploadMock, `getResourceStringsAsyncUpload, call: ${ i + 1 }` + ).toHaveBeenCalledTimes( i + 1 ); - await clock.tickAsync( 3000 ); + await vi.advanceTimersByTimeAsync( 3000 ); } return promise; } ); } ); } ); + +function resourceAsyncDownloadMockFactory() { + return { + create: vi.fn().mockImplementation( ( { attributes, relationships, type } ) => { + const resourceName = relationships.resource.attributes.slug; + const languageCode = relationships.language ? relationships.language.attributes.code : 'en'; + + return Promise.resolve( { + attributes, + type, + links: { + self: `https://example.com/${ resourceName }/${ languageCode }` + }, + related: relationships + } ); + } ) + }; +} From 33de1525ea4e9149029612ae1bb3ad58e694eb46 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 16 Sep 2024 09:47:21 +0200 Subject: [PATCH 092/172] Dev tests as ESM. --- .eslintrc.js | 1 + .../ckeditor5-dev-tests/bin/postinstall.js | 7 ++-- .../ckeditor5-dev-tests/bin/testautomated.js | 8 ++-- .../ckeditor5-dev-tests/bin/testmanual.js | 8 ++-- packages/ckeditor5-dev-tests/lib/index.js | 10 ++--- .../lib/tasks/runautomatedtests.js | 32 ++++++++-------- .../lib/tasks/runmanualtests.js | 38 +++++++++---------- .../automated-tests/assertions/attribute.js | 2 +- .../assertions/equal-markup.js | 6 +-- .../utils/automated-tests/getkarmaconfig.js | 14 ++++--- .../utils/automated-tests/getwebpackconfig.js | 18 +++++---- .../utils/automated-tests/karmanotifier.js | 10 +++-- .../utils/automated-tests/parsearguments.js | 14 +++---- .../treatwarningsaserrorswebpackplugin.js | 4 +- .../lib/utils/getdefinitionsfromfile.js | 8 ++-- .../lib/utils/getrelativefilepath.js | 8 ++-- .../utils/manual-tests/compilehtmlfiles.js | 30 ++++++++------- .../lib/utils/manual-tests/compilescripts.js | 14 +++---- .../lib/utils/manual-tests/copyassets.js | 14 +++++-- .../lib/utils/manual-tests/createserver.js | 20 +++++----- .../utils/manual-tests/getwebpackconfig.js | 20 +++++----- .../lib/utils/manual-tests/removedir.js | 12 +++--- .../manual-tests/webpacknotifierplugin.js | 8 ++-- .../lib/utils/requiredll.js | 6 +-- .../utils/transformfileoptiontotestglob.js | 10 ++--- packages/ckeditor5-dev-tests/package.json | 16 ++++---- packages/ckeditor5-dev-tests/vitest.config.js | 23 +++++++++++ 27 files changed, 192 insertions(+), 169 deletions(-) create mode 100644 packages/ckeditor5-dev-tests/vitest.config.js diff --git a/.eslintrc.js b/.eslintrc.js index a0c5542d9..5236b531e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { { files: [ // TODO: add packages as they are migrated to ESM. + './packages/ckeditor5-dev-tests/**/*', './packages/ckeditor5-dev-utils/**/*', './packages/ckeditor5-dev-translations/**/*', './packages/ckeditor5-dev-release-tools/**/*', diff --git a/packages/ckeditor5-dev-tests/bin/postinstall.js b/packages/ckeditor5-dev-tests/bin/postinstall.js index b064e10c0..b206f01e5 100755 --- a/packages/ckeditor5-dev-tests/bin/postinstall.js +++ b/packages/ckeditor5-dev-tests/bin/postinstall.js @@ -5,10 +5,11 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import isWsl from 'is-wsl'; +import { execSync } from 'child_process'; +import { createRequire } from 'module'; -const isWsl = require( 'is-wsl' ); -const { execSync } = require( 'child_process' ); +const require = createRequire( import.meta.url ); if ( isWsl ) { const executables = [ diff --git a/packages/ckeditor5-dev-tests/bin/testautomated.js b/packages/ckeditor5-dev-tests/bin/testautomated.js index 5e745297a..0a71d9fe8 100755 --- a/packages/ckeditor5-dev-tests/bin/testautomated.js +++ b/packages/ckeditor5-dev-tests/bin/testautomated.js @@ -5,11 +5,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const path = require( 'path' ); -const tests = require( '../lib/index' ); +import chalk from 'chalk'; +import path from 'path'; +import * as tests from '../lib/index.js'; const options = tests.parseArguments( process.argv.slice( 2 ) ); diff --git a/packages/ckeditor5-dev-tests/bin/testmanual.js b/packages/ckeditor5-dev-tests/bin/testmanual.js index 62dbb768d..3f1f98c12 100755 --- a/packages/ckeditor5-dev-tests/bin/testmanual.js +++ b/packages/ckeditor5-dev-tests/bin/testmanual.js @@ -5,11 +5,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const chalk = require( 'chalk' ); -const path = require( 'path' ); -const tests = require( '../lib/index' ); +import chalk from 'chalk'; +import path from 'path'; +import * as tests from '../lib/index.js'; const options = tests.parseArguments( process.argv.slice( 2 ) ); diff --git a/packages/ckeditor5-dev-tests/lib/index.js b/packages/ckeditor5-dev-tests/lib/index.js index ea0d64110..d519ff94e 100644 --- a/packages/ckeditor5-dev-tests/lib/index.js +++ b/packages/ckeditor5-dev-tests/lib/index.js @@ -3,10 +3,6 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -module.exports = { - runAutomatedTests: require( './tasks/runautomatedtests' ), - runManualTests: require( './tasks/runmanualtests' ), - parseArguments: require( './utils/automated-tests/parsearguments' ) -}; +export { default as runAutomatedTests } from './tasks/runautomatedtests.js'; +export { default as runManualTests } from './tasks/runmanualtests.js'; +export { default as parseArguments } from './utils/automated-tests/parsearguments.js'; diff --git a/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js b/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js index 055f52c9c..a60467a7e 100644 --- a/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js +++ b/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js @@ -3,18 +3,21 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const getKarmaConfig = require( '../utils/automated-tests/getkarmaconfig' ); -const chalk = require( 'chalk' ); -const { globSync } = require( 'glob' ); -const minimatch = require( 'minimatch' ); -const mkdirp = require( 'mkdirp' ); -const karmaLogger = require( 'karma/lib/logger.js' ); -const karma = require( 'karma' ); -const transformFileOptionToTestGlob = require( '../utils/transformfileoptiontotestglob' ); +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import getKarmaConfig from '../utils/automated-tests/getkarmaconfig.js'; +import chalk from 'chalk'; +import { globSync } from 'glob'; +import minimatch from 'minimatch'; +import mkdirp from 'mkdirp'; +import karmaLogger from 'karma/lib/logger.js'; +import karma from 'karma'; +import transformFileOptionToTestGlob from '../utils/transformfileoptiontotestglob.js'; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); // Glob patterns that should be ignored. It means if a specified test file is located under path // that matches to these patterns, the file will be skipped. @@ -28,7 +31,7 @@ const IGNORE_GLOBS = [ // An absolute path to the entry file that will be passed to Karma. const ENTRY_FILE_PATH = path.posix.join( process.cwd(), 'build', '.automated-tests', 'entry-point.js' ); -module.exports = function runAutomatedTests( options ) { +export default function runAutomatedTests( options ) { return Promise.resolve().then( () => { if ( !options.production ) { console.warn( chalk.yellow( @@ -48,7 +51,7 @@ module.exports = function runAutomatedTests( options ) { return runKarma( optionsForKarma ); } ); -}; +} function transformFilesToTestGlob( files ) { if ( !Array.isArray( files ) || files.length === 0 ) { @@ -211,7 +214,6 @@ function runKarma( options ) { server.on( 'run_complete', () => { // Use timeout to not write to the console in the middle of Karma's status. setTimeout( () => { - const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); const log = logger(); log.info( `Coverage report saved in '${ chalk.cyan( coveragePath ) }'.` ); diff --git a/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js b/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js index 1f8862375..58931749d 100644 --- a/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js +++ b/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js @@ -3,24 +3,22 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const chalk = require( 'chalk' ); -const { globSync } = require( 'glob' ); -const { spawn } = require( 'child_process' ); -const inquirer = require( 'inquirer' ); -const isInteractive = require( 'is-interactive' ); -const { Server: SocketServer } = require( 'socket.io' ); -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); -const createManualTestServer = require( '../utils/manual-tests/createserver' ); -const compileManualTestScripts = require( '../utils/manual-tests/compilescripts' ); -const compileManualTestHtmlFiles = require( '../utils/manual-tests/compilehtmlfiles' ); -const copyAssets = require( '../utils/manual-tests/copyassets' ); -const removeDir = require( '../utils/manual-tests/removedir' ); -const transformFileOptionToTestGlob = require( '../utils/transformfileoptiontotestglob' ); -const requireDll = require( '../utils/requiredll' ); +import fs from 'fs'; +import path from 'path'; +import chalk from 'chalk'; +import { globSync } from 'glob'; +import { spawn } from 'child_process'; +import inquirer from 'inquirer'; +import isInteractive from 'is-interactive'; +import { Server as SocketServer } from 'socket.io'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import createManualTestServer from '../utils/manual-tests/createserver.js'; +import compileManualTestScripts from '../utils/manual-tests/compilescripts.js'; +import compileManualTestHtmlFiles from '../utils/manual-tests/compilehtmlfiles.js'; +import copyAssets from '../utils/manual-tests/copyassets.js'; +import removeDir from '../utils/manual-tests/removedir.js'; +import transformFileOptionToTestGlob from '../utils/transformfileoptiontotestglob.js'; +import requireDll from '../utils/requiredll.js'; /** * Main function that runs manual tests. @@ -41,7 +39,7 @@ const requireDll = require( '../utils/requiredll' ); * @param {Boolean} [options.silent=false] Whether to hide files that will be processed by the script. * @returns {Promise} */ -module.exports = function runManualTests( options ) { +export default function runManualTests( options ) { const log = logger(); const cwd = process.cwd(); const buildDir = path.join( cwd, 'build', '.manual-tests' ); @@ -217,4 +215,4 @@ module.exports = function runManualTests( options ) { } ); } ); } -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js index 5976f6b31..c4556ff09 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js @@ -8,7 +8,7 @@ * * @param {Chai} chai */ -module.exports = chai => { +export default chai => { /** * Asserts that the target has an attribute with the given key name. * diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js index 8d586ea6a..9dd6b42a0 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js @@ -3,15 +3,15 @@ * For licensing, see LICENSE.md. */ -const AssertionError = require( 'assertion-error' ); -const { html_beautify: beautify } = require( 'js-beautify/js/lib/beautify-html' ); +import AssertionError from 'assertion-error'; +import { html_beautify as beautify } from 'js-beautify/js/lib/beautify-html.js'; /** * Factory function that registers the `equalMarkup` assertion. * * @param {Chai} chai */ -module.exports = chai => { +export default chai => { /** * Custom assertion that tests whether two given strings containing markup language are equal. * Unlike `expect().to.equal()` form Chai assertion library, this assertion formats the markup before showing a diff. diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js index 8ae15c36d..6e66ed469 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js @@ -3,10 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import path from 'path'; +import getWebpackConfigForAutomatedTests from './getwebpackconfig.js'; +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; -const path = require( 'path' ); -const getWebpackConfigForAutomatedTests = require( './getwebpackconfig' ); +const require = createRequire( import.meta.url ); +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); const AVAILABLE_REPORTERS = [ 'mocha', @@ -17,7 +21,7 @@ const AVAILABLE_REPORTERS = [ * @param {Object} options * @returns {Object} */ -module.exports = function getKarmaConfig( options ) { +export default function getKarmaConfig( options ) { if ( !AVAILABLE_REPORTERS.includes( options.reporter ) ) { throw new Error( `Specified reporter is not supported. Available reporters: ${ AVAILABLE_REPORTERS.join( ', ' ) }.` ); } @@ -199,7 +203,7 @@ module.exports = function getKarmaConfig( options ) { } return karmaConfig; -}; +} // Returns the value of Karma's browser option. // @returns {Array|null} diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js index a15cc5770..1021e5da8 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js @@ -3,19 +3,21 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import path from 'path'; +import webpack from 'webpack'; +import { loaders } from '@ckeditor/ckeditor5-dev-utils'; +import getDefinitionsFromFile from '../getdefinitionsfromfile.js'; +import TreatWarningsAsErrorsWebpackPlugin from './treatwarningsaserrorswebpackplugin.js'; +import { fileURLToPath } from 'url'; -const path = require( 'path' ); -const webpack = require( 'webpack' ); -const { loaders } = require( '@ckeditor/ckeditor5-dev-utils' ); -const getDefinitionsFromFile = require( '../getdefinitionsfromfile' ); -const TreatWarningsAsErrorsWebpackPlugin = require( './treatwarningsaserrorswebpackplugin' ); +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); /** * @param {Object} options * @returns {Object} */ -module.exports = function getWebpackConfigForAutomatedTests( options ) { +export default function getWebpackConfigForAutomatedTests( options ) { const definitions = Object.assign( {}, getDefinitionsFromFile( options.identityFile ) ); const config = { @@ -102,4 +104,4 @@ module.exports = function getWebpackConfigForAutomatedTests( options ) { } return config; -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/karmanotifier.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/karmanotifier.js index cfb5a8fde..81a8e100c 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/karmanotifier.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/karmanotifier.js @@ -3,8 +3,12 @@ * For licensing, see LICENSE.md. */ -const path = require( 'path' ); -const notifier = require( 'node-notifier' ); +import path from 'path'; +import notifier from 'node-notifier'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); const ckeditor5icon = path.join( __dirname, '..', 'icons', 'ckeditor5.png' ); @@ -14,7 +18,7 @@ const defaultNotifyOptions = { icon: ckeditor5icon }; -module.exports = { 'reporter:karmanotifier': [ 'type', karmaNotifier ] }; +export default { 'reporter:karmanotifier': [ 'type', karmaNotifier ] }; karmaNotifier.$inject = [ 'helper' ]; function karmaNotifier( helper ) { diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js index eefdb774b..567e9eebe 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js @@ -3,18 +3,16 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const minimist = require( 'minimist' ); -const { tools, logger } = require( '@ckeditor/ckeditor5-dev-utils' ); +import fs from 'fs'; +import path from 'path'; +import minimist from 'minimist'; +import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; /** * @param {Array.} args * @returns {Object} */ -module.exports = function parseArguments( args ) { +export default function parseArguments( args ) { const log = logger(); const minimistConfig = { @@ -266,4 +264,4 @@ module.exports = function parseArguments( args ) { return false; } } -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/treatwarningsaserrorswebpackplugin.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/treatwarningsaserrorswebpackplugin.js index bcd0a1bcc..718680e25 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/treatwarningsaserrorswebpackplugin.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/treatwarningsaserrorswebpackplugin.js @@ -6,7 +6,7 @@ /** * Webpack plugin that reassigns warnings as errors and stops the process if any errors or warnings detected. */ -module.exports = class TreatWarningsAsErrorsWebpackPlugin { +export default class TreatWarningsAsErrorsWebpackPlugin { apply( compiler ) { compiler.hooks.shouldEmit.tap( 'TreatWarningsAsErrorsWebpackPlugin', compilation => { compilation.errors = [ @@ -21,4 +21,4 @@ module.exports = class TreatWarningsAsErrorsWebpackPlugin { } } ); } -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js b/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js index 7729b456a..f4b552825 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js +++ b/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js @@ -3,15 +3,13 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); +import path from 'path'; /** * @param {String|null} definitionSource * @returns {Object} */ -module.exports = function getDefinitionsFromFile( definitionSource ) { +export default function getDefinitionsFromFile( definitionSource ) { if ( !definitionSource ) { return {}; } @@ -31,7 +29,7 @@ module.exports = function getDefinitionsFromFile( definitionSource ) { return {}; } -}; +} /** * @param {String|null} definitionSource diff --git a/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js b/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js index 80acee08f..431d76ec1 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js +++ b/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js @@ -3,9 +3,7 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); +import path from 'path'; /** * Get a path to a source file which will uniquely identify this file in @@ -21,7 +19,7 @@ const path = require( 'path' ); * @param {String} [cwd=process.cwd()] * @returns {String} */ -module.exports = function getRelativeFilePath( filePath, cwd = process.cwd() ) { +export default function getRelativeFilePath( filePath, cwd = process.cwd() ) { // The path ends with the directory separator. const relativePath = filePath.replace( cwd, '' ).slice( 1 ); @@ -32,4 +30,4 @@ module.exports = function getRelativeFilePath( filePath, cwd = process.cwd() ) { // The main repository. return path.join( 'ckeditor5', relativePath ); -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js index 796d41fed..a8284dc81 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js @@ -3,18 +3,20 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const fs = require( 'fs-extra' ); -const { globSync } = require( 'glob' ); -const _ = require( 'lodash' ); -const chalk = require( 'chalk' ); -const commonmark = require( 'commonmark' ); -const combine = require( 'dom-combiner' ); -const chokidar = require( 'chokidar' ); -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); -const getRelativeFilePath = require( '../getrelativefilepath' ); +import path from 'path'; +import fs from 'fs-extra'; +import { globSync } from 'glob'; +import _ from 'lodash'; +import chalk from 'chalk'; +import * as commonmark from 'commonmark'; +import combine from 'dom-combiner'; +import chokidar from 'chokidar'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import getRelativeFilePath from '../getrelativefilepath.js'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); const reader = new commonmark.Parser(); const writer = new commonmark.HtmlRenderer(); @@ -30,7 +32,7 @@ const writer = new commonmark.HtmlRenderer(); * @param {Boolean} [options.silent=false] Whether to hide files that will be processed by the script. * @returns {Promise} */ -module.exports = function compileHtmlFiles( options ) { +export default function compileHtmlFiles( options ) { const buildDir = options.buildDir; const viewTemplate = fs.readFileSync( path.join( __dirname, 'template.html' ), 'utf-8' ); const silent = options.silent || false; @@ -80,7 +82,7 @@ module.exports = function compileHtmlFiles( options ) { } ); }, options.onTestCompilationStatus ); } -}; +} /** * @param {String} buildDir An absolute path to the directory where the processed file should be saved. diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js index 4853448e4..600b8906f 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const webpack = require( 'webpack' ); -const getWebpackConfigForManualTests = require( './getwebpackconfig' ); -const getRelativeFilePath = require( '../getrelativefilepath' ); -const requireDll = require( '../requiredll' ); +import webpack from 'webpack'; +import getWebpackConfigForManualTests from './getwebpackconfig.js'; +import getRelativeFilePath from '../getrelativefilepath.js'; +import requireDll from '../requiredll.js'; /** * @param {Object} options @@ -25,7 +23,7 @@ const requireDll = require( '../requiredll' ); * @param {String} [options.identityFile] A file that provides secret keys used in the test scripts. * @returns {Promise} */ -module.exports = function compileManualTestScripts( options ) { +export default function compileManualTestScripts( options ) { const entryFiles = options.sourceFiles; const entryFilesDLL = entryFiles.filter( entryFile => requireDll( entryFile ) ); const entryFilesNonDll = entryFiles.filter( entryFile => !requireDll( entryFile ) ); @@ -71,7 +69,7 @@ module.exports = function compileManualTestScripts( options ) { const webpackPromises = webpackConfigs.map( config => runWebpack( config ) ); return Promise.all( webpackPromises ); -}; +} /** * @returns {Promise} diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/copyassets.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/copyassets.js index 36c583200..75d71d3a1 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/copyassets.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/copyassets.js @@ -3,8 +3,14 @@ * For licensing, see LICENSE.md. */ -const path = require( 'path' ); -const fs = require( 'fs-extra' ); +import path from 'path'; +import fs from 'fs-extra'; +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; + +const require = createRequire( import.meta.url ); +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); const assets = [ path.join( __dirname, 'togglesidebar.js' ), @@ -23,9 +29,9 @@ const assets = [ * │ └── ... * ... */ -module.exports = function copyAssets( buildDir ) { +export default function copyAssets( buildDir ) { for ( const assetPath of assets ) { const outputFilePath = path.join( buildDir, 'assets', path.basename( assetPath ) ); fs.copySync( assetPath, outputFilePath ); } -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js index 29460b290..cb2d08a3d 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js @@ -3,14 +3,16 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import http from 'http'; +import path from 'path'; +import { globSync } from 'glob'; +import fs from 'fs'; +import combine from 'dom-combiner'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import { fileURLToPath } from 'url'; -const http = require( 'http' ); -const path = require( 'path' ); -const { globSync } = require( 'glob' ); -const fs = require( 'fs' ); -const combine = require( 'dom-combiner' ); -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); /** * Basic HTTP server. @@ -19,7 +21,7 @@ const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); * @param {Number} [port=8125] Port to listen at. * @param {Function} [onCreate] A callback called with the reference to the HTTP server when it is up and running. */ -module.exports = function createManualTestServer( sourcePath, port = 8125, onCreate ) { +export default function createManualTestServer( sourcePath, port = 8125, onCreate ) { return new Promise( resolve => { const server = http.createServer( ( request, response ) => { onRequest( sourcePath, request, response ); @@ -56,7 +58,7 @@ module.exports = function createManualTestServer( sourcePath, port = 8125, onCre onCreate( server ); } } ); -}; +} function onRequest( sourcePath, request, response ) { response.writeHead( 200, { diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js index 4dbab390b..daa45f37f 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js @@ -3,14 +3,16 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import path from 'path'; +import webpack from 'webpack'; +import { CKEditorTranslationsPlugin } from '@ckeditor/ckeditor5-dev-translations'; +import { loaders } from '@ckeditor/ckeditor5-dev-utils'; +import WebpackNotifierPlugin from './webpacknotifierplugin.js'; +import getDefinitionsFromFile from '../getdefinitionsfromfile.js'; +import { fileURLToPath } from 'url'; -const path = require( 'path' ); -const webpack = require( 'webpack' ); -const { CKEditorTranslationsPlugin } = require( '@ckeditor/ckeditor5-dev-translations' ); -const { loaders } = require( '@ckeditor/ckeditor5-dev-utils' ); -const WebpackNotifierPlugin = require( './webpacknotifierplugin' ); -const getDefinitionsFromFile = require( '../getdefinitionsfromfile' ); +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); /** * @param {Object} options @@ -26,7 +28,7 @@ const getDefinitionsFromFile = require( '../getdefinitionsfromfile' ); * @param {String|null} [options.identityFile] * @returns {Object} */ -module.exports = function getWebpackConfigForManualTests( options ) { +export default function getWebpackConfigForManualTests( options ) { const definitions = Object.assign( {}, getDefinitionsFromFile( options.identityFile ) ); const webpackConfig = { @@ -149,5 +151,5 @@ module.exports = function getWebpackConfigForManualTests( options ) { } return webpackConfig; -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js index 069926fcd..8189e524c 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js @@ -3,11 +3,9 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const del = require( 'del' ); -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); -const chalk = require( 'chalk' ); +import del from 'del'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import chalk from 'chalk'; /** * Removes the specified directory. @@ -19,7 +17,7 @@ const chalk = require( 'chalk' ); * @param {Boolean} [options.silent=false] Whether to hide the path to the directory on the console. * @returns {Promise} */ -module.exports = function removeDir( dir, options = {} ) { +export default function removeDir( dir, options = {} ) { return del( dir ).then( () => { if ( !options.silent ) { logger().info( `Removed directory '${ chalk.cyan( dir ) }'` ); @@ -27,4 +25,4 @@ module.exports = function removeDir( dir, options = {} ) { return Promise.resolve(); } ); -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js index 01d1f3b96..48d9e80ed 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js @@ -3,14 +3,12 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const { logger } = require( '@ckeditor/ckeditor5-dev-utils' ); +import { logger } from '@ckeditor/ckeditor5-dev-utils'; /** * Plugin for Webpack which helps to inform the developer about processes. */ -module.exports = class WebpackNotifierPlugin { +export default class WebpackNotifierPlugin { /** * @param {Object} options * @param {Function} options.onTestCompilationStatus @@ -56,4 +54,4 @@ module.exports = class WebpackNotifierPlugin { this.onTestCompilationStatus( `finished:${ this.processName }` ); } ); } -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/requiredll.js b/packages/ckeditor5-dev-tests/lib/utils/requiredll.js index 023064783..ebf320ce8 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/requiredll.js +++ b/packages/ckeditor5-dev-tests/lib/utils/requiredll.js @@ -3,16 +3,14 @@ * For licensing, see LICENSE.md. */ -'use strict'; - /** * Returns `true` if any of the source files represent a DLL test. * * @param {String|Array.} sourceFiles * @returns {Boolean} */ -module.exports = function requireDll( sourceFiles ) { +export default function requireDll( sourceFiles ) { sourceFiles = Array.isArray( sourceFiles ) ? sourceFiles : [ sourceFiles ]; return sourceFiles.some( filePath => /-dll.[jt]s$/.test( filePath ) ); -}; +} diff --git a/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js b/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js index e8382c368..08cedc7b1 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js +++ b/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js @@ -3,10 +3,8 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); +import fs from 'fs'; +import path from 'path'; const EXTERNAL_DIR_PATH = path.join( process.cwd(), 'external' ); @@ -24,7 +22,7 @@ const EXTERNAL_DIR_PATH = path.join( process.cwd(), 'external' ); * @param {Boolean} [isManualTest=false] Whether the tests are manual or automated. * @returns {Array.} */ -module.exports = function transformFileOptionToTestGlob( pattern, isManualTest = false ) { +export default function transformFileOptionToTestGlob( pattern, isManualTest = false ) { if ( doesPatternMatchExternalRepositoryName( pattern ) ) { return getExternalRepositoryGlob( pattern, { isManualTest } ); } @@ -54,7 +52,7 @@ module.exports = function transformFileOptionToTestGlob( pattern, isManualTest = transformedPathForExternalPackagesWithCKEditorPrefix ] ) ]; -}; +} /** * @param {String} pattern diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 8378830b1..94f0c471d 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -16,6 +16,7 @@ "node": ">=18.0.0", "npm": ">=5.7.1" }, + "type": "module", "main": "lib/index.js", "files": [ "lib", @@ -74,8 +75,12 @@ "webpack": "^5.94.0" }, "devDependencies": { - "mockery": "^2.1.0", - "proxyquire": "^2.1.3" + "vitest": "^2.0.5" + }, + "scripts": { + "postinstall": "node bin/postinstall.js", + "test": "vitest run --config vitest.config.js", + "coverage": "vitest run --config vitest.config.js --coverage" }, "depcheckIgnore": [ "@babel/core", @@ -88,10 +93,5 @@ "mocha", "process", "typescript" - ], - "scripts": { - "postinstall": "node bin/postinstall.js", - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" - } + ] } diff --git a/packages/ckeditor5-dev-tests/vitest.config.js b/packages/ckeditor5-dev-tests/vitest.config.js new file mode 100644 index 000000000..5ad784a28 --- /dev/null +++ b/packages/ckeditor5-dev-tests/vitest.config.js @@ -0,0 +1,23 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig( { + test: { + testTimeout: 10000, + restoreMocks: true, + include: [ + 'tests/**/*.js' + ], + coverage: { + provider: 'v8', + include: [ + 'lib/**' + ], + reporter: [ 'text', 'json', 'html', 'lcov' ] + } + } +} ); From e352edf7786824941e429f87157d50908d2a9601 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 16 Sep 2024 10:29:12 +0200 Subject: [PATCH 093/172] Support for running maunal tests. --- .../lib/findmessages.js | 7 ++- .../lib/servetranslations.js | 5 ++- .../ckeditor5-dev-translations/package.json | 1 + .../tests/findmessages.js | 43 ++++++++++++++++++- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/ckeditor5-dev-translations/lib/findmessages.js b/packages/ckeditor5-dev-translations/lib/findmessages.js index 631bbed1b..5e1369bd9 100644 --- a/packages/ckeditor5-dev-translations/lib/findmessages.js +++ b/packages/ckeditor5-dev-translations/lib/findmessages.js @@ -4,7 +4,7 @@ */ import parser from '@babel/parser'; -import traverse from '@babel/traverse'; +import { default as traverse } from '@babel/traverse'; /** * Parses source and finds messages from the first argument of `t()` calls. @@ -24,7 +24,10 @@ export default function findMessages( source, sourceFile, onMessageFound, onErro ] } ); - traverse( ast, { + // Support for a non-`type=module` project. + const traverseCallable = typeof traverse === 'function' ? traverse : traverse.default; + + traverseCallable( ast, { CallExpression: ( { node } ) => { try { findMessagesInNode( node ); diff --git a/packages/ckeditor5-dev-translations/lib/servetranslations.js b/packages/ckeditor5-dev-translations/lib/servetranslations.js index d6334a0a2..1e058140e 100644 --- a/packages/ckeditor5-dev-translations/lib/servetranslations.js +++ b/packages/ckeditor5-dev-translations/lib/servetranslations.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import fs from 'fs'; +import fs from 'fs-extra'; import path from 'path'; import { fileURLToPath } from 'url'; import chalk from 'chalk'; @@ -88,7 +88,7 @@ export default function serveTranslations( compiler, options, translationService } // Add all context messages found in the core package. - const contexts = require( pathToResource ); + const contexts = fs.readJsonSync( pathToResource ); for ( const item of Object.keys( contexts ) ) { translationService.addIdMessage( item ); @@ -107,6 +107,7 @@ export default function serveTranslations( compiler, options, translationService // after any potential TypeScript file has already been compiled. module.loaders.unshift( { loader: path.join( __dirname, 'translatesourceloader.js' ), + type: 'module', options: { translateSource } } ); diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index f70a06f44..c4cc85f0f 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -25,6 +25,7 @@ "@babel/parser": "^7.18.9", "@babel/traverse": "^7.18.9", "chalk": "^4.0.0", + "fs-extra": "^11.2.0", "rimraf": "^3.0.2", "webpack-sources": "^2.0.1", "pofile": "^1.0.9" diff --git a/packages/ckeditor5-dev-translations/tests/findmessages.js b/packages/ckeditor5-dev-translations/tests/findmessages.js index b0c975df6..332cfe634 100644 --- a/packages/ckeditor5-dev-translations/tests/findmessages.js +++ b/packages/ckeditor5-dev-translations/tests/findmessages.js @@ -3,10 +3,16 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it } from 'vitest'; -import findMessages from '../lib/findmessages.js'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import traverse from '@babel/traverse'; describe( 'findMessages', () => { + let findMessages; + + beforeEach( async () => { + findMessages = ( await import( '../lib/findmessages.js' ) ).default; + } ); + it( 'should parse provided code and find messages from `t()` function calls on string literals', () => { const messages = []; @@ -207,4 +213,37 @@ describe( 'findMessages', () => { 'First t() call argument should be a string literal or an object literal (foo.js).' ] ); } ); + + describe( 'a non-type=module project support', () => { + beforeEach( async () => { + vi.resetAllMocks(); + vi.clearAllMocks(); + vi.resetModules(); + + vi.doMock( '@babel/traverse', () => ( { + default: { + default: traverse + } + } ) ); + + findMessages = ( await import( '../lib/findmessages.js' ) ).default; + } ); + + it( 'should parse provided code and find messages from `t()` function calls on string literals', () => { + const messages = []; + + findMessages( + `function x() { + const t = this.t; + t( 'Image' ); + t( 'CKEditor' ); + g( 'Some other function' ); + }`, + 'foo.js', + message => messages.push( message ) + ); + + expect( messages ).to.deep.equal( [ { id: 'Image', string: 'Image' }, { id: 'CKEditor', string: 'CKEditor' } ] ); + } ); + } ); } ); From 5d1f8e4226d96832b14ade0bcecce4247051c504 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 16 Sep 2024 10:51:47 +0200 Subject: [PATCH 094/172] Support for a custom Karma configuratiom for an IntelliJ IDE. --- packages/ckeditor5-dev-tests/bin/intellijkarmarunner/README.md | 2 +- packages/ckeditor5-dev-tests/bin/intellijkarmarunner/bin/karma | 2 +- .../intellijkarmarunner/{karma.config.js => karma.config.cjs} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/ckeditor5-dev-tests/bin/intellijkarmarunner/{karma.config.js => karma.config.cjs} (100%) diff --git a/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/README.md b/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/README.md index f86154942..c936ef936 100644 --- a/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/README.md +++ b/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/README.md @@ -18,7 +18,7 @@ yarn run test --files=engine,basic-styles 1. In the IDE, go to _Run_ > _Edit configurations..._: 1. Add a new configuration of type "**Karma**" and a name of your preference. -1. In "Configuration file", selected the "**node\_modules/ckeditor5-dev-tests/bin/intellijkarmarunner/karma.config.js**" file. +1. In "Configuration file", selected the "**node\_modules/ckeditor5-dev-tests/bin/intellijkarmarunner/karma.config.cjs**" file. 1. In "Karma Package", selected the "**node\_modules/ckeditor5-dev-tests/bin/intellijkarmarunner**" directory. 1. In "Karma options", input the CKEditor 5 tests arguments. E.g. `--files=engine,basic-styles`. 1. In "Working directory", select the base `ckeditor5` directory. diff --git a/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/bin/karma b/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/bin/karma index c583d22db..1e9a7c965 100755 --- a/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/bin/karma +++ b/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/bin/karma @@ -33,4 +33,4 @@ const intellijConfig = process.argv.find( item => item.includes( 'intellij.conf. process.argv.push( '--karma-config-overrides=' + intellijConfig ); // Now running the tests. -require( '../../testautomated' ); +import( '../../testautomated.js' ); diff --git a/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/karma.config.js b/packages/ckeditor5-dev-tests/bin/intellijkarmarunner/karma.config.cjs similarity index 100% rename from packages/ckeditor5-dev-tests/bin/intellijkarmarunner/karma.config.js rename to packages/ckeditor5-dev-tests/bin/intellijkarmarunner/karma.config.cjs From bdd79d9ab29c9db1d9432f185d4000a4d70db8d1 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 16 Sep 2024 11:30:02 +0200 Subject: [PATCH 095/172] CircleCI: Support for running package tests in separate steps. --- .circleci/config.yml | 269 ++---------------- .circleci/template.yml | 266 +++++++++++++++++ .eslintrc.js => .eslintrc.cjs | 0 .gitignore | 3 + package.json | 2 + scripts/ci/combine-coverage-lcov.js | 30 ++ scripts/ci/generate-circleci-configuration.js | 136 +++++++++ scripts/runtest.js | 32 +-- 8 files changed, 476 insertions(+), 262 deletions(-) create mode 100644 .circleci/template.yml rename .eslintrc.js => .eslintrc.cjs (100%) create mode 100644 scripts/ci/combine-coverage-lcov.js create mode 100644 scripts/ci/generate-circleci-configuration.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 502462aa1..8aaa9b41a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,5 +1,21 @@ +# This is the based configuration required by CircleCI to run a build. +# +# The repository uses the dynamic configuration to generate +# tasks for executing tests and checking the code coverage. +# +# This configuration aims to prepare a complete design and continue checking +# the repository in a new workflow. +# +# To modify the commands to execute on CI, review the following files: +# - scripts/ci/generate-circleci-configuration.js - the script that creates the `config-tests.yml` file used on the new workflow. +# - .circleci/template.yml - the template filled with data to execute. +# +# Useful resources: +# - https://circleci.com/docs/using-dynamic-configuration/ version: 2.1 +setup: true + parameters: triggerCommitHash: type: string @@ -11,16 +27,10 @@ parameters: type: boolean default: false -commands: - bootstrap_repository_command: - description: "Bootstrap the repository" - steps: - - install_ssh_keys_command - - run: - name: Install dependencies - command: yarn install - - prepare_environment_variables_commands +orbs: + continuation: circleci/continuation@0.1.2 +commands: install_ssh_keys_command: description: "Install SSH keys" steps: @@ -28,244 +38,23 @@ commands: fingerprints: - "a0:41:a2:56:c8:7d:3f:29:41:d1:87:92:fd:50:2b:6b" - npm_login_command: - description: "Enable interacting with `npm` using an auth token" - steps: - - run: - name: Login to the npm registry using '.npmrc' file - command: echo "//registry.npmjs.org/:_authToken=\${CKE5_NPM_TOKEN}" > ~/.npmrc - - git_credentials_command: - description: "Setup git configuration" - steps: - - run: - name: Setup git configuration - command: | - git config --global user.email "ckeditor-bot@cksource.com" - git config --global user.name "CKEditorBot" - - prepare_environment_variables_commands: - description: "Prepare non-secret environment variables" - steps: - - run: - name: Prepare environment variables - command: | - #!/bin/bash - - # Non-secret environment variables needed for the pipeline scripts. - CKE5_GITHUB_ORGANIZATION="ckeditor" - CKE5_GITHUB_REPOSITORY="ckeditor5-dev" - CKE5_CIRCLE_APPROVAL_JOB_NAME="release_approval" - CKE5_GITHUB_RELEASE_BRANCH="master" - - echo export CKE5_CIRCLE_APPROVAL_JOB_NAME=$CKE5_CIRCLE_APPROVAL_JOB_NAME >> $BASH_ENV - echo export CKE5_GITHUB_RELEASE_BRANCH=$CKE5_GITHUB_RELEASE_BRANCH >> $BASH_ENV - echo export CKE5_GITHUB_ORGANIZATION=$CKE5_GITHUB_ORGANIZATION >> $BASH_ENV - echo export CKE5_GITHUB_REPOSITORY=$CKE5_GITHUB_REPOSITORY >> $BASH_ENV - echo export CKE5_GITHUB_REPOSITORY_SLUG="$CKE5_GITHUB_ORGANIZATION/$CKE5_GITHUB_REPOSITORY" >> $BASH_ENV - echo export CKE5_COMMIT_SHA1=$CIRCLE_SHA1 >> $BASH_ENV - jobs: - notify_ci_failure: - machine: true - parameters: - hideAuthor: - type: string - default: "false" - steps: - - checkout - - bootstrap_repository_command - - run: - # In the PRs that comes from forked repositories, we do not share secret variables. - # Hence, some of the scripts will not be executed. - name: 👤 Verify if the build was triggered by community - Check if the build should continue - command: | - #!/bin/bash - - if [[ -z ${COVERALLS_REPO_TOKEN} ]]; - then - circleci-agent step halt - fi - - run: - environment: - CKE5_SLACK_NOTIFY_HIDE_AUTHOR: << parameters.hideAuthor >> - CKE5_PIPELINE_NUMBER: << pipeline.number >> - name: Waiting for other jobs to finish and sending notification on failure - command: yarn ckeditor5-dev-ci-circle-workflow-notifier - no_output_timeout: 1h - - validate_and_tests: - machine: true - resource_class: large - steps: - - checkout - - bootstrap_repository_command - - run: - name: Execute ESLint - command: yarn run lint - - run: - environment: - TZ: Europe/Warsaw - name: Run unit tests - command: yarn run coverage - - unless: - # Upload the code coverage results for non-nightly builds only. - condition: << pipeline.parameters.isNightly >> - steps: - - run: - # In the PRs that comes from forked repositories, we do not share secret variables. - # Hence, some of the scripts will not be executed. - name: 👤 Verify if the build was triggered by community - Check if the build should continue - command: | - #!/bin/bash - - if [[ -z ${COVERALLS_REPO_TOKEN} ]]; - then - circleci-agent step halt - fi - - run: - name: Upload code coverage - command: cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js - - release_prepare: - machine: true - resource_class: large - steps: - - checkout - - bootstrap_repository_command - - run: - name: Check if packages are ready to be released - command: yarn run release:prepare-packages --verbose --compile-only - - trigger_release_process: - machine: true - resource_class: large - steps: - - checkout - - bootstrap_repository_command - - run: - name: Verify if the project is ready to release - command: | - #!/bin/bash - - # Do not fail if the Node script ends with non-zero exit code. - set +e - - node scripts/ci/is-project-ready-to-release.js - EXIT_CODE=$( echo $? ) - - if [ ${EXIT_CODE} -eq 1 ]; - then - circleci-agent step halt - fi - - run: - name: Trigger the release pipeline - command: yarn ckeditor5-dev-ci-trigger-circle-build - - release_project: + generate_configuration: machine: true - resource_class: large steps: - checkout - - bootstrap_repository_command - - run: - name: Verify the trigger commit from the repository - command: | - #!/bin/bash - - CKE5_LATEST_COMMIT_HASH=$( git log -n 1 --pretty=format:%H origin/master ) - CKE5_TRIGGER_COMMIT_HASH=<< pipeline.parameters.triggerCommitHash >> - - if [[ "${CKE5_LATEST_COMMIT_HASH}" != "${CKE5_TRIGGER_COMMIT_HASH}" ]]; then - echo "There is a newer commit in the repository on the \`#master\` branch. Use its build to start the release." - circleci-agent step halt - fi - - npm_login_command - - git_credentials_command - - run: - name: Verify if a releaser triggered the job - command: | - #!/bin/bash - - # Do not fail if the Node script ends with non-zero exit code. - set +e - - yarn ckeditor5-dev-ci-is-job-triggered-by-member - EXIT_CODE=$( echo $? ) - - if [ ${EXIT_CODE} -ne 0 ]; - then - echo "Aborting the release due to failed verification of the approver (no rights to release)." - circleci-agent step halt - fi - - run: - name: Disable the redundant workflows option - command: yarn ckeditor5-dev-ci-circle-disable-auto-cancel-builds - - run: - name: Prepare the new version to release - command: npm run release:prepare-packages -- --verbose - - run: - name: Publish the packages - command: npm run release:publish-packages -- --verbose + - install_ssh_keys_command - run: - name: Enable the redundant workflows option - command: yarn ckeditor5-dev-ci-circle-enable-auto-cancel-builds - when: always + name: Install dependencies + command: yarn install - run: - name: Pack the "release/" directory (in case of failure) - command: | - zip -r ./release.zip ./release - when: always - - store_artifacts: - path: ./release.zip - when: always + name: Generate a new configuration to check all packages in the repository + command: node scripts/ci/generate-circleci-configuration.js + - continuation/continue: + configuration_path: .circleci/config-tests.yml workflows: version: 2 - main: - when: - and: - - equal: [ false, << pipeline.parameters.isNightly >> ] - - equal: [ false, << pipeline.parameters.isRelease >> ] - jobs: - - validate_and_tests - - release_prepare - - trigger_release_process: - requires: - - validate_and_tests - - release_prepare - filters: - branches: - only: - - master - - notify_ci_failure: - filters: - branches: - only: - - master - - release: - when: - and: - - equal: [ false, << pipeline.parameters.isNightly >> ] - - equal: [ true, << pipeline.parameters.isRelease >> ] - jobs: - - release_approval: - type: approval - - release_project: - requires: - - release_approval - - nightly: - when: - and: - - equal: [ true, << pipeline.parameters.isNightly >> ] - - equal: [ false, << pipeline.parameters.isRelease >> ] + config: jobs: - - validate_and_tests - - notify_ci_failure: - hideAuthor: "true" - filters: - branches: - only: - - master + - generate_configuration diff --git a/.circleci/template.yml b/.circleci/template.yml new file mode 100644 index 000000000..aa1e6488d --- /dev/null +++ b/.circleci/template.yml @@ -0,0 +1,266 @@ +version: 2.1 + +parameters: + triggerCommitHash: + type: string + default: "" + isNightly: + type: boolean + default: false + isRelease: + type: boolean + default: false + +commands: + bootstrap_repository_command: + description: "Bootstrap the repository" + steps: + - install_ssh_keys_command + - run: + name: Install dependencies + command: yarn install + - prepare_environment_variables_commands + + install_ssh_keys_command: + description: "Install SSH keys" + steps: + - add_ssh_keys: + fingerprints: + - "a0:41:a2:56:c8:7d:3f:29:41:d1:87:92:fd:50:2b:6b" + + npm_login_command: + description: "Enable interacting with `npm` using an auth token" + steps: + - run: + name: Login to the npm registry using '.npmrc' file + command: echo "//registry.npmjs.org/:_authToken=\${CKE5_NPM_TOKEN}" > ~/.npmrc + + git_credentials_command: + description: "Setup git configuration" + steps: + - run: + name: Setup git configuration + command: | + git config --global user.email "ckeditor-bot@cksource.com" + git config --global user.name "CKEditorBot" + + prepare_environment_variables_commands: + description: "Prepare non-secret environment variables" + steps: + - run: + name: Prepare environment variables + command: | + #!/bin/bash + + # Non-secret environment variables needed for the pipeline scripts. + CKE5_GITHUB_ORGANIZATION="ckeditor" + CKE5_GITHUB_REPOSITORY="ckeditor5-dev" + CKE5_CIRCLE_APPROVAL_JOB_NAME="release_approval" + CKE5_GITHUB_RELEASE_BRANCH="master" + + echo export CKE5_CIRCLE_APPROVAL_JOB_NAME=$CKE5_CIRCLE_APPROVAL_JOB_NAME >> $BASH_ENV + echo export CKE5_GITHUB_RELEASE_BRANCH=$CKE5_GITHUB_RELEASE_BRANCH >> $BASH_ENV + echo export CKE5_GITHUB_ORGANIZATION=$CKE5_GITHUB_ORGANIZATION >> $BASH_ENV + echo export CKE5_GITHUB_REPOSITORY=$CKE5_GITHUB_REPOSITORY >> $BASH_ENV + echo export CKE5_GITHUB_REPOSITORY_SLUG="$CKE5_GITHUB_ORGANIZATION/$CKE5_GITHUB_REPOSITORY" >> $BASH_ENV + echo export CKE5_COMMIT_SHA1=$CIRCLE_SHA1 >> $BASH_ENV + +jobs: + notify_ci_failure: + machine: true + parameters: + hideAuthor: + type: string + default: "false" + steps: + - checkout + - bootstrap_repository_command + - run: + # In the PRs that comes from forked repositories, we do not share secret variables. + # Hence, some of the scripts will not be executed. + name: 👤 Verify if the build was triggered by community - Check if the build should continue + command: | + #!/bin/bash + + if [[ -z ${COVERALLS_REPO_TOKEN} ]]; + then + circleci-agent step halt + fi + - run: + environment: + CKE5_SLACK_NOTIFY_HIDE_AUTHOR: << parameters.hideAuthor >> + CKE5_PIPELINE_NUMBER: << pipeline.number >> + name: Waiting for other jobs to finish and sending notification on failure + command: yarn ckeditor5-dev-ci-circle-workflow-notifier + no_output_timeout: 1h + + validate_and_tests: + machine: true + resource_class: large + steps: + - checkout + - bootstrap_repository_command + - run: + name: Execute ESLint + command: yarn run lint + - unless: + # Upload the code coverage results for non-nightly builds only. + condition: << pipeline.parameters.isNightly >> + steps: + - run: + # In the PRs that comes from forked repositories, we do not share secret variables. + # Hence, some of the scripts will not be executed. + name: 👤 Verify if the build was triggered by community - Check if the build should continue + command: | + #!/bin/bash + + if [[ -z ${COVERALLS_REPO_TOKEN} ]]; + then + circleci-agent step halt + fi + - run: + name: Upload code coverage + command: cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js + + release_prepare: + machine: true + resource_class: large + steps: + - checkout + - bootstrap_repository_command + - run: + name: Check if packages are ready to be released + command: yarn run release:prepare-packages --verbose --compile-only + + trigger_release_process: + machine: true + resource_class: large + steps: + - checkout + - bootstrap_repository_command + - run: + name: Verify if the project is ready to release + command: | + #!/bin/bash + + # Do not fail if the Node script ends with non-zero exit code. + set +e + + node scripts/ci/is-project-ready-to-release.js + EXIT_CODE=$( echo $? ) + + if [ ${EXIT_CODE} -eq 1 ]; + then + circleci-agent step halt + fi + - run: + name: Trigger the release pipeline + command: yarn ckeditor5-dev-ci-trigger-circle-build + + release_project: + machine: true + resource_class: large + steps: + - checkout + - bootstrap_repository_command + - run: + name: Verify the trigger commit from the repository + command: | + #!/bin/bash + + CKE5_LATEST_COMMIT_HASH=$( git log -n 1 --pretty=format:%H origin/master ) + CKE5_TRIGGER_COMMIT_HASH=<< pipeline.parameters.triggerCommitHash >> + + if [[ "${CKE5_LATEST_COMMIT_HASH}" != "${CKE5_TRIGGER_COMMIT_HASH}" ]]; then + echo "There is a newer commit in the repository on the \`#master\` branch. Use its build to start the release." + circleci-agent step halt + fi + - npm_login_command + - git_credentials_command + - run: + name: Verify if a releaser triggered the job + command: | + #!/bin/bash + + # Do not fail if the Node script ends with non-zero exit code. + set +e + + yarn ckeditor5-dev-ci-is-job-triggered-by-member + EXIT_CODE=$( echo $? ) + + if [ ${EXIT_CODE} -ne 0 ]; + then + echo "Aborting the release due to failed verification of the approver (no rights to release)." + circleci-agent step halt + fi + - run: + name: Disable the redundant workflows option + command: yarn ckeditor5-dev-ci-circle-disable-auto-cancel-builds + - run: + name: Prepare the new version to release + command: npm run release:prepare-packages -- --verbose + - run: + name: Publish the packages + command: npm run release:publish-packages -- --verbose + - run: + name: Enable the redundant workflows option + command: yarn ckeditor5-dev-ci-circle-enable-auto-cancel-builds + when: always + - run: + name: Pack the "release/" directory (in case of failure) + command: | + zip -r ./release.zip ./release + when: always + - store_artifacts: + path: ./release.zip + when: always + +workflows: + version: 2 + main: + when: + and: + - equal: [ false, << pipeline.parameters.isNightly >> ] + - equal: [ false, << pipeline.parameters.isRelease >> ] + jobs: + - validate_and_tests + - release_prepare + - trigger_release_process: + requires: + - validate_and_tests + - release_prepare + filters: + branches: + only: + - master + - notify_ci_failure: + filters: + branches: + only: + - master + + release: + when: + and: + - equal: [ false, << pipeline.parameters.isNightly >> ] + - equal: [ true, << pipeline.parameters.isRelease >> ] + jobs: + - release_approval: + type: approval + - release_project: + requires: + - release_approval + + nightly: + when: + and: + - equal: [ true, << pipeline.parameters.isNightly >> ] + - equal: [ false, << pipeline.parameters.isRelease >> ] + jobs: + - validate_and_tests + - notify_ci_failure: + hideAuthor: "true" + filters: + branches: + only: + - master diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 100% rename from .eslintrc.js rename to .eslintrc.cjs diff --git a/.gitignore b/.gitignore index 1b81873e8..faaf9bd36 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ packages/ckeditor5-dev-build-tools/dist packages/ckeditor5-dev-release-tools/tests/test-fixtures/** !packages/ckeditor5-dev-release-tools/tests/test-fixtures/.gitkeep + +# Generated automatically via CircleCI. +.circleci/config-tests.yml diff --git a/package.json b/package.json index cabf2e784..dceda2c5c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "engines": { "node": ">=18.0.0" }, + "type": "module", "devDependencies": { "@ckeditor/ckeditor5-dev-ci": "^43.0.0", "@ckeditor/ckeditor5-dev-release-tools": "^43.0.0", @@ -23,6 +24,7 @@ "fs-extra": "^11.2.0", "glob": "^10.2.5", "husky": "^8.0.2", + "js-yaml": "^4.1.0", "lint-staged": "^10.2.4", "listr2": "^6.5.0", "minimist": "^1.2.8", diff --git a/scripts/ci/combine-coverage-lcov.js b/scripts/ci/combine-coverage-lcov.js new file mode 100644 index 000000000..651242948 --- /dev/null +++ b/scripts/ci/combine-coverage-lcov.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* eslint-env node */ + +import path from 'path'; +import fs from 'fs-extra'; +import { globSync } from 'glob'; +import chalk from 'chalk'; + +const cwd = process.cwd(); +const coverageFile = path.join( cwd, 'coverage', 'lcov.info' ); + +fs.emptyDirSync( path.join( coverageFile, '..' ) ); +fs.ensureFileSync( path.join( coverageFile ) ); + +// Merge separate reports into a single file that would be sent to Coveralls. +for ( const lcovPath of globSync( './packages/*/coverage/lcov.info' ) ) { + const relativePackagePath = path.join( lcovPath, '..', '..' ); + const content = fs.readFileSync( lcovPath, 'utf-8' ) + .replaceAll( /^(SF:)/gm, `$1${ relativePackagePath }/` ); + + fs.writeFileSync( coverageFile, content, { flag: 'as' } ); +} + +console.log( chalk.cyan( `Coverage status stored in "${ chalk.underline( coverageFile ) }".` ) ); diff --git a/scripts/ci/generate-circleci-configuration.js b/scripts/ci/generate-circleci-configuration.js new file mode 100644 index 000000000..05542cc7c --- /dev/null +++ b/scripts/ci/generate-circleci-configuration.js @@ -0,0 +1,136 @@ +#!/usr/bin/env node + +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* eslint-env node */ + +// The script assumes that it is executed from the CKEditor 5 Commercial directory and aims to load +// the template file (`.circleci/template.yml`) and store it under the `.circleci/config-tests.yml` path, +// a source for a new workflow triggered from the main thread when a new build starts. +// +// See: https://circleci.com/docs/using-dynamic-configuration/. + +import fs from 'fs/promises'; +import { fileURLToPath } from 'url'; +import upath from 'upath'; +import { glob } from 'glob'; +import yaml from 'js-yaml'; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = upath.dirname( __filename ); + +const ROOT_DIRECTORY = upath.join( __dirname, '..', '..' ); +const CIRCLECI_CONFIGURATION_DIRECTORY = upath.join( ROOT_DIRECTORY, '.circleci' ); + +( async () => { + const packages = await glob( '*/', { cwd: upath.join( ROOT_DIRECTORY, 'packages' ) } ); + + packages.sort(); + + /** + * @type CircleCIConfiguration + */ + const config = yaml.load( + await fs.readFile( upath.join( CIRCLECI_CONFIGURATION_DIRECTORY, 'template.yml' ) ) + ); + + config.jobs.validate_and_tests.steps.splice( 3, 0, ...generateTestSteps( packages ) ); + config.jobs.validate_and_tests.steps.splice( -1, 0, { + run: { + name: 'Combine coverage reports into a single file', + command: 'node scripts/ci/combine-coverage-lcov.js' + } + } ); + + await fs.writeFile( + upath.join( CIRCLECI_CONFIGURATION_DIRECTORY, 'config-tests.yml' ), + yaml.dump( config, { lineWidth: -1 } ) + ); +} )(); + +function generateTestSteps( packages ) { + return packages.map( packageName => { + return { + run: { + environment: { + TZ: 'Europe/Warsaw' + }, + // When a previous package failed, we still want to check the entire repository. + when: 'always', + name: `Execute tests for "${ packageName }"`, + cwd: upath.join( 'packages', packageName ), + command: 'yarn run coverage' + } + }; + } ); +} + +/** + * This type partially covers supported options on CircleCI. + * To see the complete guide, follow: https://circleci.com/docs/configuration-reference. + * + * @typedef {Object} CircleCIConfiguration + * + * @property {String} version + * + * @property {Object.} parameters + * + * @property {Object.} jobs + * + * @property {Object.} command + * + * @property {Object} workflows + * + * @property {Boolean} [setup] + */ + +/** + * @typedef {Object} CircleCIParameter + * + * @property {'string'|'boolean'|'integer'|'enum'} type + * + * @property {String|Number|Boolean} default + */ + +/** + * @typedef {Object} CircleCIJob + * + * @property {Boolean} machine + * + * @property {Array.} steps + * + * @property {Object.} [parameters] + */ + +/** + * @typedef {Object} CircleCICommand + * + * @property {String} description + * + * @property {Array.} steps + * + * @property {Object.} [parameters] + */ + +/** + * @typedef {Object} CircleCITask + * + * @property {Object} [persist_to_workspace] + * + * @property {String} [persist_to_workspace.root] + * + * @property {Array.} [persist_to_workspace.paths] + * + * @property {Object} [run] + * + * @property {String} [run.name] + * + * @property {String} [run.command] + * + * @property {String} [run.when] + * + * @property {String} [run.working_directory] + */ diff --git a/scripts/runtest.js b/scripts/runtest.js index 2b9f11970..cad1be884 100644 --- a/scripts/runtest.js +++ b/scripts/runtest.js @@ -7,21 +7,18 @@ /* eslint-env node */ -'use strict'; - -const path = require( 'path' ); -const { execSync } = require( 'child_process' ); -const fs = require( 'fs-extra' ); -const { globSync } = require( 'glob' ); -const minimist = require( 'minimist' ); -const chalk = require( 'chalk' ); +import path from 'path'; +import { execSync } from 'child_process'; +import fs from 'fs-extra'; +import { globSync } from 'glob'; +import minimist from 'minimist'; +import chalk from 'chalk'; main(); function main() { let hasError = false; const cwd = process.cwd(); - const coverageFile = path.join( cwd, 'coverage', 'lcov.info' ); const { coverage } = parseArguments( process.argv.slice( 2 ) ); const packages = globSync( './packages/*/package.json' ) @@ -49,19 +46,10 @@ function main() { } if ( coverage ) { - fs.emptyDirSync( path.join( coverageFile, '..' ) ); - fs.ensureFileSync( path.join( coverageFile ) ); - - // Merge separate reports into a single file that would be sent to Coveralls. - for ( const lcovPath of globSync( './packages/*/coverage/lcov.info' ) ) { - const relativePackagePath = path.join( lcovPath, '..', '..' ); - const content = fs.readFileSync( lcovPath, 'utf-8' ) - .replaceAll( /^(SF:)/gm, `$1${ relativePackagePath }/` ); - - fs.writeFileSync( coverageFile, content, { flag: 'as' } ); - } - - console.log( chalk.cyan( `\nCoverage status stored in "${ chalk.underline( coverageFile ) }".` ) ); + execSync( 'node scripts/ci/combine-coverage-lcov.js', { + cwd, + stdio: 'inherit' + } ); } if ( ignoredPackages.length ) { From 74b3c3cab32568c0885b706e83451509cd571f5f Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 16 Sep 2024 11:37:13 +0200 Subject: [PATCH 096/172] s/cwd/working_directory --- scripts/ci/generate-circleci-configuration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/generate-circleci-configuration.js b/scripts/ci/generate-circleci-configuration.js index 05542cc7c..08148acf2 100644 --- a/scripts/ci/generate-circleci-configuration.js +++ b/scripts/ci/generate-circleci-configuration.js @@ -61,7 +61,7 @@ function generateTestSteps( packages ) { // When a previous package failed, we still want to check the entire repository. when: 'always', name: `Execute tests for "${ packageName }"`, - cwd: upath.join( 'packages', packageName ), + working_directory: upath.join( 'packages', packageName ), command: 'yarn run coverage' } }; From 4de2f11be1108d6ca1e358f72dff67d5d3b8823a Mon Sep 17 00:00:00 2001 From: przemyslaw-zan <69513154+przemyslaw-zan@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:43:15 +0200 Subject: [PATCH 097/172] Review requests. --- .eslintrc.js | 3 +- .../lib/createpotfiles.js | 18 +- .../ckeditor5-dev-transifex/lib/data/index.js | 8 + .../lib/{ => data}/languagecodemap.json | 0 .../ckeditor5-dev-transifex/lib/download.js | 6 +- packages/ckeditor5-dev-transifex/lib/index.js | 5 +- .../lib/transifexservice.js | 3 +- .../ckeditor5-dev-transifex/lib/upload.js | 20 +- packages/ckeditor5-dev-transifex/lib/utils.js | 76 ++- packages/ckeditor5-dev-transifex/package.json | 1 - .../tests/createpotfiles.js | 445 ++++++++---------- .../ckeditor5-dev-transifex/tests/download.js | 229 +++------ .../tests/transifexservice.js | 36 +- .../ckeditor5-dev-transifex/tests/upload.js | 280 ++++------- .../ckeditor5-dev-transifex/tests/utils.js | 80 ++-- 15 files changed, 499 insertions(+), 711 deletions(-) create mode 100644 packages/ckeditor5-dev-transifex/lib/data/index.js rename packages/ckeditor5-dev-transifex/lib/{ => data}/languagecodemap.json (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 93bdad783..b56167fe1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,7 +17,8 @@ module.exports = { // ESLint does not understand `import ... with { ... }`. // See: https://github.com/eslint/eslint/discussions/15305. - 'packages/ckeditor5-dev-ci/lib/data/index.js' + 'packages/ckeditor5-dev-ci/lib/data/index.js', + 'packages/ckeditor5-dev-transifex/lib/data/index.js' ], rules: { 'no-console': 'off', diff --git a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js index 15d7dc89a..fdd009e61 100644 --- a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js @@ -10,8 +10,6 @@ import { logger as utilsLogger } from '@ckeditor/ckeditor5-dev-utils'; import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; import { verifyProperties } from './utils.js'; -let defaultLogger; -let langContextSuffix; const corePackageName = 'ckeditor5-core'; /** @@ -29,8 +27,8 @@ const corePackageName = 'ckeditor5-core'; * @param {Logger} [options.logger] A logger. */ export default function createPotFiles( options ) { - defaultLogger = utilsLogger(); - langContextSuffix = path.join( 'lang', 'contexts.json' ); + const defaultLogger = utilsLogger(); + const langContextSuffix = path.join( 'lang', 'contexts.json' ); verifyProperties( options, [ 'sourceFiles', 'packagePaths', 'corePackagePath', 'translationsDirectory' ] ); @@ -44,12 +42,12 @@ export default function createPotFiles( options ) { logger = defaultLogger } = options; - const packageContexts = getPackageContexts( packagePaths, corePackagePath ); + const packageContexts = getPackageContexts( packagePaths, corePackagePath, langContextSuffix ); const sourceMessages = collectSourceMessages( { sourceFiles, logger } ); const errors = [].concat( assertNoMissingContext( { packageContexts, sourceMessages } ), - assertAllContextUsed( { packageContexts, sourceMessages, ignoreUnusedCorePackageContexts, corePackagePath } ), + assertAllContextUsed( { packageContexts, sourceMessages, ignoreUnusedCorePackageContexts, corePackagePath, langContextSuffix } ), assertNoRepeatedContext( { packageContexts } ) ); @@ -94,14 +92,14 @@ export default function createPotFiles( options ) { * @param {Array.} packagePaths An array of paths to packages, which will be used to find message contexts. * @returns {Map.} */ -function getPackageContexts( packagePaths, corePackagePath ) { +function getPackageContexts( packagePaths, corePackagePath, langContextSuffix ) { // Add path to core package if not included in the package paths. if ( !packagePaths.includes( corePackagePath ) ) { packagePaths = [ ...packagePaths, corePackagePath ]; } const mapEntries = packagePaths - .filter( packagePath => containsContextFile( packagePath ) ) + .filter( packagePath => containsContextFile( packagePath, langContextSuffix ) ) .map( packagePath => { const pathToContext = path.join( packagePath, langContextSuffix ); const packageName = packagePath.split( /[\\/]/ ).pop(); @@ -170,7 +168,7 @@ function assertNoMissingContext( { packageContexts, sourceMessages } ) { * @returns {Array.} */ function assertAllContextUsed( options ) { - const { packageContexts, sourceMessages, ignoreUnusedCorePackageContexts, corePackagePath } = options; + const { packageContexts, sourceMessages, ignoreUnusedCorePackageContexts, corePackagePath, langContextSuffix } = options; const usedContextMap = new Map(); const errors = []; @@ -326,7 +324,7 @@ function createPotFileContent( messages ) { /** * @param {String} packageDirectory */ -function containsContextFile( packageDirectory ) { +function containsContextFile( packageDirectory, langContextSuffix ) { return fs.existsSync( path.join( packageDirectory, langContextSuffix ) ); } diff --git a/packages/ckeditor5-dev-transifex/lib/data/index.js b/packages/ckeditor5-dev-transifex/lib/data/index.js new file mode 100644 index 000000000..c00283406 --- /dev/null +++ b/packages/ckeditor5-dev-transifex/lib/data/index.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { default as _languageCodeMap } from './languagecodemap.json' with { type: 'json' }; + +export const languageCodeMap = _languageCodeMap; diff --git a/packages/ckeditor5-dev-transifex/lib/languagecodemap.json b/packages/ckeditor5-dev-transifex/lib/data/languagecodemap.json similarity index 100% rename from packages/ckeditor5-dev-transifex/lib/languagecodemap.json rename to packages/ckeditor5-dev-transifex/lib/data/languagecodemap.json diff --git a/packages/ckeditor5-dev-transifex/lib/download.js b/packages/ckeditor5-dev-transifex/lib/download.js index 7dae5d340..d6c54f5a8 100644 --- a/packages/ckeditor5-dev-transifex/lib/download.js +++ b/packages/ckeditor5-dev-transifex/lib/download.js @@ -10,9 +10,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; import { cleanPoFileContent, createDictionaryFromPoFileContent } from '@ckeditor/ckeditor5-dev-translations'; import transifexService from './transifexservice.js'; import { verifyProperties, createLogger } from './utils.js'; -import languageCodeMap from './languagecodemap.json'; - -let logger; +import { languageCodeMap } from './data/index.js'; /** * Downloads translations from the Transifex for each localizable package. It creates `*.po` files out of the translations and replaces old @@ -32,7 +30,7 @@ let logger; * @param {Boolean} [config.simplifyLicenseHeader=false] Whether to skip adding the contribute guide URL in the output `*.po` files. */ export default async function downloadTranslations( config ) { - logger = createLogger(); + const logger = createLogger(); verifyProperties( config, [ 'organizationName', 'projectName', 'token', 'packages', 'cwd' ] ); diff --git a/packages/ckeditor5-dev-transifex/lib/index.js b/packages/ckeditor5-dev-transifex/lib/index.js index dd0e43ef9..b912148a8 100644 --- a/packages/ckeditor5-dev-transifex/lib/index.js +++ b/packages/ckeditor5-dev-transifex/lib/index.js @@ -3,9 +3,12 @@ * For licensing, see LICENSE.md. */ +import * as _transifexUtils from './utils.js'; + +export const transifexUtils = _transifexUtils; + export { default as createPotFiles } from './createpotfiles.js'; export { default as uploadPotFiles } from './upload.js'; export { default as downloadTranslations } from './download.js'; export { default as getToken } from './gettoken.js'; export { default as transifexService } from './transifexservice.js'; -export { default as transifexUtils } from './utils.js'; diff --git a/packages/ckeditor5-dev-transifex/lib/transifexservice.js b/packages/ckeditor5-dev-transifex/lib/transifexservice.js index d0da35248..0450c3a8e 100644 --- a/packages/ckeditor5-dev-transifex/lib/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/lib/transifexservice.js @@ -3,7 +3,6 @@ * For licensing, see LICENSE.md. */ -import fetch from 'node-fetch'; import { transifexApi } from '@transifex/api'; const MAX_REQUEST_ATTEMPTS = 10; @@ -321,7 +320,7 @@ function createDownloadRequest( resource, language, numberOfAttempts = 1 ) { * attempt. There are three possible cases that are handled during downloading a file: * * (1) According to the Transifex API v3.0, when the requested file is ready for download, the Transifex service returns HTTP code 303, - * which is the redirection to the new location, where the file is available. By default, `node-fetch` follows redirections so the requested + * which is the redirection to the new location, where the file is available. By default, `fetch` follows redirections so the requested * file is downloaded automatically. * (2) If the requested file is not ready yet, but the response status from the Transifex service was successful and the number of retries * has not reached the limit yet, the request is queued and retried after the REQUEST_RETRY_TIMEOUT timeout. diff --git a/packages/ckeditor5-dev-transifex/lib/upload.js b/packages/ckeditor5-dev-transifex/lib/upload.js index 2f0f7dd20..40ddba76d 100644 --- a/packages/ckeditor5-dev-transifex/lib/upload.js +++ b/packages/ckeditor5-dev-transifex/lib/upload.js @@ -13,8 +13,6 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; const RESOURCE_REGEXP = /r:(?[a-z0-9_-]+)$/i; -let TRANSIFEX_RESOURCE_ERRORS; - /** * Uploads translations to the Transifex. * @@ -37,7 +35,7 @@ let TRANSIFEX_RESOURCE_ERRORS; * @returns {Promise} */ export default async function upload( config ) { - TRANSIFEX_RESOURCE_ERRORS = {}; + const TRANSIFEX_RESOURCE_ERRORS = {}; verifyProperties( config, [ 'token', 'organizationName', 'projectName', 'cwd', 'packages' ] ); @@ -106,11 +104,11 @@ export default async function upload( config ) { // For new packages, before uploading their translations, we need to create a dedicated resource. if ( isNew ) { await transifexService.createResource( { organizationName, projectName, resourceName } ) - .catch( errorHandlerFactory( resourceName, spinner ) ); + .catch( errorHandlerFactory( TRANSIFEX_RESOURCE_ERRORS, resourceName, spinner ) ); } // Abort if creating the resource ended with an error. - if ( hasError( resourceName ) ) { + if ( hasError( TRANSIFEX_RESOURCE_ERRORS, resourceName ) ) { continue; } @@ -121,7 +119,7 @@ export default async function upload( config ) { uploadIds.push( { resourceName, uuid } ); spinner.finish(); } ) - .catch( errorHandlerFactory( resourceName, spinner ) ); + .catch( errorHandlerFactory( TRANSIFEX_RESOURCE_ERRORS, resourceName, spinner ) ); } // An empty line for making a space between list of resources and the new process info. @@ -135,7 +133,7 @@ export default async function upload( config ) { const uploadDetails = uploadIds.map( async ( { resourceName, uuid } ) => { return transifexService.getResourceUploadDetails( uuid ) - .catch( errorHandlerFactory( resourceName ) ); + .catch( errorHandlerFactory( TRANSIFEX_RESOURCE_ERRORS, resourceName ) ); } ); const summary = ( await Promise.all( uploadDetails ) ) @@ -159,7 +157,7 @@ export default async function upload( config ) { logger.info( table.toString() ); } - if ( hasError() ) { + if ( hasError( TRANSIFEX_RESOURCE_ERRORS ) ) { // An empty line for making a space between steps and the warning message. logger.info( '' ); logger.warning( 'Not all translations were uploaded due to errors in Transifex API.' ); @@ -268,10 +266,11 @@ function formatTableRow() { * * If the `packageName` is not specified, returns `true` if any error occurs. * + * @param {Object} [TRANSIFEX_RESOURCE_ERRORS] * @param {String|null} [packageName=null] * @returns {Boolean} */ -function hasError( packageName = null ) { +function hasError( TRANSIFEX_RESOURCE_ERRORS, packageName = null ) { if ( !packageName ) { return Boolean( Object.keys( TRANSIFEX_RESOURCE_ERRORS ).length ); } @@ -282,11 +281,12 @@ function hasError( packageName = null ) { /** * Creates a callback that stores errors from Transifex for the given `packageName`. * + * @param {Object} [TRANSIFEX_RESOURCE_ERRORS] * @param {String} packageName * @param {CKEditor5Spinner|null} [spinner=null] * @returns {Function} */ -function errorHandlerFactory( packageName, spinner ) { +function errorHandlerFactory( TRANSIFEX_RESOURCE_ERRORS, packageName, spinner ) { return errorResponse => { if ( spinner ) { spinner.finish( { emoji: '❌' } ); diff --git a/packages/ckeditor5-dev-transifex/lib/utils.js b/packages/ckeditor5-dev-transifex/lib/utils.js index 8914fba3b..7f04fa5fd 100644 --- a/packages/ckeditor5-dev-transifex/lib/utils.js +++ b/packages/ckeditor5-dev-transifex/lib/utils.js @@ -6,46 +6,42 @@ import chalk from 'chalk'; import { logger as loggerFactory } from '@ckeditor/ckeditor5-dev-utils'; -const utils = { - /** - * Checks whether specified `properties` are specified in the `objectToCheck` object. - * - * Throws an error if any property is missing. - * - * @param {Object} objectToCheck - * @param {Array.} properties - */ - verifyProperties( objectToCheck, properties ) { - const nonExistingProperties = properties.filter( property => objectToCheck[ property ] === undefined ); - - if ( nonExistingProperties.length ) { - throw new Error( `The specified object misses the following properties: ${ nonExistingProperties.join( ', ' ) }.` ); - } - }, - - /** - * Creates logger instance. - * - * @returns {Object} logger - * @returns {Function} logger.progress - * @returns {Function} logger.info - * @returns {Function} logger.warning - * @returns {Function} logger.error - */ - createLogger() { - const logger = loggerFactory(); +/** + * Checks whether specified `properties` are specified in the `objectToCheck` object. + * + * Throws an error if any property is missing. + * + * @param {Object} objectToCheck + * @param {Array.} properties + */ +export function verifyProperties( objectToCheck, properties ) { + const nonExistingProperties = properties.filter( property => objectToCheck[ property ] === undefined ); - return { - progress( message ) { - if ( !message ) { - this.info( '' ); - } else { - this.info( '\n📍 ' + chalk.cyan( message ) ); - } - }, - ...logger - }; + if ( nonExistingProperties.length ) { + throw new Error( `The specified object misses the following properties: ${ nonExistingProperties.join( ', ' ) }.` ); } -}; +} + +/** + * Creates logger instance. + * + * @returns {Object} logger + * @returns {Function} logger.progress + * @returns {Function} logger.info + * @returns {Function} logger.warning + * @returns {Function} logger.error + */ +export function createLogger() { + const logger = loggerFactory(); -export default utils; + return { + progress( message ) { + if ( !message ) { + this.info( '' ); + } else { + this.info( '\n📍 ' + chalk.cyan( message ) ); + } + }, + ...logger + }; +} diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index 575a2a77d..859139347 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -29,7 +29,6 @@ "chalk": "^4.0.0", "inquirer": "^7.1.0", "@transifex/api": "^4.2.1", - "node-fetch": "^2.6.7", "cli-table": "^0.3.1" }, "devDependencies": { diff --git a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js index 8e6321695..9441de44c 100644 --- a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js @@ -6,63 +6,17 @@ import { describe, expect, it, vi, beforeEach } from 'vitest'; import createPotFiles from '../lib/createpotfiles.js'; -const { - pathJoinMock, - fsExistsSyncMock, - fsOutputFileSyncMock, - fsReadFileSyncMock, - delSyncMock, - findMessagesMock, - utilsVerifyPropertiesMock -} = vi.hoisted( () => { - return { - pathJoinMock: vi.fn(), - fsExistsSyncMock: vi.fn(), - fsOutputFileSyncMock: vi.fn(), - fsReadFileSyncMock: vi.fn(), - delSyncMock: vi.fn(), - findMessagesMock: vi.fn(), - utilsVerifyPropertiesMock: vi.fn() - }; -} ); - -vi.mock( 'path', () => { - return { - default: { - join: pathJoinMock - } - }; -} ); - -vi.mock( 'fs-extra', () => { - return { - default: { - existsSync: fsExistsSyncMock, - outputFileSync: fsOutputFileSyncMock, - readFileSync: fsReadFileSyncMock - } - }; -} ); - -vi.mock( 'del', () => { - return { - default: { - sync: delSyncMock - } - }; -} ); - -vi.mock( '@ckeditor/ckeditor5-dev-translations', () => { - return { - findMessages: findMessagesMock - }; -} ); - -vi.mock( '../lib/utils.js', () => { - return { - verifyProperties: utilsVerifyPropertiesMock - }; -} ); +import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; +import { verifyProperties } from '../lib/utils.js'; +import del from 'del'; +import fs from 'fs-extra'; +import path from 'path'; + +vi.mock( '../lib/utils.js' ); +vi.mock( '@ckeditor/ckeditor5-dev-translations' ); +vi.mock( 'del' ); +vi.mock( 'fs-extra' ); +vi.mock( 'path' ); describe( 'dev-transifex/createPotFiles()', () => { let loggerMocks; @@ -74,7 +28,8 @@ describe( 'dev-transifex/createPotFiles()', () => { error: vi.fn() }; - vi.mocked( pathJoinMock ).mockImplementation( ( ...args ) => args.join( '/' ) ); + vi.mocked( verifyProperties ).mockImplementation( vi.fn() ); + vi.mocked( path.join ).mockImplementation( ( ...args ) => args.join( '/' ) ); } ); it( 'should not create any POT file if no package is passed', () => { @@ -86,7 +41,7 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should delete the build directory before creating POT files', () => { @@ -98,18 +53,18 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( delSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( delSyncMock ) ).toHaveBeenCalledWith( '/cwd/build/.transifex' ); + expect( vi.mocked( del.sync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( del.sync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex' ); } ); it( 'should create a POT file entry for one message with a corresponding context', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -125,32 +80,32 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), expect.any( Function ) ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex/ckeditor5-foo/en.pot', [ `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, @@ -164,12 +119,12 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should warn if the message context is missing', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -185,21 +140,21 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), @@ -211,23 +166,23 @@ describe( 'dev-transifex/createPotFiles()', () => { 'Context for the message id is missing (\'foo_id\' from packages/ckeditor5-foo/src/foo.js).' ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 0 ); // Mark the process as failed in case of the error. expect( process.exitCode ).toEqual( 1 ); } ); it( 'should create a POT file entry for every defined package', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'bar_id': 'bar_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-bar/src/bar.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'bar_id': 'bar_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-bar/src/bar.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -235,7 +190,7 @@ describe( 'dev-transifex/createPotFiles()', () => { messages.forEach( message => onFoundMessage( message ) ); } ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'bar', id: 'bar_id' } ]; @@ -251,40 +206,40 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 3 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-bar/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 3, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 4 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 4 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-bar/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 3, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 4, 'packages/ckeditor5-bar/src/bar.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( findMessages ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), expect.any( Function ) ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-bar/src/bar.js_content', 'packages/ckeditor5-bar/src/bar.js', @@ -292,8 +247,8 @@ describe( 'dev-transifex/createPotFiles()', () => { expect.any( Function ) ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 1, '/cwd/build/.transifex/ckeditor5-foo/en.pot', [ @@ -305,7 +260,7 @@ describe( 'dev-transifex/createPotFiles()', () => { '' ].join( '\n' ) ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 2, '/cwd/build/.transifex/ckeditor5-bar/en.pot', [ @@ -320,14 +275,14 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should create one POT file entry from multiple files in the same package', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'bar_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/bar.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'bar_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/bar.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -335,7 +290,7 @@ describe( 'dev-transifex/createPotFiles()', () => { messages.forEach( message => onFoundMessage( message ) ); } ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'bar', id: 'bar_id' } ]; @@ -351,34 +306,34 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 3, 'packages/ckeditor5-foo/src/bar.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( findMessages ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), expect.any( Function ) ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/bar.js_content', 'packages/ckeditor5-foo/src/bar.js', @@ -386,8 +341,8 @@ describe( 'dev-transifex/createPotFiles()', () => { expect.any( Function ) ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex/ckeditor5-foo/en.pot', [ `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, @@ -405,13 +360,13 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should create a POT entry filled with plural forms for message that contains has defined plural forms', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id', plural: 'foo_plural' } ]; @@ -427,32 +382,32 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), expect.any( Function ) ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex/ckeditor5-foo/en.pot', [ `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, @@ -468,13 +423,13 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should load the core context file once and use its contexts', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -490,24 +445,24 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), @@ -516,8 +471,8 @@ describe( 'dev-transifex/createPotFiles()', () => { expect( loggerMocks.error ).toHaveBeenCalledTimes( 0 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex/ckeditor5-core/en.pot', [ `# Copyright (c) 2003-${ new Date().getFullYear() }, CKSource Holding sp. z o.o. All rights reserved.`, @@ -531,13 +486,13 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should not create a POT file for the context file if that was not added to the list of packages', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -553,24 +508,24 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), @@ -578,16 +533,16 @@ describe( 'dev-transifex/createPotFiles()', () => { ); expect( loggerMocks.error ).toHaveBeenCalledTimes( 0 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should log an error if the file contains a message that cannot be parsed', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage, onErrorFound ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage, onErrorFound ) => { const errors = [ 'parse_error' ]; @@ -603,21 +558,21 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), @@ -632,14 +587,14 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should log an error if two context files contain contexts the same id', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context1' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context2' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context1' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context2' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -655,27 +610,27 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 3, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), @@ -693,13 +648,13 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should log an error if a context is unused', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -715,24 +670,24 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), @@ -749,7 +704,7 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should fail with an error describing missing properties if the required were not passed to the function', () => { - vi.mocked( utilsVerifyPropertiesMock ).mockImplementationOnce( ( options, requiredProperties ) => { + vi.mocked( verifyProperties ).mockImplementationOnce( ( options, requiredProperties ) => { throw new Error( `The specified object misses the following properties: ${ requiredProperties.join( ', ' ) }.` ); } ); @@ -765,14 +720,14 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should not log an error if a context from the core package is unused when ignoreUnusedCorePackageContexts=true', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'custom_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context', 'bar_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'custom_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -789,27 +744,27 @@ describe( 'dev-transifex/createPotFiles()', () => { ignoreUnusedCorePackageContexts: true } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 3, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), @@ -826,13 +781,13 @@ describe( 'dev-transifex/createPotFiles()', () => { } ); it( 'should not add the license header in the created a POT file entry when skipLicenseHeader=true', () => { - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( true ); - vi.mocked( fsExistsSyncMock ).mockReturnValueOnce( false ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( true ); + vi.mocked( fs.existsSync ).mockReturnValueOnce( false ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); - vi.mocked( fsReadFileSyncMock ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( JSON.stringify( { 'foo_id': 'foo_context' } ) ); + vi.mocked( fs.readFileSync ).mockReturnValueOnce( 'packages/ckeditor5-foo/src/foo.js_content' ); - vi.mocked( findMessagesMock ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { + vi.mocked( findMessages ).mockImplementationOnce( ( fileContent, filePath, onFoundMessage ) => { const messages = [ { string: 'foo', id: 'foo_id' } ]; @@ -849,32 +804,32 @@ describe( 'dev-transifex/createPotFiles()', () => { skipLicenseHeader: true } ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json' ); - expect( vi.mocked( fsExistsSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.existsSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-core/lang/contexts.json' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 1, 'packages/ckeditor5-foo/lang/contexts.json', 'utf-8' ); - expect( vi.mocked( fsReadFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.readFileSync ) ).toHaveBeenNthCalledWith( 2, 'packages/ckeditor5-foo/src/foo.js', 'utf-8' ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( findMessagesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( findMessages ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( findMessages ) ).toHaveBeenCalledWith( 'packages/ckeditor5-foo/src/foo.js_content', 'packages/ckeditor5-foo/src/foo.js', expect.any( Function ), expect.any( Function ) ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex/ckeditor5-foo/en.pot', [ 'msgctxt "foo_context"', diff --git a/packages/ckeditor5-dev-transifex/tests/download.js b/packages/ckeditor5-dev-transifex/tests/download.js index 7fc0356b2..547950723 100644 --- a/packages/ckeditor5-dev-transifex/tests/download.js +++ b/packages/ckeditor5-dev-transifex/tests/download.js @@ -7,104 +7,28 @@ import path from 'path'; import { describe, expect, it, vi, beforeEach } from 'vitest'; import download from '../lib/download.js'; -const { - fsOutputFileSyncMock, - fsRemoveSyncMock, - fsExistsSyncMock, - fsReadJsonSyncMock, - fsWriteJsonSyncMock, - chalkGrayMock, - chalkUnderlineMock, - toolsCreateSpinnerMock, - cleanPoFileContentMock, - createDictionaryFromPoFileContentMock, - transifexInitMock, - transifexGetResourceNameMock, - transifexGetLanguageCodeMock, - transifexGetProjectDataMock, - transifexGetTranslationsMock, - utilsVerifyPropertiesMock, - utilsCreateLoggerMock -} = vi.hoisted( () => { +import { cleanPoFileContent, createDictionaryFromPoFileContent } from '@ckeditor/ckeditor5-dev-translations'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { verifyProperties, createLogger } from '../lib/utils.js'; +import fs from 'fs-extra'; +import transifexService from '../lib/transifexservice.js'; + +vi.mock( '../lib/transifexservice.js' ); +vi.mock( '../lib/utils.js' ); +vi.mock( '@ckeditor/ckeditor5-dev-translations' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'fs-extra' ); + +vi.mock( 'chalk', () => ( { + default: { + underline: vi.fn( string => string ), + gray: vi.fn( string => string ) + } +} ) ); + +vi.mock( '../lib/data/index.js', () => { return { - fsOutputFileSyncMock: vi.fn(), - fsRemoveSyncMock: vi.fn(), - fsExistsSyncMock: vi.fn(), - fsReadJsonSyncMock: vi.fn(), - fsWriteJsonSyncMock: vi.fn(), - chalkGrayMock: vi.fn(), - chalkUnderlineMock: vi.fn(), - toolsCreateSpinnerMock: vi.fn(), - cleanPoFileContentMock: vi.fn(), - createDictionaryFromPoFileContentMock: vi.fn(), - transifexInitMock: vi.fn(), - transifexGetResourceNameMock: vi.fn(), - transifexGetLanguageCodeMock: vi.fn(), - transifexGetProjectDataMock: vi.fn(), - transifexGetTranslationsMock: vi.fn(), - utilsVerifyPropertiesMock: vi.fn(), - utilsCreateLoggerMock: vi.fn() - }; -} ); - -vi.mock( 'fs-extra', () => { - return { - default: { - outputFileSync: fsOutputFileSyncMock, - removeSync: fsRemoveSyncMock, - existsSync: fsExistsSyncMock, - readJsonSync: fsReadJsonSyncMock, - writeJsonSync: fsWriteJsonSyncMock - } - }; -} ); - -vi.mock( 'chalk', () => { - return { - default: { - gray: chalkGrayMock, - underline: chalkUnderlineMock - } - }; -} ); - -vi.mock( '@ckeditor/ckeditor5-dev-utils', () => { - return { - tools: { - createSpinner: toolsCreateSpinnerMock - } - }; -} ); - -vi.mock( '@ckeditor/ckeditor5-dev-translations', () => { - return { - cleanPoFileContent: cleanPoFileContentMock, - createDictionaryFromPoFileContent: createDictionaryFromPoFileContentMock - }; -} ); - -vi.mock( '../lib/transifexservice.js', () => { - return { - default: { - init: transifexInitMock, - getResourceName: transifexGetResourceNameMock, - getLanguageCode: transifexGetLanguageCodeMock, - getProjectData: transifexGetProjectDataMock, - getTranslations: transifexGetTranslationsMock - } - }; -} ); - -vi.mock( '../lib/utils.js', () => { - return { - verifyProperties: utilsVerifyPropertiesMock, - createLogger: utilsCreateLoggerMock - }; -} ); - -vi.mock( '../lib/languagecodemap.json', () => { - return { - default: { + languageCodeMap: { ne_NP: 'ne' } }; @@ -122,7 +46,7 @@ describe( 'dev-transifex/download()', () => { loggerErrorMock = vi.fn(); loggerErrorMock = vi.fn(); - vi.mocked( utilsCreateLoggerMock ).mockImplementation( () => { + vi.mocked( createLogger ).mockImplementation( () => { return { progress: loggerProgressMock, info: loggerInfoMock, @@ -135,31 +59,20 @@ describe( 'dev-transifex/download()', () => { spinnerStartMock = vi.fn(); spinnerFinishMock = vi.fn(); - vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + vi.mocked( tools.createSpinner ).mockReturnValue( { start: spinnerStartMock, finish: spinnerFinishMock } ); - // existsSync: sinon.stub() - // .withArgs( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ) - // .callsFake( () => Boolean( mocks.oldFailedDownloads ) ), - - // readJsonSync: sinon.stub() - // .withArgs( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ) - // .callsFake( () => mocks.oldFailedDownloads ), - - vi.mocked( fsExistsSyncMock ).mockImplementation( () => Boolean( mocks.oldFailedDownloads ) ); - vi.mocked( fsReadJsonSyncMock ).mockImplementation( () => mocks.oldFailedDownloads ); - - vi.mocked( createDictionaryFromPoFileContentMock ).mockImplementation( fileContent => mocks.fileContents[ fileContent ] ); - vi.mocked( cleanPoFileContentMock ).mockImplementation( fileContent => fileContent ); + vi.mocked( fs.existsSync ).mockImplementation( () => Boolean( mocks.oldFailedDownloads ) ); + vi.mocked( fs.readJsonSync ).mockImplementation( () => mocks.oldFailedDownloads ); - vi.mocked( chalkUnderlineMock ).mockImplementation( string => string ); - vi.mocked( chalkGrayMock ).mockImplementation( string => string ); + vi.mocked( createDictionaryFromPoFileContent ).mockImplementation( fileContent => mocks.fileContents[ fileContent ] ); + vi.mocked( cleanPoFileContent ).mockImplementation( fileContent => fileContent ); - vi.mocked( transifexGetResourceNameMock ).mockImplementation( resource => resource.attributes.slug ); - vi.mocked( transifexGetLanguageCodeMock ).mockImplementation( language => language.attributes.code ); - vi.mocked( transifexGetProjectDataMock ).mockImplementation( ( organizationName, projectName, localizablePackageNames ) => { + vi.mocked( transifexService.getResourceName ).mockImplementation( resource => resource.attributes.slug ); + vi.mocked( transifexService.getLanguageCode ).mockImplementation( language => language.attributes.code ); + vi.mocked( transifexService.getProjectData ).mockImplementation( ( organizationName, projectName, localizablePackageNames ) => { const projectData = { resources: mocks.resources.filter( resource => localizablePackageNames.includes( resource.attributes.slug ) ), languages: mocks.languages @@ -167,7 +80,7 @@ describe( 'dev-transifex/download()', () => { return Promise.resolve( projectData ); } ); - vi.mocked( transifexGetTranslationsMock ).mockImplementation( ( resource, languages ) => { + vi.mocked( transifexService.getTranslations ).mockImplementation( ( resource, languages ) => { const translationData = { translations: new Map( languages.map( language => [ @@ -198,7 +111,7 @@ describe( 'dev-transifex/download()', () => { token: 'secretToken' }; - vi.mocked( utilsVerifyPropertiesMock ).mockImplementation( () => { + vi.mocked( verifyProperties ).mockImplementation( () => { throw new Error( error ); } ); @@ -210,8 +123,8 @@ describe( 'dev-transifex/download()', () => { caughtError => { expect( caughtError.message.endsWith( error.message ) ).toEqual( true ); - expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( verifyProperties ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( verifyProperties ) ).toHaveBeenCalledWith( config, [ 'organizationName', 'projectName', 'token', 'packages', 'cwd' ] ); } @@ -246,17 +159,24 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.removeSync ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.removeSync ) ).toHaveBeenNthCalledWith( 1, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations' ) ); - expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.removeSync ) ).toHaveBeenNthCalledWith( 2, path.normalize( '/workspace/.transifex-failed-downloads.json' ) ); + + const removeSyncMockFirstCallOrder = vi.mocked( fs.removeSync ).mock.invocationCallOrder[ 0 ]; + const removeSyncMockSecondCallOrder = vi.mocked( fs.removeSync ).mock.invocationCallOrder[ 1 ]; + const outputFileSyncMockFirstCallOrder = vi.mocked( fs.outputFileSync ).mock.invocationCallOrder[ 0 ]; + + expect( removeSyncMockFirstCallOrder < outputFileSyncMockFirstCallOrder ).toEqual( true ); + expect( outputFileSyncMockFirstCallOrder < removeSyncMockSecondCallOrder ).toEqual( true ); } ); it( 'should download translations for non-empty resources', async () => { @@ -298,21 +218,21 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 3 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 1, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/pl.po' ), 'ckeditor5-core-pl-content' ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 2, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/de.po' ), 'ckeditor5-core-de-content' ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 3, path.normalize( '/workspace/bar/ckeditor5-ui/lang/translations/pl.po' ), 'ckeditor5-ui-pl-content' @@ -376,9 +296,9 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledWith( path.normalize( '/workspace/bar/ckeditor5-ui/lang/translations/pl.po' ), 'ckeditor5-ui-pl-content' ); @@ -423,17 +343,17 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( tools.createSpinner ) ).toHaveBeenCalledTimes( 2 ); expect( vi.mocked( spinnerStartMock ) ).toHaveBeenCalledTimes( 2 ); expect( vi.mocked( spinnerFinishMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( tools.createSpinner ) ).toHaveBeenNthCalledWith( 1, 'Processing "ckeditor5-core"...', { indentLevel: 1, emoji: '👉' } ); - expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( tools.createSpinner ) ).toHaveBeenNthCalledWith( 2, 'Processing "ckeditor5-ui"...', { indentLevel: 1, emoji: '👉' } @@ -458,7 +378,7 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 0 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should save failed downloads', async () => { @@ -503,9 +423,9 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.writeJSONSync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.writeJSONSync ) ).toHaveBeenCalledWith( path.normalize( '/workspace/.transifex-failed-downloads.json' ), [ { resourceName: 'ckeditor5-ui', languages: [ { code: 'de', errorMessage: 'An example error.' } ] } ], { spaces: 2 } @@ -569,21 +489,21 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 3 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 1, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/pl.po' ), 'ckeditor5-core-pl-content' ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 2, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/en_AU.po' ), 'ckeditor5-core-en_AU-content' ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenNthCalledWith( 3, path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/ne.po' ), 'ckeditor5-core-ne_NP-content' @@ -593,7 +513,7 @@ describe( 'dev-transifex/download()', () => { it( 'should fail with an error when the transifex service responses with an error', async () => { const error = new Error( 'An example error.' ); - vi.mocked( transifexGetProjectDataMock ).mockRejectedValue( error ); + vi.mocked( transifexService.getProjectData ).mockRejectedValue( error ); try { await download( { @@ -610,7 +530,7 @@ describe( 'dev-transifex/download()', () => { expect( err ).to.equal( error ); } - expect( vi.mocked( transifexGetProjectDataMock ) ).toHaveBeenCalled(); + expect( vi.mocked( transifexService.getProjectData ) ).toHaveBeenCalled(); } ); it( 'should pass the "simplifyLicenseHeader" flag to the "cleanPoFileContent()" function when set to `true`', async () => { @@ -643,9 +563,9 @@ describe( 'dev-transifex/download()', () => { simplifyLicenseHeader: true } ); - expect( vi.mocked( cleanPoFileContentMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( cleanPoFileContent ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( cleanPoFileContentMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( cleanPoFileContent ) ).toHaveBeenCalledWith( 'ckeditor5-core-pl-content', { simplifyLicenseHeader: true @@ -685,12 +605,17 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.removeSync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsRemoveSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.removeSync ) ).toHaveBeenCalledWith( path.normalize( '/workspace/.transifex-failed-downloads.json' ) ); + + const outputFileSyncMockFirstCallOrder = vi.mocked( fs.outputFileSync ).mock.invocationCallOrder[ 0 ]; + const removeSyncMockFirstCallOrder = vi.mocked( fs.removeSync ).mock.invocationCallOrder[ 0 ]; + + expect( outputFileSyncMockFirstCallOrder < removeSyncMockFirstCallOrder ).toEqual( true ); } ); it( 'should download translations for existing resources but only for previously failed ones', async () => { @@ -736,9 +661,9 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsOutputFileSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.outputFileSync ) ).toHaveBeenCalledWith( path.normalize( '/workspace/foo/ckeditor5-core/lang/translations/pl.po' ), 'ckeditor5-core-pl-content' ); @@ -805,9 +730,9 @@ describe( 'dev-transifex/download()', () => { ] ) } ); - expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.writeJSONSync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsWriteJsonSyncMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.writeJSONSync ) ).toHaveBeenCalledWith( path.normalize( '/workspace/.transifex-failed-downloads.json' ), [ { resourceName: 'ckeditor5-core', languages: [ { code: 'de', errorMessage: 'An example error.' } ] } ], { spaces: 2 } diff --git a/packages/ckeditor5-dev-transifex/tests/transifexservice.js b/packages/ckeditor5-dev-transifex/tests/transifexservice.js index 30f3f4aff..5436c6182 100644 --- a/packages/ckeditor5-dev-transifex/tests/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/tests/transifexservice.js @@ -7,21 +7,13 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; import transifexService from '../lib/transifexservice.js'; const { - nodeFetchMock, transifexApiMock } = vi.hoisted( () => { return { - nodeFetchMock: vi.fn(), transifexApiMock: {} }; } ); -vi.mock( 'node-fetch', () => { - return { - default: nodeFetchMock - }; -} ); - vi.mock( '@transifex/api', () => { return { transifexApi: transifexApiMock @@ -31,6 +23,8 @@ vi.mock( '@transifex/api', () => { describe( 'dev-transifex/transifex-service', () => { let testData; + let fetchMock; + let createResourceMock; let createResourceStringsAsyncUploadMock; let dataResourceTranslationsMock; @@ -45,6 +39,10 @@ describe( 'dev-transifex/transifex-service', () => { let includeResourceTranslationsMock; beforeEach( () => { + fetchMock = vi.fn(); + + vi.stubGlobal( 'fetch', fetchMock ); + createResourceMock = vi.fn(); createResourceStringsAsyncUploadMock = vi.fn(); fetchResourceTranslationsMock = vi.fn(); @@ -240,7 +238,7 @@ describe( 'dev-transifex/transifex-service', () => { } ); it( 'should return requested translations if no retries are needed', async () => { - vi.mocked( nodeFetchMock ).mockImplementation( url => Promise.resolve( { + vi.mocked( fetchMock ).mockImplementation( url => Promise.resolve( { ok: true, redirected: true, text: () => Promise.resolve( testData.translations[ url ] ) @@ -287,21 +285,21 @@ describe( 'dev-transifex/transifex-service', () => { type: 'resource_translations_async_downloads' } ); - expect( nodeFetchMock ).toHaveBeenCalledTimes( 3 ); + expect( fetchMock ).toHaveBeenCalledTimes( 3 ); - expect( nodeFetchMock ).toHaveBeenNthCalledWith( 1, 'https://example.com/ckeditor5-core/en', { + expect( fetchMock ).toHaveBeenNthCalledWith( 1, 'https://example.com/ckeditor5-core/en', { headers: { Authorization: 'Bearer secretToken' } } ); - expect( nodeFetchMock ).toHaveBeenNthCalledWith( 2, 'https://example.com/ckeditor5-core/pl', { + expect( fetchMock ).toHaveBeenNthCalledWith( 2, 'https://example.com/ckeditor5-core/pl', { headers: { Authorization: 'Bearer secretToken' } } ); - expect( nodeFetchMock ).toHaveBeenNthCalledWith( 3, 'https://example.com/ckeditor5-core/de', { + expect( fetchMock ).toHaveBeenNthCalledWith( 3, 'https://example.com/ckeditor5-core/de', { headers: { Authorization: 'Bearer secretToken' } @@ -325,7 +323,7 @@ describe( 'dev-transifex/transifex-service', () => { de: 7 }; - nodeFetchMock.mockImplementation( url => { + fetchMock.mockImplementation( url => { const language = url.split( '/' ).pop(); if ( languageCallsBeforeResolving[ language ] > 0 ) { @@ -352,7 +350,7 @@ describe( 'dev-transifex/transifex-service', () => { const { translations, failedDownloads } = await translationsPromise; - expect( nodeFetchMock ).toHaveBeenCalledTimes( 23 ); + expect( fetchMock ).toHaveBeenCalledTimes( 23 ); expect( [ ...translations.entries() ] ).toEqual( [ [ 'en', 'ckeditor5-core-en-content' ], @@ -364,7 +362,7 @@ describe( 'dev-transifex/transifex-service', () => { } ); it( 'should return failed requests if all file downloads failed', async () => { - vi.mocked( nodeFetchMock ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { ok: false, status: 500, statusText: 'Internal Server Error' @@ -387,7 +385,7 @@ describe( 'dev-transifex/transifex-service', () => { it( 'should return failed requests if the retry limit has been reached for all requests', async () => { vi.useFakeTimers(); - vi.mocked( nodeFetchMock ).mockResolvedValue( { + vi.mocked( fetchMock ).mockResolvedValue( { ok: true, redirected: false } ); @@ -450,7 +448,7 @@ describe( 'dev-transifex/transifex-service', () => { de: 8 }; - nodeFetchMock.mockImplementation( url => { + fetchMock.mockImplementation( url => { const language = url.split( '/' ).pop(); if ( languageCallsBeforeResolving[ language ] > 0 ) { @@ -487,7 +485,7 @@ describe( 'dev-transifex/transifex-service', () => { const { translations, failedDownloads } = await translationsPromise; - expect( nodeFetchMock ).toHaveBeenCalledTimes( 14 ); + expect( fetchMock ).toHaveBeenCalledTimes( 14 ); expect( [ ...translations.entries() ] ).toEqual( [ [ 'pl', 'ckeditor5-core-pl-content' ] diff --git a/packages/ckeditor5-dev-transifex/tests/upload.js b/packages/ckeditor5-dev-transifex/tests/upload.js index 458c2d95f..b72bbdc8a 100644 --- a/packages/ckeditor5-dev-transifex/tests/upload.js +++ b/packages/ckeditor5-dev-transifex/tests/upload.js @@ -6,70 +6,39 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; import upload from '../lib/upload.js'; +import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import { verifyProperties, createLogger } from '../lib/utils.js'; +import chalk from 'chalk'; +import fs from 'fs/promises'; +import path from 'path'; +import transifexService from '../lib/transifexservice.js'; + const { - fsReadFileMock, - fsWriteFileMock, - fsUnlinkMock, - fsLstatMock, - pathJoinMock, tableConstructorSpy, tablePushMock, - tableToStringMock, - chalkGrayMock, - chalkCyanMock, - chalkItalicMock, - chalkUnderlineMock, - transifexServiceInitMock, - transifexServiceGetProjectDataMock, - transifexServiceCreateResourceMock, - transifexServiceCreateSourceFileMock, - transifexServiceGetResourceUploadDetailsMock, - utilsVerifyPropertiesMock, - utilsCreateLoggerMock, - toolsCreateSpinnerMock + tableToStringMock } = vi.hoisted( () => { return { - fsReadFileMock: vi.fn(), - fsWriteFileMock: vi.fn(), - fsUnlinkMock: vi.fn(), - fsLstatMock: vi.fn(), - pathJoinMock: vi.fn(), tableConstructorSpy: vi.fn(), tablePushMock: vi.fn(), - tableToStringMock: vi.fn(), - chalkGrayMock: vi.fn(), - chalkCyanMock: vi.fn(), - chalkItalicMock: vi.fn(), - chalkUnderlineMock: vi.fn(), - transifexServiceInitMock: vi.fn(), - transifexServiceGetProjectDataMock: vi.fn(), - transifexServiceCreateResourceMock: vi.fn(), - transifexServiceCreateSourceFileMock: vi.fn(), - transifexServiceGetResourceUploadDetailsMock: vi.fn(), - utilsVerifyPropertiesMock: vi.fn(), - utilsCreateLoggerMock: vi.fn(), - toolsCreateSpinnerMock: vi.fn() + tableToStringMock: vi.fn() }; } ); -vi.mock( 'fs/promises', () => { - return { - default: { - readFile: fsReadFileMock, - writeFile: fsWriteFileMock, - unlink: fsUnlinkMock, - lstat: fsLstatMock - } - }; -} ); +vi.mock( '../lib/transifexservice.js' ); +vi.mock( '../lib/utils.js' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'fs/promises' ); +vi.mock( 'path' ); -vi.mock( 'path', () => { - return { - default: { - join: pathJoinMock - } - }; -} ); +vi.mock( 'chalk', () => ( { + default: { + cyan: vi.fn( string => string ), + gray: vi.fn( string => string ), + italic: vi.fn( string => string ), + underline: vi.fn( string => string ) + } +} ) ); vi.mock( 'cli-table', () => { return { @@ -84,44 +53,6 @@ vi.mock( 'cli-table', () => { }; } ); -vi.mock( 'chalk', () => { - return { - default: { - gray: chalkGrayMock, - cyan: chalkCyanMock, - italic: chalkItalicMock, - underline: chalkUnderlineMock - } - }; -} ); - -vi.mock( '../lib/transifexservice.js', () => { - return { - default: { - init: transifexServiceInitMock, - getProjectData: transifexServiceGetProjectDataMock, - createResource: transifexServiceCreateResourceMock, - createSourceFile: transifexServiceCreateSourceFileMock, - getResourceUploadDetails: transifexServiceGetResourceUploadDetailsMock - } - }; -} ); - -vi.mock( '../lib/utils.js', () => { - return { - verifyProperties: utilsVerifyPropertiesMock, - createLogger: utilsCreateLoggerMock - }; -} ); - -vi.mock( '@ckeditor/ckeditor5-dev-utils', () => { - return { - tools: { - createSpinner: toolsCreateSpinnerMock - } - }; -} ); - vi.mock( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', () => ( { default: { 'ckeditor5-non-existing-01': [ @@ -137,12 +68,7 @@ describe( 'dev-transifex/upload()', () => { let loggerProgressMock, loggerInfoMock, loggerWarningMock, loggerErrorMock, loggerLogMock; beforeEach( () => { - vi.mocked( chalkGrayMock ).mockImplementation( string => string ); - vi.mocked( chalkCyanMock ).mockImplementation( string => string ); - vi.mocked( chalkItalicMock ).mockImplementation( string => string ); - vi.mocked( chalkUnderlineMock ).mockImplementation( string => string ); - - vi.mocked( pathJoinMock ).mockImplementation( ( ...args ) => args.join( '/' ) ); + vi.mocked( path.join ).mockImplementation( ( ...args ) => args.join( '/' ) ); loggerProgressMock = vi.fn(); loggerInfoMock = vi.fn(); @@ -150,9 +76,9 @@ describe( 'dev-transifex/upload()', () => { loggerErrorMock = vi.fn(); loggerErrorMock = vi.fn(); - vi.mocked( fsLstatMock ).mockRejectedValue(); + vi.mocked( fs.lstat ).mockRejectedValue(); - vi.mocked( utilsCreateLoggerMock ).mockImplementation( () => { + vi.mocked( createLogger ).mockImplementation( () => { return { progress: loggerProgressMock, info: loggerInfoMock, @@ -176,7 +102,7 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( utilsVerifyPropertiesMock ).mockImplementation( () => { + vi.mocked( verifyProperties ).mockImplementation( () => { throw new Error( error ); } ); @@ -188,8 +114,8 @@ describe( 'dev-transifex/upload()', () => { caughtError => { expect( caughtError.message.endsWith( error.message ) ).toEqual( true ); - expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( utilsVerifyPropertiesMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( verifyProperties ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( verifyProperties ) ).toHaveBeenCalledWith( config, [ 'token', 'organizationName', 'projectName', 'cwd', 'packages' ] ); } @@ -201,7 +127,7 @@ describe( 'dev-transifex/upload()', () => { [ 'ckeditor5-existing-11', 'build/.transifex/ckeditor5-existing-11' ] ] ); - vi.mocked( transifexServiceGetProjectDataMock ).mockRejectedValue( new Error( 'Invalid auth' ) ); + vi.mocked( transifexService.getProjectData ).mockRejectedValue( new Error( 'Invalid auth' ) ); const config = { packages, @@ -221,12 +147,12 @@ describe( 'dev-transifex/upload()', () => { 2, 'Make sure you specified a valid auth token or an organization/project names.' ); - expect( vi.mocked( transifexServiceGetProjectDataMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( transifexServiceGetProjectDataMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( transifexService.getProjectData ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexService.getProjectData ) ).toHaveBeenCalledWith( 'ckeditor', 'ckeditor5', [ ...packages.keys() ] ); - expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 0 ); + expect( vi.mocked( transifexService.createResource ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should create a new resource if the package is processed for the first time', async () => { @@ -242,22 +168,22 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { resources: [] } ); - vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); - vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-01' ); - vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( + vi.mocked( transifexService.getProjectData ).mockResolvedValue( { resources: [] } ); + vi.mocked( transifexService.createResource ).mockResolvedValue(); + vi.mocked( transifexService.createSourceFile ).mockResolvedValue( 'uuid-01' ); + vi.mocked( transifexService.getResourceUploadDetails ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 0, 0, 0 ) ); - vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + vi.mocked( tools.createSpinner ).mockReturnValue( { start: vi.fn(), finish: vi.fn() } ); await upload( config ); - expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledWith( { + expect( vi.mocked( transifexService.createResource ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexService.createResource ) ).toHaveBeenCalledWith( { organizationName: 'ckeditor', projectName: 'ckeditor5', resourceName: 'ckeditor5-non-existing-01' @@ -277,26 +203,26 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { + vi.mocked( transifexService.getProjectData ).mockResolvedValue( { resources: [ { attributes: { name: 'ckeditor5-existing-11' } } ] } ); - vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-11' ); + vi.mocked( transifexService.createSourceFile ).mockResolvedValue( 'uuid-11' ); - vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( + vi.mocked( transifexService.getResourceUploadDetails ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) ); - vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + vi.mocked( tools.createSpinner ).mockReturnValue( { start: vi.fn(), finish: vi.fn() } ); await upload( config ); - expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 0 ); + expect( vi.mocked( transifexService.createResource ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'should send a new translation source to Transifex', async () => { @@ -312,42 +238,42 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { + vi.mocked( transifexService.getProjectData ).mockResolvedValue( { resources: [ { attributes: { name: 'ckeditor5-existing-11' } } ] } ); - vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-11' ); + vi.mocked( transifexService.createSourceFile ).mockResolvedValue( 'uuid-11' ); - vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( + vi.mocked( transifexService.getResourceUploadDetails ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) ); - vi.mocked( fsReadFileMock ).mockResolvedValue( '# Example file.' ); + vi.mocked( fs.readFile ).mockResolvedValue( '# Example file.' ); - vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + vi.mocked( tools.createSpinner ).mockReturnValue( { start: vi.fn(), finish: vi.fn() } ); await upload( config ); - expect( vi.mocked( fsReadFileMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsReadFileMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.readFile ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.readFile ) ).toHaveBeenCalledWith( '/home/ckeditor5/build/.transifex/ckeditor5-existing-11/en.pot', 'utf-8' ); - expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledWith( { + expect( vi.mocked( transifexService.createSourceFile ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexService.createSourceFile ) ).toHaveBeenCalledWith( { organizationName: 'ckeditor', projectName: 'ckeditor5', resourceName: 'ckeditor5-existing-11', content: '# Example file.' } ); - expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledWith( 'uuid-11' ); + expect( vi.mocked( transifexService.getResourceUploadDetails ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexService.getResourceUploadDetails ) ).toHaveBeenCalledWith( 'uuid-11' ); } ); it( 'should keep informed a developer what the script does', async () => { @@ -363,13 +289,13 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { + vi.mocked( transifexService.getProjectData ).mockResolvedValue( { resources: [] } ); - vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); - vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-01' ); - vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockResolvedValue( + vi.mocked( transifexService.createResource ).mockResolvedValue(); + vi.mocked( transifexService.createSourceFile ).mockResolvedValue( 'uuid-01' ); + vi.mocked( transifexService.getResourceUploadDetails ).mockResolvedValue( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 0, 0, 0 ) ); @@ -382,8 +308,8 @@ describe( 'dev-transifex/upload()', () => { finish: vi.fn() }; - vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( packageSpinner ); - vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( processSpinner ); + vi.mocked( tools.createSpinner ).mockReturnValueOnce( packageSpinner ); + vi.mocked( tools.createSpinner ).mockReturnValueOnce( processSpinner ); vi.mocked( tableToStringMock ).mockReturnValue( 'â”ģ━â”ģ' ); @@ -398,11 +324,11 @@ describe( 'dev-transifex/upload()', () => { expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( 3 ); expect( vi.mocked( loggerProgressMock ) ).toHaveBeenNthCalledWith( 4, 'Done.' ); - expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( tools.createSpinner ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( tools.createSpinner ) ).toHaveBeenNthCalledWith( 1, 'Processing "ckeditor5-non-existing-01"', { emoji: '👉', indentLevel: 1 } ); - expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenNthCalledWith( + expect( vi.mocked( tools.createSpinner ) ).toHaveBeenNthCalledWith( 2, 'Collecting responses... It takes a while.' ); @@ -410,8 +336,8 @@ describe( 'dev-transifex/upload()', () => { expect( packageSpinner.finish ).toHaveBeenCalled(); expect( processSpinner.start ).toHaveBeenCalled(); expect( processSpinner.finish ).toHaveBeenCalled(); - expect( vi.mocked( chalkGrayMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( chalkItalicMock ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( chalk.gray ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( chalk.italic ) ).toHaveBeenCalledTimes( 1 ); } ); describe( 'error handling', () => { @@ -433,15 +359,15 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( fsLstatMock ).mockResolvedValueOnce(); + vi.mocked( fs.lstat ).mockResolvedValueOnce(); - vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { + vi.mocked( transifexService.getProjectData ).mockResolvedValue( { resources: [] } ); - vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); + vi.mocked( transifexService.createResource ).mockResolvedValue(); - vi.mocked( transifexServiceCreateSourceFileMock ).mockImplementation( options => { + vi.mocked( transifexService.createSourceFile ).mockImplementation( options => { if ( options.resourceName === 'ckeditor5-non-existing-01' ) { return Promise.resolve( 'uuid-01' ); } @@ -453,7 +379,7 @@ describe( 'dev-transifex/upload()', () => { return Promise.reject( { errors: [] } ); } ); - vi.mocked( fsReadFileMock ).mockImplementation( path => { + vi.mocked( fs.readFile ).mockImplementation( path => { if ( path === config.cwd + '/build/.transifex/ckeditor5-non-existing-01/en.pot' ) { return Promise.resolve( '# ckeditor5-non-existing-01' ); } @@ -465,7 +391,7 @@ describe( 'dev-transifex/upload()', () => { return Promise.resolve( '' ); } ); - vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockImplementation( id => { + vi.mocked( transifexService.getResourceUploadDetails ).mockImplementation( id => { if ( id === 'uuid-01' ) { return Promise.resolve( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-01', 3, 0, 0 ) @@ -481,7 +407,7 @@ describe( 'dev-transifex/upload()', () => { return Promise.reject(); } ); - vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + vi.mocked( tools.createSpinner ).mockReturnValue( { start: vi.fn(), finish: vi.fn() } ); @@ -498,17 +424,17 @@ describe( 'dev-transifex/upload()', () => { 2, 'The script will process only packages listed in the file instead of all passed as "config.packages".' ); - expect( vi.mocked( fsReadFileMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledTimes( 2 ); - expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs.readFile ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transifexService.createResource ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transifexService.createSourceFile ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transifexService.getResourceUploadDetails ) ).toHaveBeenCalledTimes( 2 ); } ); it( 'should remove the ".transifex-failed-uploads.json" file if finished with no errors', async () => { await upload( config ); - expect( vi.mocked( fsUnlinkMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsUnlinkMock ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ); + expect( vi.mocked( fs.unlink ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.unlink ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json' ); } ); it( 'should store an error in the ".transifex-failed-uploads.json" file (cannot create a resource)', async () => { @@ -517,7 +443,7 @@ describe( 'dev-transifex/upload()', () => { finish: vi.fn() }; - vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( firstSpinner ); + vi.mocked( tools.createSpinner ).mockReturnValueOnce( firstSpinner ); const error = { message: 'JsonApiError: 409', @@ -528,8 +454,8 @@ describe( 'dev-transifex/upload()', () => { ] }; - vi.mocked( transifexServiceCreateResourceMock ).mockRejectedValueOnce( error ); - vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValueOnce(); + vi.mocked( transifexService.createResource ).mockRejectedValueOnce( error ); + vi.mocked( transifexService.createResource ).mockResolvedValueOnce(); await upload( config ); @@ -547,8 +473,8 @@ describe( 'dev-transifex/upload()', () => { expect( firstSpinner.finish ).toHaveBeenCalledTimes( 1 ); expect( firstSpinner.finish ).toHaveBeenCalledWith( { emoji: '❌' } ); - expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.writeFile ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.writeFile ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', JSON.stringify( { 'ckeditor5-non-existing-01': [ 'Resource with this Slug and Project already exists.' ] @@ -563,7 +489,7 @@ describe( 'dev-transifex/upload()', () => { finish: vi.fn() }; - vi.mocked( toolsCreateSpinnerMock ).mockReturnValueOnce( firstSpinner ); + vi.mocked( tools.createSpinner ).mockReturnValueOnce( firstSpinner ); const error = { message: 'JsonApiError: 409', @@ -574,7 +500,7 @@ describe( 'dev-transifex/upload()', () => { ] }; - vi.mocked( transifexServiceCreateSourceFileMock ).mockRejectedValueOnce( error ); + vi.mocked( transifexService.createSourceFile ).mockRejectedValueOnce( error ); await upload( config ); @@ -589,8 +515,8 @@ describe( 'dev-transifex/upload()', () => { 5, 'Re-running the script will process only packages specified in the file.' ); - expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.writeFile ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.writeFile ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', JSON.stringify( { 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] @@ -612,7 +538,7 @@ describe( 'dev-transifex/upload()', () => { ] }; - vi.mocked( transifexServiceGetResourceUploadDetailsMock ).mockRejectedValueOnce( error ); + vi.mocked( transifexService.getResourceUploadDetails ).mockRejectedValueOnce( error ); await upload( config ); @@ -627,8 +553,8 @@ describe( 'dev-transifex/upload()', () => { 5, 'Re-running the script will process only packages specified in the file.' ); - expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( fsWriteFileMock ) ).toHaveBeenCalledWith( + expect( vi.mocked( fs.writeFile ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs.writeFile ) ).toHaveBeenCalledWith( '/home/ckeditor5-with-errors/.transifex-failed-uploads.json', JSON.stringify( { 'ckeditor5-non-existing-01': [ 'Object not found. It may have been deleted or not been created yet.' ] @@ -660,12 +586,12 @@ describe( 'dev-transifex/upload()', () => { projectName: 'ckeditor5' }; - vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-xx' ); + vi.mocked( transifexService.createSourceFile ).mockResolvedValue( 'uuid-xx' ); - vi.mocked( transifexServiceCreateSourceFileMock ).mockResolvedValue( 'uuid-xx' ); + vi.mocked( transifexService.createSourceFile ).mockResolvedValue( 'uuid-xx' ); // Mock resources on Transifex. - vi.mocked( transifexServiceGetProjectDataMock ).mockResolvedValue( { + vi.mocked( transifexService.getProjectData ).mockResolvedValue( { resources: [ { attributes: { name: 'ckeditor5-existing-11' } }, { attributes: { name: 'ckeditor5-existing-12' } }, @@ -674,9 +600,9 @@ describe( 'dev-transifex/upload()', () => { ] } ); - vi.mocked( transifexServiceCreateResourceMock ).mockResolvedValue(); + vi.mocked( transifexService.createResource ).mockResolvedValue(); - vi.mocked( transifexServiceGetResourceUploadDetailsMock ) + vi.mocked( transifexService.getResourceUploadDetails ) .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-11', 0, 0, 0 ) ) .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-14', 0, 0, 0 ) ) .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-03', 1, 0, 0 ) ) @@ -685,7 +611,7 @@ describe( 'dev-transifex/upload()', () => { .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-non-existing-02', 0, 0, 0 ) ) .mockResolvedValueOnce( createResourceUploadDetailsResponse( 'ckeditor5-existing-12', 0, 1, 1 ) ); - vi.mocked( toolsCreateSpinnerMock ).mockReturnValue( { + vi.mocked( tools.createSpinner ).mockReturnValue( { start: vi.fn(), finish: vi.fn() } ); @@ -694,11 +620,11 @@ describe( 'dev-transifex/upload()', () => { it( 'should handle all packages', async () => { await upload( config ); - expect( vi.mocked( transifexServiceGetProjectDataMock ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( transifexServiceCreateResourceMock ) ).toHaveBeenCalledTimes( 3 ); - expect( vi.mocked( transifexServiceCreateSourceFileMock ) ).toHaveBeenCalledTimes( 7 ); - expect( vi.mocked( transifexServiceGetResourceUploadDetailsMock ) ).toHaveBeenCalledTimes( 7 ); - expect( vi.mocked( toolsCreateSpinnerMock ) ).toHaveBeenCalledTimes( 8 ); + expect( vi.mocked( transifexService.getProjectData ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( transifexService.createResource ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( transifexService.createSourceFile ) ).toHaveBeenCalledTimes( 7 ); + expect( vi.mocked( transifexService.getResourceUploadDetails ) ).toHaveBeenCalledTimes( 7 ); + expect( vi.mocked( tools.createSpinner ) ).toHaveBeenCalledTimes( 8 ); } ); it( 'should display a summary table with sorted packages (new, has changes, A-Z)', async () => { @@ -717,7 +643,7 @@ describe( 'dev-transifex/upload()', () => { // 1x for printing "It takes a while", // 5x for each column, x2 for each resource. - expect( vi.mocked( chalkGrayMock ) ).toHaveBeenCalledTimes( 11 ); + expect( vi.mocked( chalk.gray ) ).toHaveBeenCalledTimes( 11 ); } ); it( 'should not display a summary table if none of the packages were processed', async () => { diff --git a/packages/ckeditor5-dev-transifex/tests/utils.js b/packages/ckeditor5-dev-transifex/tests/utils.js index b0a832461..582b02a72 100644 --- a/packages/ckeditor5-dev-transifex/tests/utils.js +++ b/packages/ckeditor5-dev-transifex/tests/utils.js @@ -4,34 +4,19 @@ */ import { describe, expect, it, vi, beforeEach } from 'vitest'; -// import utils from '../lib/utils.js'; - -const { - chalkCyanMock, - loggerMock -} = vi.hoisted( () => { - return { - chalkCyanMock: vi.fn(), - loggerMock: vi.fn() - }; -} ); +import { verifyProperties, createLogger } from '../lib/utils.js'; -vi.mock( 'chalk', () => { - return { - default: { - cyan: chalkCyanMock - } - }; -} ); +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import chalk from 'chalk'; -vi.mock( '@ckeditor/ckeditor5-dev-utils', () => { - return { - logger: loggerMock - }; -} ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'chalk', () => ( { + default: { + cyan: vi.fn( string => string ) + } +} ) ); describe( 'dev-transifex/utils', () => { - let utils; let loggerInfoMock, loggerWarningMock, loggerErrorMock, loggerLogMock; beforeEach( async () => { @@ -40,8 +25,7 @@ describe( 'dev-transifex/utils', () => { loggerErrorMock = vi.fn(); loggerLogMock = vi.fn(); - vi.mocked( chalkCyanMock ).mockImplementation( str => str ); - vi.mocked( loggerMock ).mockImplementation( () => { + vi.mocked( logger ).mockImplementation( () => { return { info: loggerInfoMock, warning: loggerWarningMock, @@ -49,92 +33,90 @@ describe( 'dev-transifex/utils', () => { _log: loggerLogMock }; } ); - - utils = ( await import( '../lib/utils.js' ) ).default; } ); describe( 'verifyProperties()', () => { it( 'should throw an error if the specified property is not specified in an object', () => { expect( () => { - utils.verifyProperties( {}, [ 'foo' ] ); + verifyProperties( {}, [ 'foo' ] ); } ).to.throw( Error, 'The specified object misses the following properties: foo.' ); } ); it( 'should throw an error if the value of the property is `undefined`', () => { expect( () => { - utils.verifyProperties( { foo: undefined }, [ 'foo' ] ); + verifyProperties( { foo: undefined }, [ 'foo' ] ); } ).to.throw( Error, 'The specified object misses the following properties: foo.' ); } ); it( 'should throw an error containing all The specified object misses the following properties', () => { expect( () => { - utils.verifyProperties( { foo: true, bar: 0 }, [ 'foo', 'bar', 'baz', 'xxx' ] ); + verifyProperties( { foo: true, bar: 0 }, [ 'foo', 'bar', 'baz', 'xxx' ] ); } ).to.throw( Error, 'The specified object misses the following properties: baz, xxx.' ); } ); it( 'should not throw an error if the value of the property is `null`', () => { expect( () => { - utils.verifyProperties( { foo: null }, [ 'foo' ] ); + verifyProperties( { foo: null }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is a boolean (`false`)', () => { expect( () => { - utils.verifyProperties( { foo: false }, [ 'foo' ] ); + verifyProperties( { foo: false }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is a boolean (`true`)', () => { expect( () => { - utils.verifyProperties( { foo: true }, [ 'foo' ] ); + verifyProperties( { foo: true }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is a number', () => { expect( () => { - utils.verifyProperties( { foo: 1 }, [ 'foo' ] ); + verifyProperties( { foo: 1 }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is a number (falsy value)', () => { expect( () => { - utils.verifyProperties( { foo: 0 }, [ 'foo' ] ); + verifyProperties( { foo: 0 }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is a NaN', () => { expect( () => { - utils.verifyProperties( { foo: NaN }, [ 'foo' ] ); + verifyProperties( { foo: NaN }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is a non-empty string', () => { expect( () => { - utils.verifyProperties( { foo: 'foo' }, [ 'foo' ] ); + verifyProperties( { foo: 'foo' }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is an empty string', () => { expect( () => { - utils.verifyProperties( { foo: '' }, [ 'foo' ] ); + verifyProperties( { foo: '' }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is an array', () => { expect( () => { - utils.verifyProperties( { foo: [] }, [ 'foo' ] ); + verifyProperties( { foo: [] }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is an object', () => { expect( () => { - utils.verifyProperties( { foo: {} }, [ 'foo' ] ); + verifyProperties( { foo: {} }, [ 'foo' ] ); } ).to.not.throw( Error ); } ); it( 'should not throw an error if the value of the property is a function', () => { expect( () => { - utils.verifyProperties( { + verifyProperties( { foo: () => {} }, [ 'foo' ] ); } ).to.not.throw( Error ); @@ -143,11 +125,11 @@ describe( 'dev-transifex/utils', () => { describe( 'createLogger()', () => { it( 'should be a function', () => { - expect( utils.createLogger ).toBeInstanceOf( Function ); + expect( createLogger ).toBeInstanceOf( Function ); } ); it( 'should return an object with methods', () => { - const logger = utils.createLogger(); + const logger = createLogger(); expect( logger ).toBeInstanceOf( Object ); expect( logger.progress ).toBeInstanceOf( Function ); @@ -158,24 +140,24 @@ describe( 'dev-transifex/utils', () => { } ); it( 'should call the info method for a non-empty progress message', () => { - const logger = utils.createLogger(); + const logger = createLogger(); logger.progress( 'Example step.' ); expect( loggerInfoMock ).toHaveBeenCalledTimes( 1 ); expect( loggerInfoMock ).toHaveBeenCalledWith( '\n📍 Example step.' ); - expect( chalkCyanMock ).toHaveBeenCalledTimes( 1 ); - expect( chalkCyanMock ).toHaveBeenCalledWith( 'Example step.' ); + expect( chalk.cyan ).toHaveBeenCalledTimes( 1 ); + expect( chalk.cyan ).toHaveBeenCalledWith( 'Example step.' ); } ); it( 'should call the info method with an empty message for an empty progress message', () => { - const logger = utils.createLogger(); + const logger = createLogger(); logger.progress(); expect( loggerInfoMock ).toHaveBeenCalledTimes( 1 ); expect( loggerInfoMock ).toHaveBeenCalledWith( '' ); - expect( chalkCyanMock ).toHaveBeenCalledTimes( 0 ); + expect( chalk.cyan ).toHaveBeenCalledTimes( 0 ); } ); } ); } ); From 6e451e2dac0b57578c9a64ed408dff98fc94f1e2 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 16 Sep 2024 11:51:55 +0200 Subject: [PATCH 098/172] Filter out packages without tests. --- scripts/ci/generate-circleci-configuration.js | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/ci/generate-circleci-configuration.js b/scripts/ci/generate-circleci-configuration.js index 08148acf2..f55f26f76 100644 --- a/scripts/ci/generate-circleci-configuration.js +++ b/scripts/ci/generate-circleci-configuration.js @@ -13,7 +13,7 @@ // // See: https://circleci.com/docs/using-dynamic-configuration/. -import fs from 'fs/promises'; +import fs from 'fs-extra'; import { fileURLToPath } from 'url'; import upath from 'upath'; import { glob } from 'glob'; @@ -27,8 +27,15 @@ const CIRCLECI_CONFIGURATION_DIRECTORY = upath.join( ROOT_DIRECTORY, '.circleci' ( async () => { const packages = await glob( '*/', { cwd: upath.join( ROOT_DIRECTORY, 'packages' ) } ); + const packagesWithTests = await asyncFilter( packages, async packageName => { + const pkgJson = await fs.readJson( + upath.join( ROOT_DIRECTORY, 'packages', packageName, 'package.json' ) + ); - packages.sort(); + return pkgJson?.scripts?.coverage; + } ); + + packagesWithTests.sort(); /** * @type CircleCIConfiguration @@ -37,7 +44,7 @@ const CIRCLECI_CONFIGURATION_DIRECTORY = upath.join( ROOT_DIRECTORY, '.circleci' await fs.readFile( upath.join( CIRCLECI_CONFIGURATION_DIRECTORY, 'template.yml' ) ) ); - config.jobs.validate_and_tests.steps.splice( 3, 0, ...generateTestSteps( packages ) ); + config.jobs.validate_and_tests.steps.splice( 3, 0, ...generateTestSteps( packagesWithTests ) ); config.jobs.validate_and_tests.steps.splice( -1, 0, { run: { name: 'Combine coverage reports into a single file', @@ -51,6 +58,15 @@ const CIRCLECI_CONFIGURATION_DIRECTORY = upath.join( ROOT_DIRECTORY, '.circleci' ); } )(); +async function asyncFilter( items, predicate ) { + return items.reduce( async ( results, item ) => { + return [ + ...await results, + ...await predicate( item ) ? [ item ] : [] + ]; + }, [] ); +} + function generateTestSteps( packages ) { return packages.map( packageName => { return { From 9563123e27b1b9ed323b44ab7a0c4cf072878b7f Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 16 Sep 2024 13:18:24 +0200 Subject: [PATCH 099/172] First tests. --- packages/ckeditor5-dev-tests/package.json | 1 + .../tests/_utils/testsetup.js | 9 + packages/ckeditor5-dev-tests/tests/index.js | 37 ++ .../tests/tasks/runautomatedtests.js | 506 +++++++++--------- packages/ckeditor5-dev-tests/vitest.config.js | 7 + 5 files changed, 310 insertions(+), 250 deletions(-) create mode 100644 packages/ckeditor5-dev-tests/tests/_utils/testsetup.js create mode 100644 packages/ckeditor5-dev-tests/tests/index.js diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 94f0c471d..9bc232159 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -75,6 +75,7 @@ "webpack": "^5.94.0" }, "devDependencies": { + "jest-extended": "^4.0.2", "vitest": "^2.0.5" }, "scripts": { diff --git a/packages/ckeditor5-dev-tests/tests/_utils/testsetup.js b/packages/ckeditor5-dev-tests/tests/_utils/testsetup.js new file mode 100644 index 000000000..52e540b96 --- /dev/null +++ b/packages/ckeditor5-dev-tests/tests/_utils/testsetup.js @@ -0,0 +1,9 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { expect } from 'vitest'; +import * as matchers from 'jest-extended'; + +expect.extend( matchers ); diff --git a/packages/ckeditor5-dev-tests/tests/index.js b/packages/ckeditor5-dev-tests/tests/index.js new file mode 100644 index 000000000..15b9b0fc6 --- /dev/null +++ b/packages/ckeditor5-dev-tests/tests/index.js @@ -0,0 +1,37 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { describe, expect, it, vi } from 'vitest'; +import * as index from '../lib/index.js'; +import runAutomatedTests from '../lib/tasks/runautomatedtests.js'; +import runManualTests from '../lib/tasks/runmanualtests.js'; +import parseArguments from '../lib/utils/automated-tests/parsearguments.js'; + +vi.mock( '../lib/tasks/runautomatedtests.js' ); +vi.mock( '../lib/tasks/runmanualtests.js' ); +vi.mock( '../lib/utils/automated-tests/parsearguments.js' ); + +describe( 'index.js', () => { + describe( 'runAutomatedTests()', () => { + it( 'should be a function', () => { + expect( index.runAutomatedTests ).to.be.a( 'function' ); + expect( index.runAutomatedTests ).toEqual( runAutomatedTests ); + } ); + } ); + + describe( 'runManualTests()', () => { + it( 'should be a function', () => { + expect( index.runManualTests ).to.be.a( 'function' ); + expect( index.runManualTests ).toEqual( runManualTests ); + } ); + } ); + + describe( 'parseArguments()', () => { + it( 'should be a function', () => { + expect( index.parseArguments ).to.be.a( 'function' ); + expect( index.parseArguments ).toEqual( parseArguments ); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js index 6ee2f09a9..b1eb0f5cb 100644 --- a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js +++ b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js @@ -3,95 +3,82 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); -const expect = require( 'chai' ).expect; -const chalk = require( 'chalk' ); -const path = require( 'path' ); - -describe( 'runAutomatedTests', () => { - let sandbox, stubs, runAutomatedTests, karmaServerCallback; +import path from 'path'; +import fs from 'fs'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { globSync } from 'glob'; +import mkdirp from 'mkdirp'; +import chalk from 'chalk'; +import karma from 'karma'; +import karmaLogger from 'karma/lib/logger.js'; +import transformFileOptionToTestGlob from '../../lib/utils/transformfileoptiontotestglob.js'; + +const stubs = vi.hoisted( () => ( { + log: { + log: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + info: vi.fn() + }, + karma: { + server: { + constructor: vi.fn(), + on: vi.fn(), + start: vi.fn() + } + } +} ) ); + +vi.mock( 'karma', () => ( { + default: { + Server: class KarmaServer { + constructor( ...args ) { + stubs.karma.server.constructor( ...args ); + } - beforeEach( () => { - karmaServerCallback = null; - sandbox = sinon.createSandbox(); + on( ...args ) { + return stubs.karma.server.on( ...args ); + } - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); + start( ...args ) { + return stubs.karma.server.start( ...args ); + } + }, + config: { + parseConfig: vi.fn() + } + } +} ) ); + +vi.mock( 'chalk', () => ( { + default: { + yellow: vi.fn() + } +} ) ); + +vi.mock( 'fs' ); +vi.mock( 'mkdirp' ); +vi.mock( 'glob' ); +vi.mock( 'karma/lib/logger.js' ); +vi.mock( '../../lib/utils/automated-tests/getkarmaconfig.js' ); +vi.mock( '../../lib/utils/transformfileoptiontotestglob.js' ); - stubs = { - fs: { - writeFileSync: sandbox.stub(), - utimesSync: sandbox.stub(), - readdirSync: sandbox.stub() - }, - log: { - info: sandbox.stub(), - warn: sandbox.stub(), - error: sandbox.stub() - }, - mkdirp: { - sync: sandbox.stub() - }, - glob: { - globSync: sandbox.stub() - }, - karma: { - Server: class KarmaServer { - constructor( config, callback ) { - karmaServerCallback = callback; - } - - on( ...args ) { - return stubs.karma.karmaServerOn( ...args ); - } - - start( ...args ) { - return stubs.karma.karmaServerOn( ...args ); - } - }, - karmaServerOn: sandbox.stub(), - karmaServerStart: sandbox.stub(), - config: { - parseConfig: sandbox.stub() - } - }, - getKarmaConfig: sandbox.stub(), - transformFileOptionToTestGlob: sandbox.stub() - }; +describe( 'runAutomatedTests', () => { + let runAutomatedTests; - sandbox.stub( process, 'cwd' ).returns( '/workspace' ); + beforeEach( async () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/workspace' ); - mockery.registerMock( 'mkdirp', stubs.mkdirp ); - mockery.registerMock( 'karma', stubs.karma ); - mockery.registerMock( 'karma/lib/logger.js', { - setupFromConfig: sandbox.spy(), - create( name ) { - expect( name ).to.equal( 'config' ); - return stubs.log; - } - } ); - mockery.registerMock( '../utils/automated-tests/getkarmaconfig', stubs.getKarmaConfig ); - mockery.registerMock( '../utils/transformfileoptiontotestglob', stubs.transformFileOptionToTestGlob ); + vi.mocked( karmaLogger ).create.mockImplementation( name => { + expect( name ).to.equal( 'config' ); - runAutomatedTests = proxyquire( '../../lib/tasks/runautomatedtests', { - fs: stubs.fs, - glob: stubs.glob + return stubs.log; } ); - } ); - afterEach( () => { - sandbox.restore(); - mockery.disable(); + runAutomatedTests = ( await import( '../../lib/tasks/runautomatedtests.js' ) ).default; } ); - it( 'should create an entry file before tests execution', done => { + it( 'should create an entry file before tests execution', async () => { const options = { files: [ 'basic-styles' @@ -99,56 +86,51 @@ describe( 'runAutomatedTests', () => { production: true }; - stubs.fs.readdirSync.returns( [] ); + vi.mocked( fs ).readdirSync.mockReturnValue( [] ); - stubs.transformFileOptionToTestGlob.returns( [ + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ '/workspace/packages/ckeditor5-basic-styles/tests/**/*.js', '/workspace/packages/ckeditor-basic-styles/tests/**/*.js' ] ); - stubs.glob.globSync.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', - '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' - ] ); - - stubs.glob.globSync.onSecondCall().returns( [] ); + vi.mocked( globSync ) + .mockReturnValue( [] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', + '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' + ] ); const expectedEntryPointContent = [ 'import "/workspace/packages/ckeditor5-basic-styles/tests/bold.js";', - 'import "/workspace/packages/ckeditor5-basic-styles/tests/italic.js";', - '' + 'import "/workspace/packages/ckeditor5-basic-styles/tests/italic.js";' ].join( '\n' ); + const promise = runAutomatedTests( options ); + setTimeout( () => { - karmaServerCallback( 0 ); - } ); + expect( stubs.karma.server.constructor ).toHaveBeenCalledOnce(); + + const [ firstCall ] = stubs.karma.server.constructor.mock.calls; + const [ , exitCallback ] = firstCall; - runAutomatedTests( options ) - .then( () => { - expect( stubs.mkdirp.sync.calledOnce ).to.equal( true ); - expect( stubs.mkdirp.sync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests' ); + exitCallback( 0 ); + } ); - expect( stubs.fs.writeFileSync.calledOnce ).to.equal( true ); - expect( stubs.fs.writeFileSync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests/entry-point.js' ); - expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.include( expectedEntryPointContent ); + await promise; - done(); - } ); + expect( vi.mocked( mkdirp ).sync ).toHaveBeenCalledExactlyOnceWith( '/workspace/build/.automated-tests' ); + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + '/workspace/build/.automated-tests/entry-point.js', + expect.stringContaining( expectedEntryPointContent ) + ); } ); - it( 'throws when files are not specified', () => { - return runAutomatedTests( { production: true } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - err => { - expect( err.message ).to.equal( 'Karma requires files to tests. `options.files` has to be non-empty array.' ); - } - ); + it( 'throws when files are not specified', async () => { + await expect( runAutomatedTests( { production: true } ) ) + .rejects.toThrow( 'Karma requires files to tests. `options.files` has to be non-empty array.' ); } ); - it( 'throws when specified files are invalid', () => { + it( 'throws when specified files are invalid', async () => { const options = { files: [ 'basic-foo', @@ -157,38 +139,29 @@ describe( 'runAutomatedTests', () => { production: true }; - stubs.fs.readdirSync.returns( [] ); + vi.mocked( fs ).readdirSync.mockReturnValue( [] ); - stubs.transformFileOptionToTestGlob.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-foo/tests/**/*.js', - '/workspace/packages/ckeditor-basic-foo/tests/**/*.js' - ] ); + vi.mocked( transformFileOptionToTestGlob ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-foo/tests/**/*.js', + '/workspace/packages/ckeditor-basic-foo/tests/**/*.js' + ] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-bar-core/tests/**/*.js', + '/workspace/packages/ckeditor-bar-core/tests/**/*.js' + ] ); - stubs.transformFileOptionToTestGlob.onSecondCall().returns( [ - '/workspace/packages/ckeditor5-bar-core/tests/**/*.js', - '/workspace/packages/ckeditor-bar-core/tests/**/*.js' - ] ); + vi.mocked( globSync ).mockReturnValue( [] ); + + await expect( runAutomatedTests( options ) ) + .rejects.toThrow( 'Not found files to tests. Specified patterns are invalid.' ); - stubs.glob.globSync.returns( [] ); - - return runAutomatedTests( options ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - err => { - expect( stubs.log.warn.calledTwice ).to.equal( true ); - expect( stubs.log.warn.firstCall.args[ 0 ] ).to.equal( 'Pattern "%s" does not match any file.' ); - expect( stubs.log.warn.firstCall.args[ 1 ] ).to.equal( 'basic-foo' ); - expect( stubs.log.warn.secondCall.args[ 0 ] ).to.equal( 'Pattern "%s" does not match any file.' ); - expect( stubs.log.warn.secondCall.args[ 1 ] ).to.equal( 'bar-core' ); - - expect( err.message ).to.equal( 'Not found files to tests. Specified patterns are invalid.' ); - } - ); + expect( stubs.log.warn ).toHaveBeenCalledTimes( 2 ); + expect( stubs.log.warn ).toHaveBeenCalledWith( 'Pattern "%s" does not match any file.', 'basic-foo' ); + expect( stubs.log.warn ).toHaveBeenCalledWith( 'Pattern "%s" does not match any file.', 'bar-core' ); } ); - it( 'throws when Karma config parser throws', () => { + it( 'throws when Karma config parser throws', async () => { const options = { files: [ 'basic-styles' @@ -196,34 +169,34 @@ describe( 'runAutomatedTests', () => { production: true }; - stubs.fs.readdirSync.returns( [] ); - - stubs.transformFileOptionToTestGlob.returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/**/*.js', - '/workspace/packages/ckeditor-basic-styles/tests/**/*.js' - ] ); - - stubs.glob.globSync.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', - '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' - ] ); - - stubs.glob.globSync.onSecondCall().returns( [] ); - - stubs.karma.config.parseConfig.throws( new Error( 'Example error from Karma config parser.' ) ); + vi.mocked( fs ).readdirSync.mockReturnValue( [] ); + + vi.mocked( transformFileOptionToTestGlob ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-foo/tests/**/*.js', + '/workspace/packages/ckeditor-basic-foo/tests/**/*.js' + ] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-bar-core/tests/**/*.js', + '/workspace/packages/ckeditor-bar-core/tests/**/*.js' + ] ); + + vi.mocked( globSync ) + .mockReturnValue( [] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', + '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' + ] ); + + vi.mocked( karma ).config.parseConfig.mockImplementation( () => { + throw new Error( 'Example error from Karma config parser.' ); + } ); - return runAutomatedTests( options ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - err => { - expect( err.message ).to.equal( 'Example error from Karma config parser.' ); - } - ); + await expect( runAutomatedTests( options ) ) + .rejects.toThrow( 'Example error from Karma config parser.' ); } ); - it( 'should warn when the `production` option is set to `false`', () => { + it( 'should warn when the `production` option is set to `false`', async () => { const options = { files: [ 'basic-styles' @@ -231,37 +204,45 @@ describe( 'runAutomatedTests', () => { production: false }; - const consoleWarnStub = sandbox.stub( console, 'warn' ); + const consoleWarnStub = vi.spyOn( console, 'warn' ).mockImplementation( () => { + } ); - stubs.fs.readdirSync.returns( [] ); + vi.mocked( chalk ).yellow.mockReturnValue( 'chalk.yellow: warn' ); + vi.mocked( fs ).readdirSync.mockReturnValue( [] ); - stubs.transformFileOptionToTestGlob.returns( [ + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ '/workspace/packages/ckeditor5-basic-styles/tests/**/*.js', '/workspace/packages/ckeditor-basic-styles/tests/**/*.js' ] ); - stubs.glob.globSync.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', - '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' - ] ); + vi.mocked( globSync ) + .mockReturnValue( [] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', + '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' + ] ); - stubs.glob.globSync.onSecondCall().returns( [] ); + const promise = runAutomatedTests( options ); setTimeout( () => { - karmaServerCallback( 0 ); + expect( stubs.karma.server.constructor ).toHaveBeenCalledOnce(); + + const [ firstCall ] = stubs.karma.server.constructor.mock.calls; + const [ , exitCallback ] = firstCall; + + exitCallback( 0 ); } ); - return runAutomatedTests( options ) - .then( () => { - expect( consoleWarnStub ).to.be.calledOnce; - expect( consoleWarnStub ).to.be.calledWith( - chalk.yellow( '⚠ You\'re running tests in dev mode - some error protections are loose. ' + - 'Use the `--production` flag to use strictest verification methods.' ) - ); - } ); + await promise; + + expect( consoleWarnStub ).toHaveBeenCalledExactlyOnceWith( 'chalk.yellow: warn' ); + expect( vi.mocked( chalk ).yellow ).toHaveBeenCalledExactlyOnceWith( + '⚠ You\'re running tests in dev mode - some error protections are loose. ' + + 'Use the `--production` flag to use strictest verification methods.' + ); } ); - it( 'should not add the code making console use throw an error when the `production` option is set to false', () => { + it( 'should not add the code making console use throw an error when the `production` option is set to false', async () => { const options = { files: [ 'basic-styles' @@ -269,33 +250,42 @@ describe( 'runAutomatedTests', () => { production: false }; - sandbox.stub( console, 'warn' ); - - stubs.fs.readdirSync.returns( [] ); + vi.spyOn( console, 'warn' ).mockImplementation( () => { + } ); + vi.mocked( fs ).readdirSync.mockReturnValue( [] ); - stubs.transformFileOptionToTestGlob.returns( [ + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ '/workspace/packages/ckeditor5-basic-styles/tests/**/*.js', '/workspace/packages/ckeditor-basic-styles/tests/**/*.js' ] ); - stubs.glob.globSync.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', - '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' - ] ); + vi.mocked( globSync ) + .mockReturnValue( [] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', + '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' + ] ); - stubs.glob.globSync.onSecondCall().returns( [] ); + const promise = runAutomatedTests( options ); setTimeout( () => { - karmaServerCallback( 0 ); + expect( stubs.karma.server.constructor ).toHaveBeenCalledOnce(); + + const [ firstCall ] = stubs.karma.server.constructor.mock.calls; + const [ , exitCallback ] = firstCall; + + exitCallback( 0 ); } ); - return runAutomatedTests( options ) - .then( () => { - expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.not.include( '// Make using any method from the console to fail.' ); - } ); + await promise; + + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + expect.any( String ), + expect.not.stringContaining( '// Make using any method from the console to fail.' ) + ); } ); - it( 'should add the code making console use throw an error when the `production` option is set to true', () => { + it( 'should add the code making console use throw an error when the `production` option is set to true', async () => { const options = { files: [ 'basic-styles' @@ -303,31 +293,42 @@ describe( 'runAutomatedTests', () => { production: true }; - stubs.fs.readdirSync.returns( [] ); + vi.spyOn( console, 'warn' ).mockImplementation( () => { + } ); + vi.mocked( fs ).readdirSync.mockReturnValue( [] ); - stubs.transformFileOptionToTestGlob.returns( [ + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ '/workspace/packages/ckeditor5-basic-styles/tests/**/*.js', '/workspace/packages/ckeditor-basic-styles/tests/**/*.js' ] ); - stubs.glob.globSync.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', - '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' - ] ); + vi.mocked( globSync ) + .mockReturnValue( [] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', + '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' + ] ); - stubs.glob.globSync.onSecondCall().returns( [] ); + const promise = runAutomatedTests( options ); setTimeout( () => { - karmaServerCallback( 0 ); + expect( stubs.karma.server.constructor ).toHaveBeenCalledOnce(); + + const [ firstCall ] = stubs.karma.server.constructor.mock.calls; + const [ , exitCallback ] = firstCall; + + exitCallback( 0 ); } ); - return runAutomatedTests( options ) - .then( () => { - expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.include( '// Make using any method from the console to fail.' ); - } ); + await promise; + + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + expect.any( String ), + expect.stringContaining( '// Make using any method from the console to fail.' ) + ); } ); - it( 'should load custom assertions automatically (camelCase)', done => { + it( 'should load custom assertions automatically (camelCase in paths)', async () => { const options = { files: [ 'basic-styles' @@ -335,19 +336,19 @@ describe( 'runAutomatedTests', () => { production: true }; - stubs.fs.readdirSync.returns( [ 'assertionA.js', 'assertionB.js' ] ); + vi.mocked( fs ).readdirSync.mockReturnValue( [ 'assertionA.js', 'assertionB.js' ] ); - stubs.transformFileOptionToTestGlob.returns( [ + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ '/workspace/packages/ckeditor5-basic-styles/tests/**/*.js', '/workspace/packages/ckeditor-basic-styles/tests/**/*.js' ] ); - stubs.glob.globSync.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', - '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' - ] ); - - stubs.glob.globSync.onSecondCall().returns( [] ); + vi.mocked( globSync ) + .mockReturnValue( [] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', + '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' + ] ); const assertionsDir = path.join( __dirname, '..', '..', 'lib', 'utils', 'automated-tests', 'assertions' ).replace( /\\/g, '/' ); @@ -355,28 +356,30 @@ describe( 'runAutomatedTests', () => { `import assertionAFactory from "${ assertionsDir }/assertionA.js";`, `import assertionBFactory from "${ assertionsDir }/assertionB.js";`, 'assertionAFactory( chai );', - 'assertionBFactory( chai );', - '' + 'assertionBFactory( chai );' ].join( '\n' ); + const promise = runAutomatedTests( options ); + setTimeout( () => { - karmaServerCallback( 0 ); - } ); + expect( stubs.karma.server.constructor ).toHaveBeenCalledOnce(); - runAutomatedTests( options ) - .then( () => { - expect( stubs.mkdirp.sync.calledOnce ).to.equal( true ); - expect( stubs.mkdirp.sync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests' ); + const [ firstCall ] = stubs.karma.server.constructor.mock.calls; + const [ , exitCallback ] = firstCall; - expect( stubs.fs.writeFileSync.calledOnce ).to.equal( true ); - expect( stubs.fs.writeFileSync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests/entry-point.js' ); - expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.include( expectedEntryPointContent ); + exitCallback( 0 ); + } ); - done(); - } ); + await promise; + + expect( vi.mocked( mkdirp ).sync ).toHaveBeenCalledExactlyOnceWith( '/workspace/build/.automated-tests' ); + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + '/workspace/build/.automated-tests/entry-point.js', + expect.stringContaining( expectedEntryPointContent ) + ); } ); - it( 'should load custom assertions automatically (kebab-case)', done => { + it( 'should load custom assertions automatically (kebab-case in paths)', async () => { const options = { files: [ 'basic-styles' @@ -384,19 +387,19 @@ describe( 'runAutomatedTests', () => { production: true }; - stubs.fs.readdirSync.returns( [ 'assertion-a.js', 'assertion-b.js' ] ); + vi.mocked( fs ).readdirSync.mockReturnValue( [ 'assertion-a.js', 'assertion-b.js' ] ); - stubs.transformFileOptionToTestGlob.returns( [ + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ '/workspace/packages/ckeditor5-basic-styles/tests/**/*.js', '/workspace/packages/ckeditor-basic-styles/tests/**/*.js' ] ); - stubs.glob.globSync.onFirstCall().returns( [ - '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', - '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' - ] ); - - stubs.glob.globSync.onSecondCall().returns( [] ); + vi.mocked( globSync ) + .mockReturnValue( [] ) + .mockReturnValueOnce( [ + '/workspace/packages/ckeditor5-basic-styles/tests/bold.js', + '/workspace/packages/ckeditor5-basic-styles/tests/italic.js' + ] ); const assertionsDir = path.join( __dirname, '..', '..', 'lib', 'utils', 'automated-tests', 'assertions' ).replace( /\\/g, '/' ); @@ -408,20 +411,23 @@ describe( 'runAutomatedTests', () => { '' ].join( '\n' ); + const promise = runAutomatedTests( options ); + setTimeout( () => { - karmaServerCallback( 0 ); - } ); + expect( stubs.karma.server.constructor ).toHaveBeenCalledOnce(); + + const [ firstCall ] = stubs.karma.server.constructor.mock.calls; + const [ , exitCallback ] = firstCall; - runAutomatedTests( options ) - .then( () => { - expect( stubs.mkdirp.sync.calledOnce ).to.equal( true ); - expect( stubs.mkdirp.sync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests' ); + exitCallback( 0 ); + } ); - expect( stubs.fs.writeFileSync.calledOnce ).to.equal( true ); - expect( stubs.fs.writeFileSync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests/entry-point.js' ); - expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.include( expectedEntryPointContent ); + await promise; - done(); - } ); + expect( vi.mocked( mkdirp ).sync ).toHaveBeenCalledExactlyOnceWith( '/workspace/build/.automated-tests' ); + expect( vi.mocked( fs ).writeFileSync ).toHaveBeenCalledExactlyOnceWith( + '/workspace/build/.automated-tests/entry-point.js', + expect.stringContaining( expectedEntryPointContent ) + ); } ); } ); diff --git a/packages/ckeditor5-dev-tests/vitest.config.js b/packages/ckeditor5-dev-tests/vitest.config.js index 5ad784a28..075897d63 100644 --- a/packages/ckeditor5-dev-tests/vitest.config.js +++ b/packages/ckeditor5-dev-tests/vitest.config.js @@ -7,11 +7,18 @@ import { defineConfig } from 'vitest/config'; export default defineConfig( { test: { + setupFiles: [ + './tests/_utils/testsetup.js' + ], testTimeout: 10000, + mockReset: true, restoreMocks: true, include: [ 'tests/**/*.js' ], + exclude: [ + './tests/_utils/**/*.js' + ], coverage: { provider: 'v8', include: [ From 2696d95382e1f44832916250175dfa92517c36c3 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 17 Sep 2024 15:12:28 +0200 Subject: [PATCH 100/172] Tests for "runManualTests()". --- .../lib/tasks/runmanualtests.js | 2 +- .../lib/utils/manual-tests/createserver.js | 2 +- .../tests/tasks/runautomatedtests.js | 2 +- .../tests/tasks/runmanualtests.js | 1571 ++++++----------- 4 files changed, 573 insertions(+), 1004 deletions(-) diff --git a/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js b/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js index 58931749d..bcb315fc1 100644 --- a/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js +++ b/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js @@ -5,9 +5,9 @@ import fs from 'fs'; import path from 'path'; +import { spawn } from 'child_process'; import chalk from 'chalk'; import { globSync } from 'glob'; -import { spawn } from 'child_process'; import inquirer from 'inquirer'; import isInteractive from 'is-interactive'; import { Server as SocketServer } from 'socket.io'; diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js index cb2d08a3d..bafe7dfc4 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js @@ -27,7 +27,7 @@ export default function createManualTestServer( sourcePath, port = 8125, onCreat onRequest( sourcePath, request, response ); } ).listen( port ); - // SIGINT isn't caught on Windows in process. However CTRL+C can be catch + // SIGINT isn't caught on Windows in process. However, `CTRL+C` can be caught // by `readline` module. After that we can emit SIGINT to the process manually. if ( process.platform === 'win32' ) { const readline = require( 'readline' ).createInterface( { diff --git a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js index b1eb0f5cb..61099ca15 100644 --- a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js +++ b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js @@ -63,7 +63,7 @@ vi.mock( 'karma/lib/logger.js' ); vi.mock( '../../lib/utils/automated-tests/getkarmaconfig.js' ); vi.mock( '../../lib/utils/transformfileoptiontotestglob.js' ); -describe( 'runAutomatedTests', () => { +describe( 'runAutomatedTests()', () => { let runAutomatedTests; beforeEach( async () => { diff --git a/packages/ckeditor5-dev-tests/tests/tasks/runmanualtests.js b/packages/ckeditor5-dev-tests/tests/tasks/runmanualtests.js index f59ca777d..ab8791403 100644 --- a/packages/ckeditor5-dev-tests/tests/tasks/runmanualtests.js +++ b/packages/ckeditor5-dev-tests/tests/tasks/runmanualtests.js @@ -3,235 +3,211 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import fs from 'fs'; +import path from 'path'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { Server } from 'socket.io'; +import { spawn } from 'child_process'; +import { globSync } from 'glob'; +import chalk from 'chalk'; +import inquirer from 'inquirer'; +import isInteractive from 'is-interactive'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import requireDll from '../../lib/utils/requiredll.js'; +import createManualTestServer from '../../lib/utils/manual-tests/createserver.js'; +import compileManualTestScripts from '../../lib/utils/manual-tests/compilescripts.js'; +import compileManualTestHtmlFiles from '../../lib/utils/manual-tests/compilehtmlfiles.js'; +import transformFileOptionToTestGlob from '../../lib/utils/transformfileoptiontotestglob.js'; +import removeDir from '../../lib/utils/manual-tests/removedir.js'; +import runManualTests from '../../lib/tasks/runmanualtests.js'; + +const stubs = vi.hoisted( () => ( { + log: { + log: vi.fn(), + error: vi.fn(), + warning: vi.fn(), + info: vi.fn() + }, + spawn: { + spawnReturnValue: { + on: ( event, callback ) => { + if ( !stubs.spawn.spawnEvents[ event ] ) { + stubs.spawn.spawnEvents[ event ] = []; + } -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); -const expect = require( 'chai' ).expect; + stubs.spawn.spawnEvents[ event ].push( callback ); + + // Return the same object containing the `on()` method to allow method chaining: `.on( ... ).on( ... )`. + return stubs.spawn.spawnReturnValue; + } + }, + spawnExitCode: 0, + spawnEvents: {}, + spawnTriggerEvent: ( event, data ) => { + if ( stubs.spawn.spawnEvents[ event ] ) { + for ( const callback of stubs.spawn.spawnEvents[ event ] ) { + callback( data ); + } -describe( 'runManualTests', () => { - let sandbox, spies, runManualTests, defaultOptions; + delete stubs.spawn.spawnEvents[ event ]; + } + } + } +} ) ); + +vi.mock( 'socket.io' ); +vi.mock( 'child_process' ); +vi.mock( 'inquirer' ); +vi.mock( 'glob' ); +vi.mock( 'chalk', () => ( { + default: { + bold: vi.fn( input => input ) + } +} ) ); +vi.mock( 'path' ); +vi.mock( 'fs' ); +vi.mock( 'is-interactive' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( '../../lib/utils/manual-tests/createserver.js' ); +vi.mock( '../../lib/utils/manual-tests/compilehtmlfiles.js' ); +vi.mock( '../../lib/utils/manual-tests/compilescripts.js' ); +vi.mock( '../../lib/utils/manual-tests/removedir.js' ); +vi.mock( '../../lib/utils/manual-tests/copyassets.js' ); +vi.mock( '../../lib/utils/transformfileoptiontotestglob.js' ); +vi.mock( '../../lib/utils/requiredll.js' ); + +describe( 'runManualTests()', () => { + let defaultOptions; beforeEach( () => { - sandbox = sinon.createSandbox(); + stubs.spawn.spawnExitCode = 0; + + vi.mocked( spawn ).mockImplementation( () => { + // Simulate closing a new process. It does not matter that this simulation ends the child process immediately. + // All that matters is that the `close` event is emitted with specified exit code. + process.nextTick( () => { + stubs.spawn.spawnTriggerEvent( 'close', stubs.spawn.spawnExitCode ); + } ); - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false + return stubs.spawn.spawnReturnValue; } ); - spies = { - socketIO: { - Server: sandbox.stub().returns( new ( class {} )() ) - }, - childProcess: { - spawn: sandbox.stub().callsFake( () => { - // Simulate closing a new process. It does not matter that this simulation ends the child process immediately. - // All that matters is that the `close` event is emitted with specified exit code. - process.nextTick( () => { - spies.childProcess.spawnTriggerEvent( 'close', spies.childProcess.spawnExitCode ); - } ); + vi.mocked( globSync ).mockImplementation( pattern => { + const patterns = { + // Valid pattern for manual tests. + 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js': [ + 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', + 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js' + ], + // Another valid pattern for manual tests. + 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js': [ + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ], + // Invalid pattern for manual tests (points to `/_utils/` subdirectory). + 'workspace/packages/ckeditor-*/tests/**/manual/_utils/**/*.js': [ + 'workspace/packages/ckeditor-foo/tests/manual/_utils/feature-e.js', + 'workspace/packages/ckeditor-bar/tests/manual/_utils/feature-f.js' + ], + // Invalid pattern for manual tests (points outside manual test directory). + 'workspace/packages/ckeditor-*/tests/**/outside/**/*.js': [ + 'workspace/packages/ckeditor-foo/tests/outside/feature-g.js', + 'workspace/packages/ckeditor-bar/tests/outside/feature-h.js' + ], + // Valid pattern for manual tests that require DLLs. + 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js': [ + 'workspace/packages/ckeditor5-foo/tests/manual/dll/feature-i-dll.js', + 'workspace/packages/ckeditor5-bar/tests/manual/dll/feature-j-dll.js' + ], + // Pattern for finding `package.json` in all repositories. + // External repositories are first, then the root repository. + '{,external/*/}package.json': [ + 'workspace/ckeditor5/external/ckeditor5-internal/package.json', + 'workspace/ckeditor5/external/collaboration-features/package.json', + 'workspace/ckeditor5/package.json' + ] + }; - return spies.childProcess.spawnReturnValue; - } ), - spawnReturnValue: { - on: ( event, callback ) => { - if ( !spies.childProcess.spawnEvents[ event ] ) { - spies.childProcess.spawnEvents[ event ] = []; - } + const separator = process.platform === 'win32' ? '\\' : '/'; + const result = patterns[ pattern ] || []; - spies.childProcess.spawnEvents[ event ].push( callback ); + return result.map( p => p.split( '/' ).join( separator ) ); + } ); - // Return the same object containing the `on()` method to allow method chaining: `.on( ... ).on( ... )`. - return spies.childProcess.spawnReturnValue; - } - }, - spawnExitCode: 0, - spawnEvents: {}, - spawnTriggerEvent: ( event, data ) => { - if ( spies.childProcess.spawnEvents[ event ] ) { - for ( const callback of spies.childProcess.spawnEvents[ event ] ) { - callback( data ); - } + vi.mocked( path ).join.mockImplementation( ( ...chunks ) => chunks.join( '/' ) ); + vi.mocked( path ).resolve.mockImplementation( path => '/absolute/path/to/' + path ); + vi.mocked( path ).basename.mockImplementation( path => path.split( /[\\/]/ ).pop() ); + vi.mocked( path ).dirname.mockImplementation( path => { + const chunks = path.split( /[\\/]/ ); - delete spies.childProcess.spawnEvents[ event ]; - } - } - }, - inquirer: { - prompt: sandbox.stub() - }, - glob: { - globSync: sandbox.stub().callsFake( pattern => { - const patterns = { - // Valid pattern for manual tests. - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js': [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js' - ], - // Another valid pattern for manual tests. - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js': [ - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - // Invalid pattern for manual tests (points to `/_utils/` subdirectory). - 'workspace/packages/ckeditor-*/tests/**/manual/_utils/**/*.js': [ - 'workspace/packages/ckeditor-foo/tests/manual/_utils/feature-e.js', - 'workspace/packages/ckeditor-bar/tests/manual/_utils/feature-f.js' - ], - // Invalid pattern for manual tests (points outside manual test directory). - 'workspace/packages/ckeditor-*/tests/**/outside/**/*.js': [ - 'workspace/packages/ckeditor-foo/tests/outside/feature-g.js', - 'workspace/packages/ckeditor-bar/tests/outside/feature-h.js' - ], - // Valid pattern for manual tests that require DLLs. - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js': [ - 'workspace/packages/ckeditor5-foo/tests/manual/dll/feature-i-dll.js', - 'workspace/packages/ckeditor5-bar/tests/manual/dll/feature-j-dll.js' - ], - // Pattern for finding `package.json` in all repositories. - // External repositories are first, then the root repository. - '{,external/*/}package.json': [ - 'workspace/ckeditor5/external/ckeditor5-internal/package.json', - 'workspace/ckeditor5/external/collaboration-features/package.json', - 'workspace/ckeditor5/package.json' - ] - }; - - const separator = process.platform === 'win32' ? '\\' : '/'; - const result = patterns[ pattern ] || []; - - return result.map( p => p.split( '/' ).join( separator ) ); - } ) - }, - chalk: { - bold: sandbox.stub().callsFake( msg => msg ) - }, - fs: { - readFileSync: sandbox.stub() - }, - path: { - join: sandbox.stub().callsFake( ( ...chunks ) => chunks.join( '/' ) ), - resolve: sandbox.stub().callsFake( path => '/absolute/path/to/' + path ), - basename: sandbox.stub().callsFake( path => path.split( /[\\/]/ ).pop() ), - dirname: sandbox.stub().callsFake( path => { - const chunks = path.split( /[\\/]/ ); - - chunks.pop(); - - return chunks.join( '/' ); - } ) - }, - devUtils: { - logger: sandbox.stub().callsFake( () => ( { - warning: spies.devUtils.logWarning, - info: spies.devUtils.logInfo - } ) ), - logWarning: sandbox.stub(), - logInfo: sandbox.stub() - }, - isInteractive: sandbox.stub(), - requireDll: sandbox.stub().callsFake( sourceFiles => { - return sourceFiles.some( filePath => /-dll.[jt]s$/.test( filePath ) ); - } ), - server: sandbox.stub(), - htmlFileCompiler: sandbox.spy( () => Promise.resolve() ), - scriptCompiler: sandbox.spy( () => Promise.resolve() ), - removeDir: sandbox.spy( () => Promise.resolve() ), - copyAssets: sandbox.spy(), - transformFileOptionToTestGlob: sandbox.stub().returns( [] ) - }; + chunks.pop(); + + return chunks.join( '/' ); + } ); - mockery.registerMock( 'socket.io', spies.socketIO ); - mockery.registerMock( 'child_process', spies.childProcess ); - mockery.registerMock( 'inquirer', spies.inquirer ); - mockery.registerMock( 'glob', spies.glob ); - mockery.registerMock( 'chalk', spies.chalk ); - mockery.registerMock( 'path', spies.path ); - mockery.registerMock( 'fs', spies.fs ); - mockery.registerMock( 'is-interactive', spies.isInteractive ); - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', spies.devUtils ); - mockery.registerMock( '../utils/manual-tests/createserver', spies.server ); - mockery.registerMock( '../utils/manual-tests/compilehtmlfiles', spies.htmlFileCompiler ); - mockery.registerMock( '../utils/manual-tests/compilescripts', spies.scriptCompiler ); - mockery.registerMock( '../utils/manual-tests/removedir', spies.removeDir ); - mockery.registerMock( '../utils/manual-tests/copyassets', spies.copyAssets ); - mockery.registerMock( '../utils/transformfileoptiontotestglob', spies.transformFileOptionToTestGlob ); - mockery.registerMock( '../utils/requiredll', spies.requireDll ); - - sandbox.stub( process, 'cwd' ).returns( 'workspace' ); + vi.mocked( logger ).mockImplementation( () => stubs.log ); + vi.mocked( requireDll ).mockImplementation( sourceFiles => { + return sourceFiles.some( filePath => /-dll.[jt]s$/.test( filePath ) ); + } ); + + vi.mocked( compileManualTestScripts ).mockResolvedValue(); + vi.mocked( compileManualTestHtmlFiles ).mockResolvedValue(); + vi.mocked( removeDir ).mockResolvedValue(); + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [] ); + + vi.spyOn( process, 'cwd' ).mockReturnValue( 'workspace' ); // The `glob` util returns paths in format depending on the platform. - sandbox.stub( process, 'platform' ).value( 'linux' ); + vi.spyOn( process, 'platform', 'get' ).mockReturnValue( 'linux' ); defaultOptions = { dll: null }; - - runManualTests = require( '../../lib/tasks/runmanualtests' ); } ); - afterEach( () => { - sandbox.restore(); - mockery.disable(); - } ); - - it( 'should run all manual tests and return promise', () => { - spies.transformFileOptionToTestGlob.returns( [ + it( 'should run all manual tests and return promise', async () => { + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); - return runManualTests( defaultOptions ) - .then( () => { - expect( spies.removeDir.calledOnce ).to.equal( true ); - expect( spies.removeDir.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - - expect( spies.htmlFileCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.htmlFileCompiler.firstCall, { - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - language: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - disableWatch: true, - silent: false - } ); - - expect( spies.scriptCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: null, - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - debug: undefined, - disableWatch: true, - identityFile: undefined - } ); - - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - } ); + await runManualTests( defaultOptions ); + + expect( vi.mocked( removeDir ) ).toHaveBeenCalledExactlyOnceWith( 'workspace/build/.manual-tests', expect.any( Object ) ); + expect( vi.mocked( createManualTestServer ) ).toHaveBeenCalledExactlyOnceWith( + 'workspace/build/.manual-tests', + undefined, + expect.any( Function ) + ); + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + buildDir: 'workspace/build/.manual-tests', + sourceFiles: [ + 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', + 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ], + onTestCompilationStatus: expect.any( Function ), + silent: false + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + cwd: 'workspace', + buildDir: 'workspace/build/.manual-tests', + sourceFiles: [ + 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', + 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ], + themePath: null, + onTestCompilationStatus: expect.any( Function ) + } ) ); } ); - it( 'runs specified manual tests', () => { - spies.transformFileOptionToTestGlob.onFirstCall().returns( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ); - spies.transformFileOptionToTestGlob.onSecondCall().returns( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); + it( 'runs specified manual tests', async () => { + vi.mocked( transformFileOptionToTestGlob ) + .mockReturnValueOnce( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ) + .mockReturnValueOnce( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); const options = { files: [ @@ -242,62 +218,31 @@ describe( 'runManualTests', () => { debug: [ 'CK_DEBUG' ] }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.removeDir.calledOnce ).to.equal( true ); - expect( spies.removeDir.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - - expect( spies.transformFileOptionToTestGlob.calledTwice ).to.equal( true ); - expect( spies.transformFileOptionToTestGlob.firstCall.args[ 0 ] ).to.equal( 'ckeditor5-classic' ); - expect( spies.transformFileOptionToTestGlob.firstCall.args[ 1 ] ).to.equal( true ); - expect( spies.transformFileOptionToTestGlob.secondCall.args[ 0 ] ).to.equal( 'ckeditor-classic/manual/classic.js' ); - expect( spies.transformFileOptionToTestGlob.secondCall.args[ 1 ] ).to.equal( true ); - - expect( spies.htmlFileCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.htmlFileCompiler.firstCall, { - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - language: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - disableWatch: false, - silent: false - } ); - - expect( spies.scriptCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: 'path/to/theme', - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - debug: [ 'CK_DEBUG' ], - disableWatch: false, - identityFile: undefined - } ); - - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); + + expect( vi.mocked( transformFileOptionToTestGlob ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transformFileOptionToTestGlob ) ).toHaveBeenCalledWith( 'ckeditor5-classic', true ); + expect( vi.mocked( transformFileOptionToTestGlob ) ).toHaveBeenCalledWith( 'ckeditor-classic/manual/classic.js', true ); + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + sourceFiles: [ + 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', + 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ] + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + sourceFiles: [ + 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', + 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ], + debug: [ 'CK_DEBUG' ] + } ) ); } ); - it( 'allows specifying language and additionalLanguages (to CKEditorTranslationsPlugin)', () => { - spies.transformFileOptionToTestGlob.onFirstCall().returns( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ); - spies.transformFileOptionToTestGlob.onSecondCall().returns( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); - + it( 'allows specifying language and additionalLanguages (to `CKEditorTranslationsPlugin`)', async () => { const options = { files: [ 'ckeditor5-classic', @@ -312,55 +257,17 @@ describe( 'runManualTests', () => { debug: [ 'CK_DEBUG' ] }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.removeDir.calledOnce ).to.equal( true ); - expect( spies.removeDir.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - - expect( spies.htmlFileCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.htmlFileCompiler.firstCall, { - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - language: 'pl', - onTestCompilationStatus: sinon.match.func, - additionalLanguages: [ 'ar', 'en' ], - disableWatch: false, - silent: false - } ); - - expect( spies.scriptCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: 'path/to/theme', - language: 'pl', - onTestCompilationStatus: sinon.match.func, - additionalLanguages: [ 'ar', 'en' ], - debug: [ 'CK_DEBUG' ], - disableWatch: false, - tsconfig: undefined, - identityFile: undefined - } ); + await runManualTests( { ...defaultOptions, ...options } ); - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - } ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + additionalLanguages: [ 'ar', 'en' ] + } ) ); } ); - it( 'allows specifying port', () => { - spies.transformFileOptionToTestGlob.onFirstCall().returns( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ); - spies.transformFileOptionToTestGlob.onSecondCall().returns( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); + it( 'allows specifying port', async () => { + vi.mocked( transformFileOptionToTestGlob ) + .mockReturnValueOnce( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ) + .mockReturnValueOnce( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); const options = { files: [ @@ -370,18 +277,16 @@ describe( 'runManualTests', () => { port: 8888 }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - expect( spies.server.firstCall.args[ 1 ] ).to.equal( 8888 ); - } ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); - it( 'allows specifying identity file (absolute path)', () => { - spies.transformFileOptionToTestGlob.onFirstCall().returns( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ); - spies.transformFileOptionToTestGlob.onSecondCall().returns( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); + expect( vi.mocked( createManualTestServer ) ).toHaveBeenCalledExactlyOnceWith( + expect.any( String ), + 8888, + expect.any( Function ) + ); + } ); + it( 'allows specifying identity file (an absolute path)', async () => { const options = { files: [ 'ckeditor5-classic', @@ -390,36 +295,14 @@ describe( 'runManualTests', () => { identityFile: '/absolute/path/to/secrets.js' }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: null, - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - debug: undefined, - additionalLanguages: undefined, - disableWatch: false, - identityFile: '/absolute/path/to/secrets.js' - } ); - } ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); - it( 'allows specifying identity file (relative path)', () => { - spies.transformFileOptionToTestGlob.onFirstCall().returns( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ); - spies.transformFileOptionToTestGlob.onSecondCall().returns( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + identityFile: '/absolute/path/to/secrets.js' + } ) ); + } ); + it( 'allows specifying identity file (a relative path)', async () => { const options = { files: [ 'ckeditor5-classic', @@ -428,34 +311,15 @@ describe( 'runManualTests', () => { identityFile: 'path/to/secrets.js' }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: null, - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - debug: undefined, - additionalLanguages: undefined, - disableWatch: false, - identityFile: 'path/to/secrets.js' - } ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); + + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + identityFile: 'path/to/secrets.js' + } ) ); } ); - it( 'should allow hiding processed files in the console', () => { - spies.transformFileOptionToTestGlob.returns( [ + it( 'should allow hiding processed files in the console', async () => { + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); @@ -464,55 +328,18 @@ describe( 'runManualTests', () => { silent: true }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.removeDir.calledOnce ).to.equal( true ); - expect( spies.removeDir.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - expect( spies.removeDir.firstCall.args[ 1 ] ).to.deep.equal( { silent: true } ); - - expect( spies.htmlFileCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.htmlFileCompiler.firstCall, { - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - language: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - disableWatch: true, - silent: true - } ); + await runManualTests( { ...defaultOptions, ...options } ); - expect( spies.scriptCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: null, - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - debug: undefined, - disableWatch: true, - identityFile: undefined - } ); - - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - } ); + expect( vi.mocked( removeDir ) ).toHaveBeenCalledExactlyOnceWith( expect.any( String ), expect.objectContaining( { + silent: true + } ) ); + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + silent: true + } ) ); } ); - it( 'should allow disabling listening for changes in source files', () => { - spies.transformFileOptionToTestGlob.returns( [ + it( 'should allow disabling listening for changes in source files', async () => { + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); @@ -521,51 +348,18 @@ describe( 'runManualTests', () => { disableWatch: true }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.htmlFileCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.htmlFileCompiler.firstCall, { - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - language: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - disableWatch: true, - silent: false - } ); + await runManualTests( { ...defaultOptions, ...options } ); - expect( spies.scriptCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: null, - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - debug: undefined, - disableWatch: true, - identityFile: undefined - } ); - - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - } ); + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: true + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: true + } ) ); } ); - it( 'compiles only manual test files (ignores utils and files outside the manual directory)', () => { - spies.transformFileOptionToTestGlob.returns( [ + it( 'compiles only manual test files (ignores utils and files outside the manual directory)', async () => { + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor-*/tests/**/manual/_utils/**/*.js', 'workspace/packages/ckeditor-*/tests/**/outside/**/*.js' @@ -575,70 +369,49 @@ describe( 'runManualTests', () => { disableWatch: true }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.htmlFileCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.htmlFileCompiler.firstCall, { - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - language: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - disableWatch: true, - silent: false - } ); - - expect( spies.scriptCompiler.calledOnce ).to.equal( true ); - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: null, - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - debug: undefined, - disableWatch: true, - identityFile: undefined - } ); + await runManualTests( { ...defaultOptions, ...options } ); - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - } ); + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + sourceFiles: [ + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ] + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + sourceFiles: [ + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ] + } ) ); } ); - it( 'should start a socket.io server as soon as the http server is up and running', () => { - spies.transformFileOptionToTestGlob.returns( [ + it( 'should start a socket.io server as soon as the http server is up and running', async () => { + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); - const httpServerMock = sinon.spy(); + const httpServerMock = vi.fn(); - spies.server.callsFake( ( buildDire, port, onCreate ) => onCreate( httpServerMock ) ); + vi.mocked( createManualTestServer ).mockImplementation( ( buildDire, port, onCreate ) => onCreate( httpServerMock ) ); - return runManualTests( defaultOptions ) - .then( () => { - sinon.assert.calledOnce( spies.socketIO.Server ); - sinon.assert.calledWithExactly( spies.socketIO.Server, httpServerMock ); - } ); + await runManualTests( defaultOptions ); + + expect( vi.mocked( Server ) ).toHaveBeenCalledExactlyOnceWith( httpServerMock ); } ); - it( 'should set disableWatch to true if files flag is not provided', () => { - return runManualTests( defaultOptions ) - .then( () => { - sinon.assert.calledWith( spies.scriptCompiler.firstCall, sinon.match.has( 'disableWatch', true ) ); - } ); + it( 'should set disableWatch to true if files flag is not provided', async () => { + await runManualTests( defaultOptions ); + + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: true + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: true + } ) ); } ); - it( 'should set disableWatch to false if files flag is provided', () => { + it( 'should set disableWatch to false if files flag is provided', async () => { const options = { files: [ 'ckeditor5-classic', @@ -646,13 +419,17 @@ describe( 'runManualTests', () => { ] }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - sinon.assert.calledWith( spies.scriptCompiler.firstCall, sinon.match.has( 'disableWatch', false ) ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); + + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: false + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: false + } ) ); } ); - it( 'should read disableWatch flag value even if files flag is provided', () => { + it( 'should read disableWatch flag value even if files flag is provided', async () => { const options = { files: [ 'ckeditor5-classic', @@ -661,563 +438,374 @@ describe( 'runManualTests', () => { disableWatch: true }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - sinon.assert.calledWith( spies.scriptCompiler.firstCall, sinon.match.has( 'disableWatch', true ) ); - } ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); - it( 'should set default values for files', () => { - return runManualTests( defaultOptions ) - .then( () => { - expect( spies.transformFileOptionToTestGlob.calledTwice ).to.equal( true ); - expect( spies.transformFileOptionToTestGlob.firstCall.args[ 0 ] ).to.equal( '*' ); - expect( spies.transformFileOptionToTestGlob.secondCall.args[ 0 ] ).to.equal( 'ckeditor5' ); - } ); + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: true + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: true + } ) ); } ); - it( 'should transform provided files to glob', () => { - const options = { - files: [ - 'ckeditor5-classic', - 'ckeditor-classic/manual/classic.js' - ] - }; + it( 'should find all CKEditor 5 manual tests if the `files` option is not defined', async () => { + await runManualTests( defaultOptions ); - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.transformFileOptionToTestGlob.calledTwice ).to.equal( true ); - expect( spies.transformFileOptionToTestGlob.firstCall.args[ 0 ] ).to.equal( 'ckeditor5-classic' ); - expect( spies.transformFileOptionToTestGlob.secondCall.args[ 0 ] ).to.equal( 'ckeditor-classic/manual/classic.js' ); - } ); + expect( vi.mocked( transformFileOptionToTestGlob ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( transformFileOptionToTestGlob ) ).toHaveBeenCalledWith( '*', true ); + expect( vi.mocked( transformFileOptionToTestGlob ) ).toHaveBeenCalledWith( 'ckeditor5', true ); } ); - it( 'should not duplicate glob files in the final sourceFiles array', () => { - spies.transformFileOptionToTestGlob.returns( [ + it( 'should not duplicate glob files in the final sourceFiles array', async () => { + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); - return runManualTests( defaultOptions ) - .then( () => { - expect( spies.scriptCompiler.firstCall.args[ 0 ].sourceFiles ).to.deep.equal( [ - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ] ); - } ); + await runManualTests( defaultOptions ); + + expect( vi.mocked( transformFileOptionToTestGlob ) ).toHaveBeenCalledTimes( 2 ); + + expect( vi.mocked( compileManualTestHtmlFiles ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + sourceFiles: [ + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ] + } ) ); + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + sourceFiles: [ + 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', + 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' + ] + } ) ); } ); describe( 'DLLs', () => { - it( 'should not build the DLLs if there are no DLL-related manual tests', () => { - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' - ] ); + beforeEach( () => { + vi.mocked( isInteractive ).mockReturnValue( true ); - return runManualTests( defaultOptions ) - .then( () => { - sinon.assert.notCalled( spies.childProcess.spawn ); - sinon.assert.notCalled( spies.devUtils.logInfo ); - sinon.assert.notCalled( spies.devUtils.logWarning ); - sinon.assert.notCalled( spies.inquirer.prompt ); - sinon.assert.notCalled( spies.path.resolve ); - sinon.assert.notCalled( spies.fs.readFileSync ); - } ); - } ); - - it( 'should not build the DLLs if the console is not interactive', () => { - spies.isInteractive.returns( false ); - spies.transformFileOptionToTestGlob.returns( [ + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' ] ); - - return runManualTests( defaultOptions ) - .then( () => { - sinon.assert.notCalled( spies.childProcess.spawn ); - sinon.assert.notCalled( spies.devUtils.logInfo ); - sinon.assert.notCalled( spies.devUtils.logWarning ); - sinon.assert.notCalled( spies.inquirer.prompt ); - sinon.assert.notCalled( spies.path.resolve ); - sinon.assert.notCalled( spies.fs.readFileSync ); - } ); } ); - it( 'should not build the DLLs and not ask user if `--dll` flag is `false`, even if console is interactive', () => { - spies.isInteractive.returns( true ); - spies.transformFileOptionToTestGlob.returns( [ + it( 'should not build the DLLs if there are no DLL-related manual tests', async () => { + vi.mocked( transformFileOptionToTestGlob ).mockReturnValue( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' + 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); + await runManualTests( defaultOptions ); + + expect( vi.mocked( spawn ) ).not.toHaveBeenCalled(); + expect( vi.mocked( inquirer ).prompt ).not.toHaveBeenCalled(); + expect( stubs.log.info ).not.toHaveBeenCalled(); + expect( stubs.log.warning ).not.toHaveBeenCalled(); + } ); + + it( 'should not build the DLLs if the console is not interactive', async () => { + vi.mocked( isInteractive ).mockReturnValue( false ); + + await runManualTests( defaultOptions ); + + expect( vi.mocked( spawn ) ).not.toHaveBeenCalled(); + expect( vi.mocked( inquirer ).prompt ).not.toHaveBeenCalled(); + expect( stubs.log.info ).not.toHaveBeenCalled(); + expect( stubs.log.warning ).not.toHaveBeenCalled(); + } ); + + it( 'should not build the DLLs and not ask user if `--dll` flag is `false`, even if console is interactive', async () => { const options = { dll: false }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - sinon.assert.notCalled( spies.childProcess.spawn ); - sinon.assert.notCalled( spies.devUtils.logInfo ); - sinon.assert.notCalled( spies.devUtils.logWarning ); - sinon.assert.notCalled( spies.inquirer.prompt ); - sinon.assert.notCalled( spies.path.resolve ); - sinon.assert.notCalled( spies.fs.readFileSync ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); + + expect( vi.mocked( spawn ) ).not.toHaveBeenCalled(); + expect( vi.mocked( inquirer ).prompt ).not.toHaveBeenCalled(); + expect( stubs.log.info ).not.toHaveBeenCalled(); + expect( stubs.log.warning ).not.toHaveBeenCalled(); } ); - it( 'should not build the DLLs if user declined the question', () => { - spies.isInteractive.returns( true ); - spies.inquirer.prompt.resolves( { confirm: false } ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); + it( 'should not build the DLLs if user declined the question', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { confirm: false } ); - return runManualTests( defaultOptions ) - .then( () => { - sinon.assert.notCalled( spies.childProcess.spawn ); - - sinon.assert.calledOnce( spies.devUtils.logWarning ); - sinon.assert.calledWith( spies.devUtils.logWarning.firstCall, - '\n⚠ Some tests require DLL builds.\n' - ); - - sinon.assert.calledTwice( spies.devUtils.logInfo ); - sinon.assert.calledWith( spies.devUtils.logInfo.firstCall, - 'You don\'t have to update these builds every time unless you want to check changes in DLL tests.' - ); - sinon.assert.calledWith( spies.devUtils.logInfo.secondCall, - 'You can use the following flags to skip this prompt in the future: --dll / --no-dll.\n' - ); - - sinon.assert.calledOnce( spies.inquirer.prompt ); - sinon.assert.calledWith( spies.inquirer.prompt.firstCall, [ { - message: 'Create the DLL builds now?', - type: 'confirm', - name: 'confirm', - default: false - } ] ); - - sinon.assert.notCalled( spies.path.resolve ); - sinon.assert.notCalled( spies.fs.readFileSync ); - } ); + await runManualTests( defaultOptions ); + + expect( vi.mocked( spawn ) ).not.toHaveBeenCalled(); + expect( vi.mocked( inquirer ).prompt ).toHaveBeenCalledExactlyOnceWith( [ + { + message: 'Create the DLL builds now?', + type: 'confirm', + name: 'confirm', + default: false + } + ] ); + expect( stubs.log.warning ).toHaveBeenCalledExactlyOnceWith( '\n⚠ Some tests require DLL builds.\n' ); + expect( stubs.log.info ).toHaveBeenCalledTimes( 2 ); + expect( stubs.log.info ).toHaveBeenCalledWith( + 'You don\'t have to update these builds every time unless you want to check changes in DLL tests.' + ); + expect( stubs.log.info ).toHaveBeenCalledWith( + 'You can use the following flags to skip this prompt in the future: --dll / --no-dll.\n' + ); + expect( vi.mocked( chalk ).bold ).toHaveBeenCalledTimes( 3 ); } ); - it( 'should open the package.json in each repository in proper order (root repository first, then external ones)', () => { - spies.isInteractive.returns( true ); - spies.inquirer.prompt.resolves( { confirm: true } ); - spies.fs.readFileSync.returns( JSON.stringify( { + it( 'should open the package.json in each repository in proper order (root repository first, then external ones)', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { confirm: true } ); + vi.mocked( fs ).readFileSync.mockReturnValue( JSON.stringify( { name: 'ckeditor5-example-package' } ) ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); - const consoleStub = sinon.stub( console, 'log' ); - - return runManualTests( defaultOptions ) - .then( () => { - consoleStub.restore(); - - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.firstArg ).to.equal( '\n📍 DLL building complete.\n' ); - - sinon.assert.notCalled( spies.childProcess.spawn ); - - sinon.assert.calledOnce( spies.devUtils.logWarning ); - sinon.assert.calledWith( spies.devUtils.logWarning.firstCall, - '\n⚠ Some tests require DLL builds.\n' - ); - - sinon.assert.calledTwice( spies.devUtils.logInfo ); - sinon.assert.calledWith( spies.devUtils.logInfo.firstCall, - 'You don\'t have to update these builds every time unless you want to check changes in DLL tests.' - ); - sinon.assert.calledWith( spies.devUtils.logInfo.secondCall, - 'You can use the following flags to skip this prompt in the future: --dll / --no-dll.\n' - ); - - sinon.assert.calledOnce( spies.inquirer.prompt ); - sinon.assert.calledWith( spies.inquirer.prompt.firstCall, [ { - message: 'Create the DLL builds now?', - type: 'confirm', - name: 'confirm', - default: false - } ] ); - - // The `path.resolve()` calls are not sorted, so it is called in the same order as data returned from `glob`. - sinon.assert.calledThrice( spies.path.resolve ); - sinon.assert.calledWith( spies.path.resolve.firstCall, - 'workspace/ckeditor5/external/ckeditor5-internal/package.json' - ); - sinon.assert.calledWith( spies.path.resolve.secondCall, - 'workspace/ckeditor5/external/collaboration-features/package.json' - ); - sinon.assert.calledWith( spies.path.resolve.thirdCall, - 'workspace/ckeditor5/package.json' - ); - - // The `fs.readFileSync()` calls are sorted: root repository first, then external ones. - sinon.assert.calledThrice( spies.fs.readFileSync ); - sinon.assert.calledWith( spies.fs.readFileSync.firstCall, - '/absolute/path/to/workspace/ckeditor5/package.json' - ); - sinon.assert.calledWith( spies.fs.readFileSync.secondCall, - '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal/package.json' - ); - sinon.assert.calledWith( spies.fs.readFileSync.thirdCall, - '/absolute/path/to/workspace/ckeditor5/external/collaboration-features/package.json' - ); - } ); + const consoleStub = vi.spyOn( console, 'log' ).mockImplementation( () => {} ); + + await runManualTests( defaultOptions ); + + expect( consoleStub ).toHaveBeenCalledExactlyOnceWith( '\n📍 DLL building complete.\n' ); + consoleStub.mockRestore(); + + // The `path.resolve()` calls are not sorted, so it is called in the same order as data returned from `glob`. + expect( vi.mocked( path ).resolve ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( path ).resolve ).toHaveBeenCalledWith( 'workspace/ckeditor5/external/ckeditor5-internal/package.json' ); + expect( vi.mocked( path ).resolve ).toHaveBeenCalledWith( 'workspace/ckeditor5/external/collaboration-features/package.json' ); + expect( vi.mocked( path ).resolve ).toHaveBeenCalledWith( 'workspace/ckeditor5/package.json' ); + + // The `fs.readFileSync()` calls are sorted: root repository first, then external ones. + expect( vi.mocked( fs ).readFileSync ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( fs ).readFileSync ).toHaveBeenNthCalledWith( + 1, + '/absolute/path/to/workspace/ckeditor5/package.json', + 'utf-8' + ); + + expect( vi.mocked( fs ).readFileSync ).toHaveBeenNthCalledWith( + 2, + '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal/package.json', + 'utf-8' + ); + expect( vi.mocked( fs ).readFileSync ).toHaveBeenNthCalledWith( + 3, + '/absolute/path/to/workspace/ckeditor5/external/collaboration-features/package.json', + 'utf-8' + ); } ); - it( 'should not build the DLLs if no repository has scripts in package.json', () => { - spies.isInteractive.returns( true ); - spies.inquirer.prompt.resolves( { confirm: true } ); - spies.fs.readFileSync.returns( JSON.stringify( { + it( 'should not build the DLLs if no repository has scripts in package.json', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { confirm: true } ); + vi.mocked( fs ).readFileSync.mockReturnValue( JSON.stringify( { name: 'ckeditor5-example-package' } ) ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); - - const consoleStub = sinon.stub( console, 'log' ); - return runManualTests( defaultOptions ) - .then( () => { - consoleStub.restore(); + vi.spyOn( console, 'log' ).mockImplementation( () => {} ); - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.firstArg ).to.equal( '\n📍 DLL building complete.\n' ); + await runManualTests( defaultOptions ); - sinon.assert.notCalled( spies.childProcess.spawn ); - } ); + expect( vi.mocked( spawn ) ).not.toHaveBeenCalled(); } ); - it( 'should not build the DLLs if no repository has script to build DLLs in package.json', () => { - spies.isInteractive.returns( true ); - spies.inquirer.prompt.resolves( { confirm: true } ); - spies.fs.readFileSync.returns( JSON.stringify( { + it( 'should not build the DLLs if no repository has script to build DLLs in package.json', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { confirm: true } ); + vi.mocked( fs ).readFileSync.mockReturnValue( JSON.stringify( { name: 'ckeditor5-example-package', scripts: { 'build': 'node ./scripts/build' } } ) ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); - const consoleStub = sinon.stub( console, 'log' ); + vi.spyOn( console, 'log' ).mockImplementation( () => {} ); - return runManualTests( defaultOptions ) - .then( () => { - consoleStub.restore(); + await runManualTests( defaultOptions ); - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.firstArg ).to.equal( '\n📍 DLL building complete.\n' ); - - sinon.assert.notCalled( spies.childProcess.spawn ); - } ); + expect( vi.mocked( spawn ) ).not.toHaveBeenCalled(); } ); - it( 'should build the DLLs in each repository that has script to build DLLs in package.json', () => { - spies.isInteractive.returns( true ); - spies.inquirer.prompt.resolves( { confirm: true } ); - spies.fs.readFileSync - .returns( JSON.stringify( { + it( 'should build the DLLs in each repository that has script to build DLLs in package.json', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { confirm: true } ); + vi.mocked( fs ).readFileSync.mockImplementation( input => { + if ( input === '/absolute/path/to/workspace/ckeditor5/external/collaboration-features/package.json' ) { + return JSON.stringify( { + name: 'ckeditor5-example-package', + scripts: { + 'build': 'node ./scripts/build' + } + } ); + } + + return JSON.stringify( { name: 'ckeditor5-example-package', scripts: { 'dll:build': 'node ./scripts/build-dll' } - } ) ) - .withArgs( '/absolute/path/to/workspace/ckeditor5/external/collaboration-features/package.json' ) - .returns( JSON.stringify( { - name: 'ckeditor5-example-package', - scripts: { - 'build': 'node ./scripts/build' - } - } ) ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); - - const consoleStub = sinon.stub( console, 'log' ); + } ); + } ); - return runManualTests( defaultOptions ) - .then( () => { - consoleStub.restore(); + vi.spyOn( console, 'log' ).mockImplementation( () => {} ); - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.firstArg ).to.equal( '\n📍 DLL building complete.\n' ); + await runManualTests( defaultOptions ); - sinon.assert.calledTwice( spies.childProcess.spawn ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5', + stdio: 'inherit' + } + ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal', + stdio: 'inherit' + } + ); + } ); - sinon.assert.calledWith( spies.childProcess.spawn.firstCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5', - stdio: 'inherit' - } - ); - sinon.assert.calledWith( spies.childProcess.spawn.secondCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal', - stdio: 'inherit' + it( 'should build the DLLs automatically and not ask user if `--dll` flag is `true`, even if console is interactive', async () => { + vi.mocked( fs ).readFileSync.mockImplementation( input => { + if ( input === '/absolute/path/to/workspace/ckeditor5/external/collaboration-features/package.json' ) { + return JSON.stringify( { + name: 'ckeditor5-example-package', + scripts: { + 'build': 'node ./scripts/build' } - ); - - sinon.assert.calledOnce( spies.devUtils.logWarning ); - sinon.assert.calledWith( spies.devUtils.logWarning.firstCall, - '\n⚠ Some tests require DLL builds.\n' - ); - - sinon.assert.callCount( spies.devUtils.logInfo, 4 ); - sinon.assert.calledWith( spies.devUtils.logInfo.getCall( 0 ), - 'You don\'t have to update these builds every time unless you want to check changes in DLL tests.' - ); - sinon.assert.calledWith( spies.devUtils.logInfo.getCall( 1 ), - 'You can use the following flags to skip this prompt in the future: --dll / --no-dll.\n' - ); - sinon.assert.calledWith( spies.devUtils.logInfo.getCall( 2 ), - '\n📍 Building DLLs in ckeditor5...\n' - ); - sinon.assert.calledWith( spies.devUtils.logInfo.getCall( 3 ), - '\n📍 Building DLLs in ckeditor5-internal...\n' - ); - } ); - } ); + } ); + } - it( 'should build the DLLs automatically and not ask user if `--dll` flag is `true`, even if console is interactive', () => { - spies.isInteractive.returns( true ); - spies.fs.readFileSync - .returns( JSON.stringify( { + return JSON.stringify( { name: 'ckeditor5-example-package', scripts: { 'dll:build': 'node ./scripts/build-dll' } - } ) ) - .withArgs( '/absolute/path/to/workspace/ckeditor5/external/collaboration-features/package.json' ) - .returns( JSON.stringify( { - name: 'ckeditor5-example-package', - scripts: { - 'build': 'node ./scripts/build' - } - } ) ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); + } ); + } ); const options = { dll: true }; - const consoleStub = sinon.stub( console, 'log' ); - - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - consoleStub.restore(); + vi.spyOn( console, 'log' ).mockImplementation( () => {} ); - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.firstArg ).to.equal( '\n📍 DLL building complete.\n' ); + await runManualTests( { ...defaultOptions, ...options } ); - sinon.assert.notCalled( spies.inquirer.prompt ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5', + stdio: 'inherit' + } + ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal', + stdio: 'inherit' + } + ); + } ); - sinon.assert.calledTwice( spies.childProcess.spawn ); - sinon.assert.calledWith( spies.childProcess.spawn.firstCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5', - stdio: 'inherit' + it( 'should reject a promise if building DLLs has failed', async () => { + vi.mocked( inquirer ).prompt.mockResolvedValue( { confirm: true } ); + vi.mocked( fs ).readFileSync.mockImplementation( input => { + if ( input === '/absolute/path/to/workspace/ckeditor5/external/collaboration-features/package.json' ) { + return JSON.stringify( { + name: 'ckeditor5-example-package', + scripts: { + 'build': 'node ./scripts/build' } - ); - sinon.assert.calledWith( spies.childProcess.spawn.secondCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal', - stdio: 'inherit' - } - ); - - sinon.assert.notCalled( spies.devUtils.logWarning ); + } ); + } - sinon.assert.calledTwice( spies.devUtils.logInfo ); - sinon.assert.calledWith( spies.devUtils.logInfo.firstCall, - '\n📍 Building DLLs in ckeditor5...\n' - ); - sinon.assert.calledWith( spies.devUtils.logInfo.secondCall, - '\n📍 Building DLLs in ckeditor5-internal...\n' - ); + return JSON.stringify( { + name: 'ckeditor5-example-package', + scripts: { + 'dll:build': 'node ./scripts/build-dll' + } } ); - } ); - - it( 'should reject a promise if building DLLs has failed', () => { - spies.isInteractive.returns( true ); - spies.inquirer.prompt.resolves( { confirm: true } ); - spies.fs.readFileSync.returns( JSON.stringify( { - name: 'ckeditor5-example-package', - scripts: { - 'dll:build': 'node ./scripts/build-dll' + } ); + stubs.spawn.spawnExitCode = 1; + + await expect( runManualTests( defaultOptions ) ) + .rejects.toThrow( 'Building DLLs in ckeditor5 finished with an error.' ); + + expect( vi.mocked( spawn ) ).toHaveBeenCalledExactlyOnceWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5', + stdio: 'inherit' } - } ) ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); - spies.childProcess.spawnExitCode = 1; - - return runManualTests( defaultOptions ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error.message ).to.equal( 'Building DLLs in ckeditor5 finished with an error.' ); - - sinon.assert.calledOnce( spies.childProcess.spawn ); - sinon.assert.calledWith( spies.childProcess.spawn.firstCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5', - stdio: 'inherit' - } - ); - } - ); + ); } ); - it( 'should build the DLLs in each repository for Windows environment', () => { - sandbox.stub( process, 'platform' ).value( 'win32' ); + it( 'should build the DLLs in each repository for Windows environment', async () => { + vi.spyOn( process, 'platform', 'get' ).mockReturnValue( 'win32' ); - spies.isInteractive.returns( true ); - spies.inquirer.prompt.resolves( { confirm: true } ); - spies.fs.readFileSync.returns( JSON.stringify( { + vi.mocked( inquirer ).prompt.mockResolvedValue( { confirm: true } ); + vi.mocked( fs ).readFileSync.mockReturnValue( JSON.stringify( { name: 'ckeditor5-example-package', scripts: { 'dll:build': 'node ./scripts/build-dll' } } ) ); - spies.transformFileOptionToTestGlob.returns( [ - 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js', - 'workspace/packages/ckeditor5-*/tests/**/manual/dll/**/*.js' - ] ); - - const consoleStub = sinon.stub( console, 'log' ); - - return runManualTests( defaultOptions ) - .then( () => { - consoleStub.restore(); - - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.firstArg ).to.equal( '\n📍 DLL building complete.\n' ); - - sinon.assert.calledOnce( spies.scriptCompiler ); - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace\\packages\\ckeditor5-foo\\tests\\manual\\feature-a.js', - 'workspace\\packages\\ckeditor5-bar\\tests\\manual\\feature-b.js', - 'workspace\\packages\\ckeditor-foo\\tests\\manual\\feature-c.js', - 'workspace\\packages\\ckeditor-bar\\tests\\manual\\feature-d.js', - 'workspace\\packages\\ckeditor5-foo\\tests\\manual\\dll\\feature-i-dll.js', - 'workspace\\packages\\ckeditor5-bar\\tests\\manual\\dll\\feature-j-dll.js' - ], - themePath: null, - language: undefined, - tsconfig: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - debug: undefined, - disableWatch: true, - identityFile: undefined - } ); - sinon.assert.calledOnce( spies.htmlFileCompiler ); - sinon.assert.calledWith( spies.htmlFileCompiler.firstCall, { - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace\\packages\\ckeditor5-foo\\tests\\manual\\feature-a.js', - 'workspace\\packages\\ckeditor5-bar\\tests\\manual\\feature-b.js', - 'workspace\\packages\\ckeditor-foo\\tests\\manual\\feature-c.js', - 'workspace\\packages\\ckeditor-bar\\tests\\manual\\feature-d.js', - 'workspace\\packages\\ckeditor5-foo\\tests\\manual\\dll\\feature-i-dll.js', - 'workspace\\packages\\ckeditor5-bar\\tests\\manual\\dll\\feature-j-dll.js' - ], - language: undefined, - onTestCompilationStatus: sinon.match.func, - additionalLanguages: undefined, - disableWatch: true, - silent: false - } ); - - sinon.assert.calledThrice( spies.childProcess.spawn ); - sinon.assert.calledWith( spies.childProcess.spawn.firstCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5', - stdio: 'inherit' - } - ); - sinon.assert.calledWith( spies.childProcess.spawn.secondCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal', - stdio: 'inherit' - } - ); - sinon.assert.calledWith( spies.childProcess.spawn.thirdCall, - 'yarnpkg', - [ 'run', 'dll:build' ], - { - encoding: 'utf8', - shell: true, - cwd: '/absolute/path/to/workspace/ckeditor5/external/collaboration-features', - stdio: 'inherit' - } - ); - } ); + vi.spyOn( console, 'log' ).mockImplementation( () => {} ); + + await runManualTests( defaultOptions ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5', + stdio: 'inherit' + } + ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5/external/ckeditor5-internal', + stdio: 'inherit' + } + ); + expect( vi.mocked( spawn ) ).toHaveBeenCalledWith( + 'yarnpkg', + [ 'run', 'dll:build' ], + { + encoding: 'utf8', + shell: true, + cwd: '/absolute/path/to/workspace/ckeditor5/external/collaboration-features', + stdio: 'inherit' + } + ); } ); - it( 'allows specifying tsconfig file', () => { - spies.transformFileOptionToTestGlob.onFirstCall().returns( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ); - spies.transformFileOptionToTestGlob.onSecondCall().returns( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); + it( 'allows specifying tsconfig file', async () => { + vi.mocked( transformFileOptionToTestGlob ) + .mockReturnValueOnce( [ 'workspace/packages/ckeditor5-*/tests/**/manual/**/*.js' ] ) + .mockReturnValueOnce( [ 'workspace/packages/ckeditor-*/tests/**/manual/**/*.js' ] ); const options = { files: [ @@ -1227,30 +815,11 @@ describe( 'runManualTests', () => { tsconfig: '/absolute/path/to/tsconfig.json' }; - return runManualTests( { ...defaultOptions, ...options } ) - .then( () => { - expect( spies.server.calledOnce ).to.equal( true ); - expect( spies.server.firstCall.args[ 0 ] ).to.equal( 'workspace/build/.manual-tests' ); - - sinon.assert.calledWith( spies.scriptCompiler.firstCall, { - cwd: 'workspace', - buildDir: 'workspace/build/.manual-tests', - sourceFiles: [ - 'workspace/packages/ckeditor5-foo/tests/manual/feature-a.js', - 'workspace/packages/ckeditor5-bar/tests/manual/feature-b.js', - 'workspace/packages/ckeditor-foo/tests/manual/feature-c.js', - 'workspace/packages/ckeditor-bar/tests/manual/feature-d.js' - ], - themePath: null, - language: undefined, - onTestCompilationStatus: sinon.match.func, - debug: undefined, - additionalLanguages: undefined, - disableWatch: false, - tsconfig: '/absolute/path/to/tsconfig.json', - identityFile: undefined - } ); - } ); + await runManualTests( { ...defaultOptions, ...options } ); + + expect( vi.mocked( compileManualTestScripts ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + tsconfig: '/absolute/path/to/tsconfig.json' + } ) ); } ); } ); } ); From c9c067ccb70c5c0902f5b9661c8bf854ddaa5fa2 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 17 Sep 2024 22:49:20 +0200 Subject: [PATCH 101/172] Converted attribute tests. --- .../tests/utils/automated-tests/assertions/attribute.js | 7 +++---- .../tests/utils/automated-tests/assertions/equal-markup.js | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/attribute.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/attribute.js index 99d4514b5..7684962e2 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/attribute.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/attribute.js @@ -3,12 +3,11 @@ * For licensing, see LICENSE.md. */ -const chai = require( 'chai' ); -const expect = chai.expect; -const attributeFactory = require( '../../../../lib/utils/automated-tests/assertions/attribute' ); +import { beforeAll, describe, expect, it, chai } from 'vitest'; +import attributeFactory from '../../../../lib/utils/automated-tests/assertions/attribute.js'; describe( 'attribute chai assertion', () => { - before( () => { + beforeAll( () => { attributeFactory( chai ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/equal-markup.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/equal-markup.js index 302fc3b0c..de9bc8fa1 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/equal-markup.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/assertions/equal-markup.js @@ -3,12 +3,11 @@ * For licensing, see LICENSE.md. */ -const chai = require( 'chai' ); -const expect = chai.expect; -const equalMarkupFactory = require( '../../../../lib/utils/automated-tests/assertions/equal-markup' ); +import { beforeAll, describe, expect, it, chai } from 'vitest'; +import equalMarkupFactory from '../../../../lib/utils/automated-tests/assertions/equal-markup.js'; describe( 'equalMarkup chai assertion', () => { - before( () => { + beforeAll( () => { equalMarkupFactory( chai ); } ); From 14c8f2caaa9277d7f34973ed3e4931c589649723 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 18 Sep 2024 10:58:45 +0200 Subject: [PATCH 102/172] More tests. --- .../ckeditor5-dev-tests/bin/testmanual.js | 2 +- .../utils/automated-tests/parsearguments.js | 3 + .../lib/utils/getdefinitionsfromfile.js | 3 + .../utils/manual-tests/getwebpackconfig.js | 5 +- .../utils/transformfileoptiontotestglob.js | 16 +- .../getdefinitionsfromfile/secret.cjs | 12 + .../fixtures/karma-config-overrides/noop.cjs | 7 + .../karma-config-overrides/removecoverage.cjs | 8 + .../entrypoint.cjs} | 0 .../tests/tasks/runautomatedtests.js | 3 +- .../utils/automated-tests/getkarmaconfig.js | 224 ++++++++++-------- .../utils/automated-tests/getwebpackconfig.js | 92 +++---- .../utils/automated-tests/parsearguments.js | 20 +- .../treatwarningsaserrorswebpackplugin.js | 83 ++++--- .../tests/utils/getdefinitionsfromfile.js | 67 ++---- .../tests/utils/getrelativefilepath.js | 198 +++++++--------- .../utils/manual-tests/compilehtmlfiles.js | 12 +- .../tests/utils/requiredll.js | 134 +++++------ .../utils/transformfileoptiontotestglob.js | 44 ++-- packages/ckeditor5-dev-tests/vitest.config.js | 3 +- 20 files changed, 449 insertions(+), 487 deletions(-) create mode 100644 packages/ckeditor5-dev-tests/tests/fixtures/getdefinitionsfromfile/secret.cjs create mode 100644 packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/noop.cjs create mode 100644 packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/removecoverage.cjs rename packages/ckeditor5-dev-tests/tests/{utils/automated-tests/fixtures/file.js => fixtures/treatwarningsaserrorswebpackplugin/entrypoint.cjs} (100%) diff --git a/packages/ckeditor5-dev-tests/bin/testmanual.js b/packages/ckeditor5-dev-tests/bin/testmanual.js index 3f1f98c12..97fd6dbfc 100755 --- a/packages/ckeditor5-dev-tests/bin/testmanual.js +++ b/packages/ckeditor5-dev-tests/bin/testmanual.js @@ -24,5 +24,5 @@ tests.runManualTests( options ) // Mark result of this task as invalid. process.exitCode = 1; - console.log( chalk.red( error ) ); + console.log( chalk.red( error.stack ) ); } ); diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js index 567e9eebe..fe654960b 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js @@ -5,9 +5,12 @@ import fs from 'fs'; import path from 'path'; +import { createRequire } from 'module'; import minimist from 'minimist'; import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; +const require = createRequire( import.meta.url ); + /** * @param {Array.} args * @returns {Object} diff --git a/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js b/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js index f4b552825..fdca410ff 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js +++ b/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js @@ -4,6 +4,9 @@ */ import path from 'path'; +import { createRequire } from 'module'; + +const require = createRequire( import.meta.url ); /** * @param {String|null} definitionSource diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js index daa45f37f..5f918c110 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js @@ -4,16 +4,19 @@ */ import path from 'path'; +import { fileURLToPath } from 'url'; +import { createRequire } from 'module'; import webpack from 'webpack'; import { CKEditorTranslationsPlugin } from '@ckeditor/ckeditor5-dev-translations'; import { loaders } from '@ckeditor/ckeditor5-dev-utils'; import WebpackNotifierPlugin from './webpacknotifierplugin.js'; import getDefinitionsFromFile from '../getdefinitionsfromfile.js'; -import { fileURLToPath } from 'url'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); +const require = createRequire( import.meta.url ); + /** * @param {Object} options * @param {String} options.cwd Current working directory. Usually it points to the CKEditor 5 root directory. diff --git a/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js b/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js index 08cedc7b1..7d60c4da6 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js +++ b/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js @@ -6,7 +6,7 @@ import fs from 'fs'; import path from 'path'; -const EXTERNAL_DIR_PATH = path.join( process.cwd(), 'external' ); +const EXTERNAL_DIR_NAME = 'external'; /** * Converts values of `--files` argument to proper globs. Handles both JS and TS files. These are the supported types of values: @@ -61,9 +61,11 @@ export default function transformFileOptionToTestGlob( pattern, isManualTest = f * @returns {Array.} */ function getExternalRepositoryGlob( pattern, { isManualTest } ) { + const externalPath = path.join( process.cwd(), EXTERNAL_DIR_NAME ); + const repositoryGlob = isManualTest ? - path.join( EXTERNAL_DIR_PATH, pattern, 'tests', 'manual', '**', '*' ) + '.{js,ts}' : - path.join( EXTERNAL_DIR_PATH, pattern, 'tests', '**', '*' ) + '.{js,ts}'; + path.join( externalPath, pattern, 'tests', 'manual', '**', '*' ) + '.{js,ts}' : + path.join( externalPath, pattern, 'tests', '**', '*' ) + '.{js,ts}'; return [ repositoryGlob.split( path.sep ).join( path.posix.sep ) @@ -75,12 +77,14 @@ function getExternalRepositoryGlob( pattern, { isManualTest } ) { * @returns {Boolean} */ function doesPatternMatchExternalRepositoryName( pattern ) { - if ( !fs.existsSync( EXTERNAL_DIR_PATH ) ) { + const externalPath = path.join( process.cwd(), EXTERNAL_DIR_NAME ); + + if ( !fs.existsSync( externalPath ) ) { return false; } - return fs.readdirSync( EXTERNAL_DIR_PATH ) - .filter( externalDir => fs.statSync( path.join( EXTERNAL_DIR_PATH, externalDir ) ).isDirectory() ) + return fs.readdirSync( externalPath ) + .filter( externalDir => fs.statSync( path.join( externalPath, externalDir ) ).isDirectory() ) .includes( pattern ); } diff --git a/packages/ckeditor5-dev-tests/tests/fixtures/getdefinitionsfromfile/secret.cjs b/packages/ckeditor5-dev-tests/tests/fixtures/getdefinitionsfromfile/secret.cjs new file mode 100644 index 000000000..3f6701c37 --- /dev/null +++ b/packages/ckeditor5-dev-tests/tests/fixtures/getdefinitionsfromfile/secret.cjs @@ -0,0 +1,12 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +module.exports = { + SECRET: 'secret', + ANOTHER_SECRET: 'another-secret', + NON_PRIMITIVE_SECRET: { + foo: [ 'bar', 'baz' ] + } +}; diff --git a/packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/noop.cjs b/packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/noop.cjs new file mode 100644 index 000000000..0b81095cf --- /dev/null +++ b/packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/noop.cjs @@ -0,0 +1,7 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +module.exports = () => { +}; diff --git a/packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/removecoverage.cjs b/packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/removecoverage.cjs new file mode 100644 index 000000000..0828ebaed --- /dev/null +++ b/packages/ckeditor5-dev-tests/tests/fixtures/karma-config-overrides/removecoverage.cjs @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md. + */ + +module.exports = config => { + config.reporters.splice( config.reporters.indexOf( 'coverage' ), 1 ); +} diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/fixtures/file.js b/packages/ckeditor5-dev-tests/tests/fixtures/treatwarningsaserrorswebpackplugin/entrypoint.cjs similarity index 100% rename from packages/ckeditor5-dev-tests/tests/utils/automated-tests/fixtures/file.js rename to packages/ckeditor5-dev-tests/tests/fixtures/treatwarningsaserrorswebpackplugin/entrypoint.cjs diff --git a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js index 61099ca15..7cc0c9288 100644 --- a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js +++ b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js @@ -204,8 +204,7 @@ describe( 'runAutomatedTests()', () => { production: false }; - const consoleWarnStub = vi.spyOn( console, 'warn' ).mockImplementation( () => { - } ); + const consoleWarnStub = vi.spyOn( console, 'warn' ).mockImplementation( () => {} ); vi.mocked( chalk ).yellow.mockReturnValue( 'chalk.yellow: warn' ); vi.mocked( fs ).readdirSync.mockReturnValue( [] ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getkarmaconfig.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getkarmaconfig.js index 975c18cc4..e028cf62b 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getkarmaconfig.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getkarmaconfig.js @@ -3,49 +3,41 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const mockery = require( 'mockery' ); -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const path = require( 'path' ); +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import getWebpackConfigForAutomatedTests from '../../../lib/utils/automated-tests/getwebpackconfig.js'; +import getKarmaConfig from '../../../lib/utils/automated-tests/getkarmaconfig.js'; + +vi.mock( 'path', () => ( { + default: { + join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ), + dirname: vi.fn() + } +} ) ); +vi.mock( '../../../lib/utils/automated-tests/getwebpackconfig.js' ); describe( 'getKarmaConfig()', () => { - let getKarmaConfig, sandbox, karmaConfigOverrides; - const originalEnv = process.env; + const karmaConfigOverrides = { + // A relative path according to the tested file. + // From: /ckeditor5-dev/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js + // To: /ckeditor5-dev/packages/ckeditor5-dev-tests/tests/utils/automated-tests/fixtures/karma-config-overrides/*.cjs + noop: '../../../tests/fixtures/karma-config-overrides/noop.cjs', + removeCoverage: '../../../tests/fixtures/karma-config-overrides/removecoverage.cjs' + }; beforeEach( () => { - sandbox = sinon.createSandbox(); - - karmaConfigOverrides = sandbox.spy(); - sandbox.stub( process, 'cwd' ).returns( 'workspace' ); - sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); + vi.spyOn( process, 'cwd' ).mockReturnValue( 'workspace' ); - // Sinon cannot stub non-existing props. process.env = Object.assign( {}, originalEnv, { CI: false } ); - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( './getwebpackconfig', options => options ); - mockery.registerMock( 'karma-config-overrides', karmaConfigOverrides ); - - getKarmaConfig = require( '../../../lib/utils/automated-tests/getkarmaconfig' ); } ); afterEach( () => { - sandbox.restore(); - mockery.disable(); - mockery.deregisterAll(); - process.env = originalEnv; } ); it( 'should return basic karma config for all tested files', () => { + vi.mocked( getWebpackConfigForAutomatedTests ).mockReturnValue( { webpackConfig: true } ); + const options = { files: [ '*' ], reporter: 'mocha', @@ -63,16 +55,28 @@ describe( 'getKarmaConfig()', () => { const karmaConfig = getKarmaConfig( options ); - expect( karmaConfig ).to.have.own.property( 'basePath', 'workspace' ); - expect( karmaConfig ).to.have.own.property( 'frameworks' ); - expect( karmaConfig ).to.have.own.property( 'files' ); - expect( karmaConfig ).to.have.own.property( 'preprocessors' ); - expect( karmaConfig ).to.have.own.property( 'webpack' ); - expect( karmaConfig.webpack ).to.deep.equal( { ...options, files: [ 'workspace/packages/ckeditor5-*/tests/**/*.js' ] } ); - expect( karmaConfig ).to.have.own.property( 'webpackMiddleware' ); - expect( karmaConfig ).to.have.own.property( 'reporters' ); - expect( karmaConfig ).to.have.own.property( 'browsers' ); - expect( karmaConfig ).to.have.own.property( 'singleRun', true ); + expect( vi.mocked( getWebpackConfigForAutomatedTests ) ).toHaveBeenCalledExactlyOnceWith( { + ...options, + files: [ + 'workspace/packages/ckeditor5-*/tests/**/*.js' + ] + } ); + + expect( karmaConfig ).toEqual( expect.objectContaining( { + basePath: 'workspace', + frameworks: expect.any( Array ), + files: expect.any( Array ), + preprocessors: expect.any( Object ), + webpack: expect.any( Object ), + webpackMiddleware: expect.any( Object ), + reporters: expect.any( Array ), + browsers: expect.any( Array ), + singleRun: true + } ) ); + + expect( karmaConfig.webpack ).toEqual( expect.objectContaining( { + webpackConfig: true + } ) ); } ); // See: https://github.com/ckeditor/ckeditor5/issues/8823 @@ -92,15 +96,23 @@ describe( 'getKarmaConfig()', () => { } } ); - expect( karmaConfig ).to.have.own.property( 'proxies' ); - expect( karmaConfig.proxies ).to.have.own.property( '/assets/' ); - expect( karmaConfig.proxies ).to.have.own.property( '/example.com/image.png' ); - expect( karmaConfig.proxies ).to.have.own.property( '/www.example.com/image.png' ); - - expect( karmaConfig.files ).to.be.an( 'array' ); - expect( karmaConfig.files.length ).to.equal( 2 ); - expect( karmaConfig.files[ 0 ] ).to.equal( 'workspace/entry-file.js' ); - expect( karmaConfig.files[ 1 ].pattern ).to.equal( 'packages/ckeditor5-utils/tests/_assets/**/*' ); + expect( karmaConfig ).toEqual( expect.objectContaining( { + proxies: expect.any( Object ), + files: expect.any( Array ) + } ) ); + expect( karmaConfig.proxies ).toEqual( expect.objectContaining( { + '/assets/': expect.any( String ), + '/example.com/image.png': expect.any( String ), + '/www.example.com/image.png': expect.any( String ) + } ) ); + + expect( karmaConfig.files ).toHaveLength( 2 ); + expect( karmaConfig.files ).toEqual( expect.arrayContaining( [ + 'workspace/entry-file.js', + expect.objectContaining( { + pattern: 'packages/ckeditor5-utils/tests/_assets/**/*' + } ) + ] ) ); } ); it( 'should contain a list of available plugins', () => { @@ -119,50 +131,53 @@ describe( 'getKarmaConfig()', () => { } } ); - expect( karmaConfig.plugins ).to.be.an( 'array' ); - expect( karmaConfig.plugins ).to.have.lengthOf.above( 0 ); + expect( karmaConfig ).toEqual( expect.objectContaining( { + files: expect.any( Array ) + } ) ); + expect( karmaConfig.files ).not.toHaveLength( 0 ); } ); it( 'should enable webpack watcher when passed the "karmaConfigOverrides" option (execute in Intellij)', () => { + vi.mocked( getWebpackConfigForAutomatedTests ).mockReturnValue( { watch: null } ); + const karmaConfig = getKarmaConfig( { files: [ '*' ], reporter: 'mocha', - karmaConfigOverrides: 'karma-config-overrides', + karmaConfigOverrides: karmaConfigOverrides.noop, globPatterns: { '*': 'workspace/packages/ckeditor5-*/tests/**/*.js' } } ); - expect( karmaConfig.webpack ).to.contain.property( 'watch', true ); + expect( karmaConfig.webpack ).toEqual( expect.objectContaining( { + watch: true + } ) ); } ); it( 'should configure coverage reporter', () => { + vi.mocked( getWebpackConfigForAutomatedTests ).mockReturnValue( { } ); + const karmaConfig = getKarmaConfig( { files: [ '*' ], reporter: 'mocha', - karmaConfigOverrides: 'karma-config-overrides', + karmaConfigOverrides: karmaConfigOverrides.noop, globPatterns: { '*': 'workspace/packages/ckeditor5-*/tests/**/*.js' }, coverage: true } ); - expect( karmaConfig.reporters ).to.contain( 'coverage' ); - expect( karmaConfig.coverageReporter ).to.contain.property( 'reporters' ); + expect( karmaConfig ).toEqual( expect.objectContaining( { + reporters: expect.arrayContaining( [ 'coverage' ] ), + coverageReporter: { + reporters: expect.any( Array ), + watermarks: expect.any( Object ) + } + } ) ); } ); it( 'should remove webpack babel-loader if coverage reporter is removed by overrides', () => { - mockery.registerMock( 'karma-config-overrides-remove-coverage', config => { - config.reporters.splice( config.reporters.indexOf( 'coverage' ), 1 ); - } ); - - const karmaConfig = getKarmaConfig( { - files: [ '*' ], - reporter: 'mocha', - karmaConfigOverrides: 'karma-config-overrides-remove-coverage', - globPatterns: { - '*': 'workspace/packages/ckeditor5-*/tests/**/*.js' - }, + vi.mocked( getWebpackConfigForAutomatedTests ).mockReturnValue( { module: { rules: [ { @@ -172,20 +187,33 @@ describe( 'getKarmaConfig()', () => { loader: 'other-loader' } ] + } + } ); + + const karmaConfig = getKarmaConfig( { + files: [ '*' ], + reporter: 'mocha', + karmaConfigOverrides: karmaConfigOverrides.removeCoverage, + globPatterns: { + '*': 'workspace/packages/ckeditor5-*/tests/**/*.js' }, coverage: true } ); const loaders = karmaConfig.webpack.module.rules.map( rule => rule.loader ); - expect( karmaConfig.reporters ).to.not.contain( 'coverage' ); - expect( loaders ).to.not.contain( 'babel-loader' ); - expect( loaders ).to.contain( 'other-loader' ); + expect( karmaConfig ).not.toEqual( expect.objectContaining( { + reporters: expect.arrayContaining( [ 'coverage' ] ) + } ) ); + + expect( loaders ).not.toEqual( expect.arrayContaining( [ 'babel-loader' ] ) ); + expect( loaders ).toEqual( expect.arrayContaining( [ 'other-loader' ] ) ); } ); - it( 'should return custom launchers with flags', () => { + it( 'should return custom browser launchers with flags', () => { const options = { reporter: 'mocha', + files: [ '*' ], globPatterns: { '*': 'workspace/packages/ckeditor5-*/tests/**/*.js' } @@ -193,30 +221,36 @@ describe( 'getKarmaConfig()', () => { const karmaConfig = getKarmaConfig( options ); - expect( karmaConfig ).to.have.own.property( 'customLaunchers' ); - expect( karmaConfig.customLaunchers ).to.have.own.property( 'CHROME_CI' ); - expect( karmaConfig.customLaunchers ).to.have.own.property( 'CHROME_LOCAL' ); - - expect( karmaConfig.customLaunchers.CHROME_CI ).to.have.own.property( 'base', 'Chrome' ); - expect( karmaConfig.customLaunchers.CHROME_CI ).to.have.own.property( 'flags' ); - expect( karmaConfig.customLaunchers.CHROME_CI.flags ).to.deep.equal( [ - '--disable-background-timer-throttling', - '--js-flags="--expose-gc"', - '--disable-renderer-backgrounding', - '--disable-backgrounding-occluded-windows', - '--disable-search-engine-choice-screen', - '--no-sandbox' - ] ); - - expect( karmaConfig.customLaunchers.CHROME_LOCAL ).to.have.own.property( 'base', 'Chrome' ); - expect( karmaConfig.customLaunchers.CHROME_LOCAL ).to.have.own.property( 'flags' ); - expect( karmaConfig.customLaunchers.CHROME_LOCAL.flags ).to.deep.equal( [ - '--disable-background-timer-throttling', - '--js-flags="--expose-gc"', - '--disable-renderer-backgrounding', - '--disable-backgrounding-occluded-windows', - '--disable-search-engine-choice-screen', - '--remote-debugging-port=9222' - ] ); + expect( karmaConfig ).toEqual( expect.objectContaining( { + customLaunchers: expect.any( Object ) + } ) ); + + expect( karmaConfig.customLaunchers ).toEqual( expect.objectContaining( { + CHROME_CI: expect.objectContaining( { + base: 'Chrome', + flags: [ + '--disable-background-timer-throttling', + '--js-flags="--expose-gc"', + '--disable-renderer-backgrounding', + '--disable-backgrounding-occluded-windows', + '--disable-search-engine-choice-screen', + '--no-sandbox' + ] + } ) + } ) ); + + expect( karmaConfig.customLaunchers ).toEqual( expect.objectContaining( { + CHROME_LOCAL: expect.objectContaining( { + base: 'Chrome', + flags: [ + '--disable-background-timer-throttling', + '--js-flags="--expose-gc"', + '--disable-renderer-backgrounding', + '--disable-backgrounding-occluded-windows', + '--disable-search-engine-choice-screen', + '--remote-debugging-port=9222' + ] + } ) + } ) ); } ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getwebpackconfig.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getwebpackconfig.js index c4831526e..3838ece6f 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getwebpackconfig.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/getwebpackconfig.js @@ -3,54 +3,22 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); -const { expect } = require( 'chai' ); +import { describe, expect, it, vi } from 'vitest'; +import { loaders } from '@ckeditor/ckeditor5-dev-utils'; +import TreatWarningsAsErrorsWebpackPlugin from '../../../lib/utils/automated-tests/treatwarningsaserrorswebpackplugin.js'; +import getWebpackConfigForAutomatedTests from '../../../lib/utils/automated-tests/getwebpackconfig.js'; +import getDefinitionsFromFile from '../../../lib/utils/getdefinitionsfromfile.js'; + +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( '../../../lib/utils/getdefinitionsfromfile.js' ); +vi.mock( '../../../lib/utils/automated-tests/treatwarningsaserrorswebpackplugin', () => ( { + default: class TreatWarningsAsErrorsWebpackPlugin {} +} ) ); describe( 'getWebpackConfigForAutomatedTests()', () => { - let getWebpackConfigForAutomatedTests, stubs; - - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - getDefinitionsFromFile: sinon.stub().returns( {} ), - loaders: { - getIconsLoader: sinon.stub().returns( {} ), - getStylesLoader: sinon.stub().returns( {} ), - getTypeScriptLoader: sinon.stub().returns( {} ), - getFormattedTextLoader: sinon.stub().returns( {} ), - getCoverageLoader: sinon.stub().returns( {} ), - getJavaScriptLoader: sinon.stub().returns( {} ) - }, - TreatWarningsAsErrorsWebpackPlugin: class TreatWarningsAsErrorsWebpackPlugin {} - }; - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { loaders: stubs.loaders } ); - - mockery.registerMock( '../getdefinitionsfromfile', stubs.getDefinitionsFromFile ); - - mockery.registerMock( './treatwarningsaserrorswebpackplugin', stubs.TreatWarningsAsErrorsWebpackPlugin ); - - getWebpackConfigForAutomatedTests = require( '../../../lib/utils/automated-tests/getwebpackconfig' ); - } ); - - afterEach( () => { - sinon.restore(); - mockery.disable(); - mockery.deregisterAll(); - } ); - it( 'should return basic webpack configuration object', () => { - const debug = []; const webpackConfig = getWebpackConfigForAutomatedTests( { - debug, + debug: [], themePath: '/theme/path', tsconfig: '/tsconfig/path' } ); @@ -58,18 +26,16 @@ describe( 'getWebpackConfigForAutomatedTests()', () => { expect( webpackConfig.resolve.extensions ).to.deep.equal( [ '.ts', '.js', '.json' ] ); expect( webpackConfig.resolve.fallback.timers ).to.equal( false ); - expect( stubs.loaders.getIconsLoader.calledOnce ).to.equal( true ); - - expect( stubs.loaders.getStylesLoader.calledOnce ).to.equal( true ); - expect( stubs.loaders.getStylesLoader.firstCall.args[ 0 ] ).to.have.property( 'themePath', '/theme/path' ); - expect( stubs.loaders.getStylesLoader.firstCall.args[ 0 ] ).to.have.property( 'minify', true ); - - expect( stubs.loaders.getTypeScriptLoader.calledOnce ).to.equal( true ); - expect( stubs.loaders.getTypeScriptLoader.firstCall.args[ 0 ] ).to.have.property( 'configFile', '/tsconfig/path' ); - - expect( stubs.loaders.getFormattedTextLoader.calledOnce ).to.equal( true ); - - expect( stubs.loaders.getCoverageLoader.called ).to.equal( false ); + expect( vi.mocked( loaders.getIconsLoader ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( loaders.getFormattedTextLoader ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( loaders.getCoverageLoader ) ).not.toHaveBeenCalledOnce(); + expect( vi.mocked( loaders.getStylesLoader ) ).toHaveBeenCalledExactlyOnceWith( { + themePath: '/theme/path', + minify: true + } ); + expect( vi.mocked( loaders.getTypeScriptLoader ) ).toHaveBeenCalledExactlyOnceWith( { + configFile: '/tsconfig/path' + } ); expect( webpackConfig.resolveLoader.modules[ 0 ] ).to.equal( 'node_modules' ); expect( webpackConfig.devtool ).to.equal( undefined ); @@ -87,7 +53,8 @@ describe( 'getWebpackConfigForAutomatedTests()', () => { getWebpackConfigForAutomatedTests( { files: [ '**/*.js' ] } ); - expect( stubs.loaders.getJavaScriptLoader.called ).to.equal( false ); + + expect( vi.mocked( loaders.getJavaScriptLoader ) ).not.toHaveBeenCalledOnce(); } ); it( 'should return webpack configuration containing a loader for measuring the coverage', () => { @@ -96,7 +63,7 @@ describe( 'getWebpackConfigForAutomatedTests()', () => { files: [ '**/*.js' ] } ); - expect( stubs.loaders.getCoverageLoader.called ).to.equal( true ); + expect( vi.mocked( loaders.getCoverageLoader ) ).toHaveBeenCalledOnce(); } ); it( 'should return webpack configuration with source map support', () => { @@ -124,7 +91,7 @@ describe( 'getWebpackConfigForAutomatedTests()', () => { } ); it( 'should return webpack configuration with loaded identity file', () => { - stubs.getDefinitionsFromFile.returns( { LICENSE_KEY: 'secret' } ); + vi.mocked( getDefinitionsFromFile ).mockReturnValue( { LICENSE_KEY: 'secret' } ); const webpackConfig = getWebpackConfigForAutomatedTests( { identityFile: 'path/to/secrets.js' @@ -132,7 +99,7 @@ describe( 'getWebpackConfigForAutomatedTests()', () => { const plugin = webpackConfig.plugins[ 0 ]; - expect( stubs.getDefinitionsFromFile.firstCall.args[ 0 ] ).to.equal( 'path/to/secrets.js' ); + expect( vi.mocked( getDefinitionsFromFile ) ).toHaveBeenCalledExactlyOnceWith( 'path/to/secrets.js' ); expect( plugin.definitions.LICENSE_KEY ).to.equal( 'secret' ); } ); @@ -169,8 +136,9 @@ describe( 'getWebpackConfigForAutomatedTests()', () => { production: true } ); - expect( webpackConfig.plugins.filter( plugin => plugin instanceof stubs.TreatWarningsAsErrorsWebpackPlugin ) ) - .to.have.lengthOf( 1 ); + const plugin = webpackConfig.plugins.find( plugin => plugin instanceof TreatWarningsAsErrorsWebpackPlugin ); + + expect( plugin ).toBeTruthy(); } ); it( 'should load TypeScript files first when importing JS files', () => { diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js index d76d9eb57..75c66178a 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js @@ -3,28 +3,18 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const mockery = require( 'mockery' ); -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const proxyquire = require( 'proxyquire' ); +import { describe, expect, it, vi } from 'vitest'; const originalPosixJoin = path.posix.join; +vi.mock( 'fs' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); + describe( 'parseArguments()', () => { let parseArguments, sandbox, stubs, packageName; beforeEach( () => { - sandbox = sinon.createSandbox(); - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); + vi.spyOn( process, 'cwd' ).mockReturnValue( '/' ); stubs = { cwd: sandbox.stub( process, 'cwd' ).callsFake( () => '/' ), diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js index 326cb611c..76b876755 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js @@ -3,59 +3,68 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it } from 'vitest'; +import webpack from 'webpack'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import TreatWarningsAsErrorsWebpackPlugin from '../../../lib/utils/automated-tests/treatwarningsaserrorswebpackplugin.js'; -const webpack = require( 'webpack' ); -const path = require( 'path' ); -const { expect } = require( 'chai' ); +const __filename = fileURLToPath( import.meta.url ); +const __dirname = path.dirname( __filename ); describe( 'TreatWarningsAsErrorsWebpackPlugin()', () => { - let TreatWarningsAsErrorsWebpackPlugin; + it( 'should reassign warnings to errors and not emit the code when errors are present', () => { + return new Promise( ( resolve, reject ) => { + runCompiler( + { + mode: 'development', + entry: './treatwarningsaserrorswebpackplugin/entrypoint.cjs', + plugins: [ + { + apply( compiler ) { + compiler.hooks.make.tap( 'MakeCompilationWarning', compilation => { + compilation.errors.push( new Error( 'Compilation error 1' ) ); + compilation.errors.push( new Error( 'Compilation error 2' ) ); + compilation.warnings.push( new Error( 'Compilation warning 1' ) ); + compilation.warnings.push( new Error( 'Compilation warning 2' ) ); + } ); + } + }, + new TreatWarningsAsErrorsWebpackPlugin() + ] + }, + ( err, stats ) => { + if ( err ) { + return reject( err ); + } - beforeEach( () => { - TreatWarningsAsErrorsWebpackPlugin = require( '../../../lib/utils/automated-tests/treatwarningsaserrorswebpackplugin' ); - } ); + try { + const statsJson = stats.toJson( { errorDetails: false } ); - it( 'should reassign warnings to errors and not emit the code when errors are present', done => { - runCompiler( { - mode: 'development', - entry: './file', - plugins: [ - { - apply( compiler ) { - compiler.hooks.make.tap( 'MakeCompilationWarning', compilation => { - compilation.errors.push( new Error( 'Compilation error 1' ) ); - compilation.errors.push( new Error( 'Compilation error 2' ) ); - compilation.warnings.push( new Error( 'Compilation warning 1' ) ); - compilation.warnings.push( new Error( 'Compilation warning 2' ) ); - } ); + expect( statsJson.errors.length ).to.equal( 4 ); + expect( statsJson.warnings.length ).to.equal( 0 ); + expect( statsJson.errors[ 0 ].message ).to.equal( 'Compilation error 1' ); + expect( statsJson.errors[ 1 ].message ).to.equal( 'Compilation error 2' ); + expect( statsJson.errors[ 2 ].message ).to.equal( 'Compilation warning 1' ); + expect( statsJson.errors[ 3 ].message ).to.equal( 'Compilation warning 2' ); + expect( statsJson.assets[ 0 ].emitted ).to.equal( false ); + resolve(); + } catch ( error ) { + reject( error ); } - }, - new TreatWarningsAsErrorsWebpackPlugin() - ] - }, stats => { - const statsJson = stats.toJson( { errorDetails: false } ); - - expect( statsJson.errors.length ).to.equal( 4 ); - expect( statsJson.warnings.length ).to.equal( 0 ); - expect( statsJson.errors[ 0 ].message ).to.equal( 'Compilation error 1' ); - expect( statsJson.errors[ 1 ].message ).to.equal( 'Compilation error 2' ); - expect( statsJson.errors[ 2 ].message ).to.equal( 'Compilation warning 1' ); - expect( statsJson.errors[ 3 ].message ).to.equal( 'Compilation warning 2' ); - expect( statsJson.assets[ 0 ].emitted ).to.equal( false ); - done(); + } ); } ); } ); } ); function runCompiler( options, callback ) { - options.context = path.join( __dirname, 'fixtures' ); + options.context = path.join( __dirname, '..', '..', 'fixtures' ); const compiler = webpack( options ); compiler.outputFileSystem = {}; compiler.run( ( err, stats ) => { - callback( stats ); + callback( err, stats ); } ); } diff --git a/packages/ckeditor5-dev-tests/tests/utils/getdefinitionsfromfile.js b/packages/ckeditor5-dev-tests/tests/utils/getdefinitionsfromfile.js index 2525b1846..28e88923b 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/getdefinitionsfromfile.js +++ b/packages/ckeditor5-dev-tests/tests/utils/getdefinitionsfromfile.js @@ -3,49 +3,20 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); -const { expect } = require( 'chai' ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import path from 'path'; +import getDefinitionsFromFile from '../../lib/utils/getdefinitionsfromfile.js'; describe( 'getDefinitionsFromFile()', () => { - let getDefinitionsFromFile, consoleStub; - beforeEach( () => { - consoleStub = sinon.stub( console, 'error' ); - sinon.stub( process, 'cwd' ).returns( '/workspace' ); - - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( 'path', { - join: sinon.stub().callsFake( ( ...chunks ) => chunks.join( '/' ).replace( '/./', '/' ) ), - isAbsolute: sinon.stub().callsFake( path => path.startsWith( '/' ) ) - } ); - - mockery.registerMock( '/workspace/path/to/secret.js', { - SECRET: 'secret', - ANOTHER_SECRET: 'another-secret', - NON_PRIMITIVE_SECRET: { - foo: [ 'bar', 'baz' ] - } - } ); - - getDefinitionsFromFile = require( '../../lib/utils/getdefinitionsfromfile' ); - } ); - - afterEach( () => { - sinon.restore(); - mockery.disable(); - mockery.deregisterAll(); + vi.spyOn( path, 'join' ).mockImplementation( ( ...chunks ) => chunks.join( '/' ).replace( '/./', '/' ) ); } ); it( 'should return definition object if path to identity file is relative', () => { - const definitions = getDefinitionsFromFile( './path/to/secret.js' ); + const definitions = getDefinitionsFromFile( + // A relative path according to a package root. + path.join( '.', 'tests', 'fixtures', 'getdefinitionsfromfile', 'secret.cjs' ) + ); expect( definitions ).to.deep.equal( { SECRET: '"secret"', @@ -55,7 +26,9 @@ describe( 'getDefinitionsFromFile()', () => { } ); it( 'should return definition object if path to identity file is absolute', () => { - const definitions = getDefinitionsFromFile( '/workspace/path/to/secret.js' ); + const definitions = getDefinitionsFromFile( + path.join( __dirname, '..', 'fixtures', 'getdefinitionsfromfile', 'secret.cjs' ) + ); expect( definitions ).to.deep.equal( { SECRET: '"secret"', @@ -71,28 +44,32 @@ describe( 'getDefinitionsFromFile()', () => { } ); it( 'should not throw an error and return empty object if path to identity file is not valid', () => { + const consoleStub = vi.spyOn( console, 'error' ).mockImplementation( () => {} ); let definitions; expect( () => { definitions = getDefinitionsFromFile( 'foo.js' ); } ).to.not.throw(); - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.args[ 0 ] ).to.satisfy( msg => msg.startsWith( 'Cannot find module \'/workspace/foo.js\'' ) ); + expect( consoleStub ).toHaveBeenCalledExactlyOnceWith( + expect.stringContaining( 'Cannot find module' ) + ); expect( definitions ).to.deep.equal( {} ); } ); - it( 'should not throw an error and return empty object if stringifying the identity file has failed', () => { - sinon.stub( JSON, 'stringify' ).throws( new Error( 'Example error.' ) ); + it( 'should not throw an error and return empty object if stringifies the identity file has failed', () => { + const consoleStub = vi.spyOn( console, 'error' ).mockImplementation( () => {} ); + vi.spyOn( JSON, 'stringify' ).mockImplementation( () => { + throw new Error( 'Example error.' ); + } ); let definitions; expect( () => { - definitions = getDefinitionsFromFile( '/workspace/path/to/secret.js' ); + definitions = getDefinitionsFromFile( path.join( '.', 'tests', 'fixtures', 'getdefinitionsfromfile', 'secret.cjs' ) ); } ).to.not.throw(); - expect( consoleStub.callCount ).to.equal( 1 ); - expect( consoleStub.firstCall.args[ 0 ] ).to.equal( 'Example error.' ); + expect( consoleStub ).toHaveBeenCalledExactlyOnceWith( 'Example error.' ); expect( definitions ).to.deep.equal( {} ); } ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/getrelativefilepath.js b/packages/ckeditor5-dev-tests/tests/utils/getrelativefilepath.js index d1d763179..8e110a742 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/getrelativefilepath.js +++ b/packages/ckeditor5-dev-tests/tests/utils/getrelativefilepath.js @@ -3,142 +3,122 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const path = require( 'path' ); -const sinon = require( 'sinon' ); -const { expect } = require( 'chai' ); - -describe( 'dev-tests/utils', () => { - let getRelativeFilePath; - - beforeEach( () => { - getRelativeFilePath = require( '../../lib/utils/getrelativefilepath' ); - } ); - - describe( 'getRelativeFilePath()', () => { - let sandbox; +import path from 'path'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import getRelativeFilePath from '../../lib/utils/getrelativefilepath.js'; +describe( 'getRelativeFilePath()', () => { + describe( 'Unix paths', () => { beforeEach( () => { - sandbox = sinon.createSandbox(); - } ); - - afterEach( () => { - sandbox.restore(); + vi.spyOn( path, 'join' ).mockImplementation( ( ...args ) => args.join( '/' ) ); } ); - describe( 'Unix paths', () => { - beforeEach( () => { - sandbox.stub( path, 'join' ).callsFake( ( ...args ) => args.join( '/' ) ); - } ); + it( 'returns path which starts with package name (simple check)', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/Users/foo' ); - it( 'returns path which starts with package name (simple check)', () => { - sandbox.stub( process, 'cwd' ).returns( '/Users/foo' ); + checkPath( '/Users/foo/packages/ckeditor5-foo/tests/manual/foo.js', 'ckeditor5-foo/tests/manual/foo.js' ); + } ); - checkPath( '/Users/foo/packages/ckeditor5-foo/tests/manual/foo.js', 'ckeditor5-foo/tests/manual/foo.js' ); - } ); + it( 'returns path which starts with package name (workspace directory looks like package name)', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/Users/foo/ckeditor5-workspace/ckeditor5' ); - it( 'returns path which starts with package name (workspace directory looks like package name)', () => { - sandbox.stub( process, 'cwd' ).returns( '/Users/foo/ckeditor5-workspace/ckeditor5' ); + checkPath( + '/Users/foo/ckeditor5-workspace/ckeditor5/packages/ckeditor5-foo/tests/manual/foo.js', + 'ckeditor5-foo/tests/manual/foo.js' + ); + } ); - checkPath( - '/Users/foo/ckeditor5-workspace/ckeditor5/packages/ckeditor5-foo/tests/manual/foo.js', - 'ckeditor5-foo/tests/manual/foo.js' - ); - } ); + it( 'returns a proper path for "ckeditor-" prefix', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/space' ); - it( 'returns a proper path for "ckeditor-" prefix', () => { - sandbox.stub( process, 'cwd' ).returns( '/work/space' ); + checkPath( '/work/space/packages/ckeditor-foo/tests/manual/foo.js', 'ckeditor-foo/tests/manual/foo.js' ); + } ); - checkPath( '/work/space/packages/ckeditor-foo/tests/manual/foo.js', 'ckeditor-foo/tests/manual/foo.js' ); - } ); + it( 'returns a proper path for "ckeditor-" prefix and "ckeditor.js" file', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/space' ); - it( 'returns a proper path for "ckeditor-" prefix and "ckeditor.js" file', () => { - sandbox.stub( process, 'cwd' ).returns( '/work/space' ); + checkPath( '/work/space/packages/ckeditor-foo/tests/manual/ckeditor.js', 'ckeditor-foo/tests/manual/ckeditor.js' ); + } ); - checkPath( '/work/space/packages/ckeditor-foo/tests/manual/ckeditor.js', 'ckeditor-foo/tests/manual/ckeditor.js' ); - } ); + it( 'returns a proper path to from the main (root) package', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/space' ); + checkPath( '/work/space/packages/ckeditor5/tests/manual/foo.js', 'ckeditor5/tests/manual/foo.js' ); + } ); - it( 'returns a proper path to from the main (root) package', () => { - sandbox.stub( process, 'cwd' ).returns( '/work/space' ); - checkPath( '/work/space/packages/ckeditor5/tests/manual/foo.js', 'ckeditor5/tests/manual/foo.js' ); - } ); + it( 'returns a proper path for "ckeditor5.js" file', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/space' ); + checkPath( + '/work/space/packages/ckeditor5-build-a/tests/manual/ckeditor5.js', + 'ckeditor5-build-a/tests/manual/ckeditor5.js' + ); + } ); - it( 'returns a proper path for "ckeditor5.js" file', () => { - sandbox.stub( process, 'cwd' ).returns( '/work/space' ); - checkPath( - '/work/space/packages/ckeditor5-build-a/tests/manual/ckeditor5.js', - 'ckeditor5-build-a/tests/manual/ckeditor5.js' - ); - } ); + it( 'returns a proper path for "ckeditor.js" file', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/space' ); + checkPath( + '/work/space/packages/ckeditor5-build-a/tests/manual/ckeditor.js', + 'ckeditor5-build-a/tests/manual/ckeditor.js' ); + } ); + } ); - it( 'returns a proper path for "ckeditor.js" file', () => { - sandbox.stub( process, 'cwd' ).returns( '/work/space' ); - checkPath( - '/work/space/packages/ckeditor5-build-a/tests/manual/ckeditor.js', - 'ckeditor5-build-a/tests/manual/ckeditor.js' ); - } ); + describe( 'Windows paths', () => { + beforeEach( () => { + vi.spyOn( path, 'join' ).mockImplementation( ( ...args ) => args.join( '\\' ) ); } ); - describe( 'Windows paths', () => { - beforeEach( () => { - sandbox.stub( path, 'join' ).callsFake( ( ...args ) => args.join( '\\' ) ); - } ); + it( 'returns path which starts with package name (simple check)', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\work\\space' ); - it( 'returns path which starts with package name (simple check)', () => { - sandbox.stub( process, 'cwd' ).returns( 'C:\\work\\space' ); + checkPath( 'C:\\work\\space\\packages\\ckeditor5-foo\\tests\\manual\\foo.js', 'ckeditor5-foo\\tests\\manual\\foo.js' ); + } ); - checkPath( 'C:\\work\\space\\packages\\ckeditor5-foo\\tests\\manual\\foo.js', 'ckeditor5-foo\\tests\\manual\\foo.js' ); - } ); + it( 'returns path which starts with package name (workspace directory looks like package name)', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\Document and settings\\foo\\ckeditor5-workspace\\ckeditor5' ); - it( 'returns path which starts with package name (workspace directory looks like package name)', () => { - sandbox.stub( process, 'cwd' ).returns( 'C:\\Document and settings\\foo\\ckeditor5-workspace\\ckeditor5' ); + checkPath( + 'C:\\Document and settings\\foo\\ckeditor5-workspace\\ckeditor5\\packages\\ckeditor5-foo\\tests\\manual\\foo.js', + 'ckeditor5-foo\\tests\\manual\\foo.js' + ); + } ); - checkPath( - 'C:\\Document and settings\\foo\\ckeditor5-workspace\\ckeditor5\\packages\\ckeditor5-foo\\tests\\manual\\foo.js', - 'ckeditor5-foo\\tests\\manual\\foo.js' - ); - } ); + it( 'returns a proper path for "ckeditor-" prefix', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\work\\space' ); - it( 'returns a proper path for "ckeditor-" prefix', () => { - sandbox.stub( process, 'cwd' ).returns( 'C:\\work\\space' ); + checkPath( 'C:\\work\\space\\packages\\ckeditor-foo\\tests\\manual\\foo.js', 'ckeditor-foo\\tests\\manual\\foo.js' ); + } ); - checkPath( 'C:\\work\\space\\packages\\ckeditor-foo\\tests\\manual\\foo.js', 'ckeditor-foo\\tests\\manual\\foo.js' ); - } ); + it( 'returns a proper path for "ckeditor-" prefix and "ckeditor.js" file', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\work\\space' ); - it( 'returns a proper path for "ckeditor-" prefix and "ckeditor.js" file', () => { - sandbox.stub( process, 'cwd' ).returns( 'C:\\work\\space' ); + checkPath( + 'C:\\work\\space\\packages\\ckeditor-foo\\tests\\manual\\ckeditor.js', + 'ckeditor-foo\\tests\\manual\\ckeditor.js' + ); + } ); - checkPath( - 'C:\\work\\space\\packages\\ckeditor-foo\\tests\\manual\\ckeditor.js', - 'ckeditor-foo\\tests\\manual\\ckeditor.js' - ); - } ); + it( 'returns a proper path to from the main (root) package', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\work\\space' ); + checkPath( 'C:\\work\\space\\tests\\manual\\foo.js', 'ckeditor5\\tests\\manual\\foo.js' ); + } ); - it( 'returns a proper path to from the main (root) package', () => { - sandbox.stub( process, 'cwd' ).returns( 'C:\\work\\space' ); - checkPath( 'C:\\work\\space\\tests\\manual\\foo.js', 'ckeditor5\\tests\\manual\\foo.js' ); - } ); + it( 'returns a proper path for "ckeditor5.js" file', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\work\\space' ); + checkPath( + 'C:\\work\\space\\packages\\ckeditor5-build-a\\tests\\manual\\ckeditor5.js', + 'ckeditor5-build-a\\tests\\manual\\ckeditor5.js' + ); + } ); - it( 'returns a proper path for "ckeditor5.js" file', () => { - sandbox.stub( process, 'cwd' ).returns( 'C:\\work\\space' ); - checkPath( - 'C:\\work\\space\\packages\\ckeditor5-build-a\\tests\\manual\\ckeditor5.js', - 'ckeditor5-build-a\\tests\\manual\\ckeditor5.js' - ); - } ); - - it( 'returns a proper path for "ckeditor.js" file', () => { - sandbox.stub( process, 'cwd' ).returns( 'C:\\work\\space' ); - checkPath( - 'C:\\work\\space\\packages\\ckeditor5-build-a\\tests\\manual\\ckeditor.js', - 'ckeditor5-build-a\\tests\\manual\\ckeditor.js' - ); - } ); + it( 'returns a proper path for "ckeditor.js" file', () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( 'C:\\work\\space' ); + checkPath( + 'C:\\work\\space\\packages\\ckeditor5-build-a\\tests\\manual\\ckeditor.js', + 'ckeditor5-build-a\\tests\\manual\\ckeditor.js' + ); } ); } ); - - function checkPath( filePath, expectedPath ) { - expect( getRelativeFilePath( filePath ) ).to.equal( expectedPath ); - } } ); + +function checkPath( filePath, expectedPath ) { + expect( getRelativeFilePath( filePath ) ).to.equal( expectedPath ); +} diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js index e94a529da..7753418d2 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js @@ -3,16 +3,10 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { createRequire } from 'module'; -const path = require( 'path' ); -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); -const { use, expect } = require( 'chai' ); -const chokidar = require( 'chokidar' ); -const sinonChai = require( 'sinon-chai' ); - -use( sinonChai ); +const require = createRequire( import.meta.url ); const fakeDirname = path.dirname( require.resolve( '../../../lib/utils/manual-tests/compilehtmlfiles' ) ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/requiredll.js b/packages/ckeditor5-dev-tests/tests/utils/requiredll.js index b4a13f5ef..a0e129973 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/requiredll.js +++ b/packages/ckeditor5-dev-tests/tests/utils/requiredll.js @@ -3,103 +3,83 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { describe, expect, it } from 'vitest'; +import requireDll from '../../lib/utils/requiredll.js'; -const sinon = require( 'sinon' ); -const { expect } = require( 'chai' ); +describe( 'requireDll()', () => { + it( 'should return true when loads JavaScript DLL file (Unix)', () => { + const files = [ + '/workspace/ckeditor5/tests/manual/all-features-dll.js' + ]; -describe( 'dev-tests/utils', () => { - let requireDll; - - beforeEach( () => { - requireDll = require( '../../lib/utils/requiredll' ); + expect( requireDll( files ) ).to.equal( true ); } ); - describe( 'requireDll()', () => { - let sandbox; - - beforeEach( () => { - sandbox = sinon.createSandbox(); - } ); - - afterEach( () => { - sandbox.restore(); - } ); - - it( 'should return true when loads JavaScript DLL file (Unix)', () => { - const files = [ - '/workspace/ckeditor5/tests/manual/all-features-dll.js' - ]; + it( 'should return true when loads single JavaScript DLL file (Unix)', () => { + const file = '/workspace/ckeditor5/tests/manual/all-features-dll.js'; - expect( requireDll( files ) ).to.equal( true ); - } ); - - it( 'should return true when loads single JavaScript DLL file (Unix)', () => { - const file = '/workspace/ckeditor5/tests/manual/all-features-dll.js'; - - expect( requireDll( file ) ).to.equal( true ); - } ); + expect( requireDll( file ) ).to.equal( true ); + } ); - it( 'should return true when loads TypeScript DLL file (Unix)', () => { - const files = [ - '/workspace/ckeditor5/tests/manual/all-features-dll.ts' - ]; + it( 'should return true when loads TypeScript DLL file (Unix)', () => { + const files = [ + '/workspace/ckeditor5/tests/manual/all-features-dll.ts' + ]; - expect( requireDll( files ) ).to.equal( true ); - } ); + expect( requireDll( files ) ).to.equal( true ); + } ); - it( 'should return true when loads JavaScript DLL file (Windows)', () => { - const files = [ - 'C:\\workspace\\ckeditor5\\tests\\manual\\all-features-dll.js' - ]; + it( 'should return true when loads JavaScript DLL file (Windows)', () => { + const files = [ + 'C:\\workspace\\ckeditor5\\tests\\manual\\all-features-dll.js' + ]; - expect( requireDll( files ) ).to.equal( true ); - } ); + expect( requireDll( files ) ).to.equal( true ); + } ); - it( 'should return true when loads TypeScript DLL file (Windows)', () => { - const files = [ - 'C:\\workspace\\ckeditor5\\tests\\manual\\all-features-dll.ts' - ]; + it( 'should return true when loads TypeScript DLL file (Windows)', () => { + const files = [ + 'C:\\workspace\\ckeditor5\\tests\\manual\\all-features-dll.ts' + ]; - expect( requireDll( files ) ).to.equal( true ); - } ); + expect( requireDll( files ) ).to.equal( true ); + } ); - it( 'should return false when loads JavaScript non-DLL file (Unix)', () => { - const files = [ - '/workspace/ckeditor5/tests/manual/article.js' - ]; + it( 'should return false when loads JavaScript non-DLL file (Unix)', () => { + const files = [ + '/workspace/ckeditor5/tests/manual/article.js' + ]; - expect( requireDll( files ) ).to.equal( false ); - } ); + expect( requireDll( files ) ).to.equal( false ); + } ); - it( 'should return false when loads single JavaScript non-DLL file (Unix)', () => { - const file = '/workspace/ckeditor5/tests/manual/article.js'; + it( 'should return false when loads single JavaScript non-DLL file (Unix)', () => { + const file = '/workspace/ckeditor5/tests/manual/article.js'; - expect( requireDll( file ) ).to.equal( false ); - } ); + expect( requireDll( file ) ).to.equal( false ); + } ); - it( 'should return false when loads TypeScript non-DLL file (Unix)', () => { - const files = [ - '/workspace/ckeditor5/tests/manual/article.ts' - ]; + it( 'should return false when loads TypeScript non-DLL file (Unix)', () => { + const files = [ + '/workspace/ckeditor5/tests/manual/article.ts' + ]; - expect( requireDll( files ) ).to.equal( false ); - } ); + expect( requireDll( files ) ).to.equal( false ); + } ); - it( 'should return false when loads JavaScript non-DLL file (Windows)', () => { - const files = [ - 'C:\\workspace\\ckeditor5\\tests\\manual\\article.js' - ]; + it( 'should return false when loads JavaScript non-DLL file (Windows)', () => { + const files = [ + 'C:\\workspace\\ckeditor5\\tests\\manual\\article.js' + ]; - expect( requireDll( files ) ).to.equal( false ); - } ); + expect( requireDll( files ) ).to.equal( false ); + } ); - it( 'should return false when loads TypeScript non-DLL file (Windows)', () => { - const files = [ - 'C:\\workspace\\ckeditor5\\tests\\manual\\article.ts' - ]; + it( 'should return false when loads TypeScript non-DLL file (Windows)', () => { + const files = [ + 'C:\\workspace\\ckeditor5\\tests\\manual\\article.ts' + ]; - expect( requireDll( files ) ).to.equal( false ); - } ); + expect( requireDll( files ) ).to.equal( false ); } ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/transformfileoptiontotestglob.js b/packages/ckeditor5-dev-tests/tests/utils/transformfileoptiontotestglob.js index 179494936..c302c926a 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/transformfileoptiontotestglob.js +++ b/packages/ckeditor5-dev-tests/tests/utils/transformfileoptiontotestglob.js @@ -3,30 +3,20 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import path from 'path'; +import fs from 'fs'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import transformFileOptionToTestGlob from '../../lib/utils/transformfileoptiontotestglob.js'; -const path = require( 'path' ); -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const fs = require( 'fs' ); - -describe( 'dev-tests/utils', () => { - let transformFileOptionToTestGlob, sandbox, readdirSyncStub, existsSyncStub, statSyncStub; +vi.mock( 'fs' ); +describe( 'transformFileOptionToTestGlob()', () => { beforeEach( () => { - sandbox = sinon.createSandbox(); - - sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ); - sandbox.stub( process, 'cwd' ).returns( '/workspace' ); - statSyncStub = sandbox.stub( fs, 'statSync' ).returns( { isDirectory: () => true } ); - readdirSyncStub = sandbox.stub( fs, 'readdirSync' ).returns( [ 'external-directory' ] ); - existsSyncStub = sandbox.stub( fs, 'existsSync' ).returns( true ); - - transformFileOptionToTestGlob = require( '../../lib/utils/transformfileoptiontotestglob' ); - } ); - - afterEach( () => { - sandbox.restore(); + vi.spyOn( path, 'join' ).mockImplementation( ( ...chunks ) => chunks.join( '/' ) ); + vi.spyOn( process, 'cwd' ).mockReturnValue( '/workspace' ); + vi.mocked( fs ).statSync.mockReturnValue( { isDirectory: () => true } ); + vi.mocked( fs ).readdirSync.mockReturnValue( [ 'external-directory' ] ); + vi.mocked( fs ).existsSync.mockReturnValue( true ); } ); describe( 'converts "ckeditor5" to pattern matching all root package tests', () => { @@ -255,7 +245,7 @@ describe( 'dev-tests/utils', () => { describe( 'should return correct glob for external dirs when external dir name passed', () => { it( 'for automated tests', () => { - readdirSyncStub.returns( [ 'test-external-directory' ] ); + vi.mocked( fs ).readdirSync.mockReturnValue( [ 'test-external-directory' ] ); expect( transformFileOptionToTestGlob( 'test-external-directory' ) ).to.deep.equal( [ '/workspace/external/test-external-directory/tests/**/*.{js,ts}' @@ -263,7 +253,7 @@ describe( 'dev-tests/utils', () => { } ); it( 'for manual tests', () => { - readdirSyncStub.returns( [ 'test-external-directory' ] ); + vi.mocked( fs ).readdirSync.mockReturnValue( [ 'test-external-directory' ] ); expect( transformFileOptionToTestGlob( 'test-external-directory', true ) ).to.deep.equal( [ '/workspace/external/test-external-directory/tests/manual/**/*.{js,ts}' @@ -271,8 +261,8 @@ describe( 'dev-tests/utils', () => { } ); it( 'should not match external directory when isDirectory returns false', () => { - statSyncStub.returns( { isDirectory: () => false } ); - readdirSyncStub.returns( [ 'test-external-file' ] ); + vi.mocked( fs ).statSync.mockReturnValue( { isDirectory: () => false } ); + vi.mocked( fs ).readdirSync.mockReturnValue( [ 'test-external-file' ] ); expect( transformFileOptionToTestGlob( 'test-external-directory', true ) ).to.deep.equal( [ '/workspace/packages/ckeditor5-test-external-directory/tests/manual/**/*.{js,ts}', @@ -284,10 +274,10 @@ describe( 'dev-tests/utils', () => { } ); it( 'should not call readdirSync if directory does not exist', () => { - existsSyncStub.returns( false ); + vi.mocked( fs ).existsSync.mockReturnValue( false ); transformFileOptionToTestGlob( 'test-random-directory' ); - expect( readdirSyncStub.called ).to.equal( false ); + expect( vi.mocked( fs ).readdirSync ).not.toHaveBeenCalled(); } ); } ); diff --git a/packages/ckeditor5-dev-tests/vitest.config.js b/packages/ckeditor5-dev-tests/vitest.config.js index 075897d63..450eeda30 100644 --- a/packages/ckeditor5-dev-tests/vitest.config.js +++ b/packages/ckeditor5-dev-tests/vitest.config.js @@ -17,7 +17,8 @@ export default defineConfig( { 'tests/**/*.js' ], exclude: [ - './tests/_utils/**/*.js' + './tests/_utils/**/*.js', + './tests/fixtures/**/*.js' ], coverage: { provider: 'v8', From 3488b6343d7b1ed2e2ffa8770cc2a4352c367d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Wed, 18 Sep 2024 11:15:20 +0200 Subject: [PATCH 103/172] Removed the `jsdoc-plugins` package. --- README.md | 3 +- packages/ckeditor5-dev-docs/lib/build.js | 109 +++- packages/ckeditor5-dev-docs/lib/buildjsdoc.js | 94 --- .../ckeditor5-dev-docs/lib/buildtypedoc.js | 91 --- packages/ckeditor5-dev-docs/package.json | 2 - packages/jsdoc-plugins/CHANGELOG.md | 250 -------- packages/jsdoc-plugins/LICENSE.md | 39 -- packages/jsdoc-plugins/README.md | 217 ------- packages/jsdoc-plugins/lib/cleanup.js | 77 --- .../jsdoc-plugins/lib/custom-tags/error.js | 27 - .../lib/custom-tags/observable.js | 17 - packages/jsdoc-plugins/lib/doclet.jsdoc | 114 ---- .../lib/event-extender/event-extender.js | 36 -- .../lib/export-fixer/export-fixer.js | 24 - .../jsdoc-plugins/lib/fix-code-snippets.js | 80 --- .../fixers/convert-short-refs-to-full-refs.js | 227 ------- .../fixers/fix-incorrect-class-constructor.js | 68 -- .../lib/longname-fixer/longname-fixer.js | 41 -- .../addmissingeventdocletsforobservables.js | 133 ---- .../lib/observable-event-provider/index.js | 16 - .../lib/purge-private-api-docs.js | 113 ---- .../lib/relation-fixer/addmissingdoclets.js | 148 ----- .../relation-fixer/addtypedefproperties.js | 108 ---- .../lib/relation-fixer/buildrelations.js | 163 ----- .../filteroutintenraldoclets.js | 17 - .../relation-fixer/getmissingdocletsdata.js | 211 ------ .../jsdoc-plugins/lib/relation-fixer/index.js | 77 --- .../lib/utils/doclet-collection.js | 67 -- .../jsdoc-plugins/lib/utils/doclet-logger.js | 21 - .../lib/validator/doclet-validator.js | 487 -------------- packages/jsdoc-plugins/lib/validator/types.js | 81 --- .../jsdoc-plugins/lib/validator/validator.js | 63 -- packages/jsdoc-plugins/package.json | 47 -- packages/jsdoc-plugins/tests/comment-fixer.js | 6 - .../tests/convert-short-refs-to-full-refs.js | 529 --------------- .../jsdoc-plugins/tests/doclet-collection.js | 68 -- .../jsdoc-plugins/tests/doclet-validator.js | 600 ------------------ .../jsdoc-plugins/tests/fix-code-snippets.js | 194 ------ .../_utils/extract-api-docs.js | 73 --- .../tests/integration-tests/_utils/logger.js | 12 - .../class-inheriting-from-mixins.js | 94 --- .../input/class.jsdoc | 15 - .../input/mixin.jsdoc | 35 - .../class-static-methods.js | 64 -- .../class-static-methods/input.jsdoc | 23 - .../integration-tests/class/class-basics.js | 157 ----- .../tests/integration-tests/class/input.jsdoc | 48 -- .../events-basics/event-basics.js | 145 ----- .../events-basics/input.jsdoc | 30 - .../exported-variables/exported-variables.js | 62 -- .../exported-variables/input.jsdoc | 20 - .../integration-tests/functions/functions.js | 70 -- .../integration-tests/functions/input.jsdoc | 17 - .../addmissingeventdocletsforobservables.js | 287 --------- .../tests/relation-fixer/addmissingdoclets.js | 117 ---- .../tests/relation-fixer/buildrelations.js | 172 ----- .../relationfixerintegration.js | 209 ------ .../test-data/inheritance-implicit.js | 33 - .../test-data/inheritance-inheritdoc.js | 42 -- .../relation-fixer/test-data/interface.js | 53 -- .../tests/relation-fixer/test-data/mixins.js | 54 -- .../test-data/unwanted-doclets.js | 50 -- scripts/changelog.js | 4 - 63 files changed, 80 insertions(+), 6471 deletions(-) delete mode 100644 packages/ckeditor5-dev-docs/lib/buildjsdoc.js delete mode 100644 packages/ckeditor5-dev-docs/lib/buildtypedoc.js delete mode 100644 packages/jsdoc-plugins/CHANGELOG.md delete mode 100644 packages/jsdoc-plugins/LICENSE.md delete mode 100644 packages/jsdoc-plugins/README.md delete mode 100644 packages/jsdoc-plugins/lib/cleanup.js delete mode 100644 packages/jsdoc-plugins/lib/custom-tags/error.js delete mode 100644 packages/jsdoc-plugins/lib/custom-tags/observable.js delete mode 100644 packages/jsdoc-plugins/lib/doclet.jsdoc delete mode 100644 packages/jsdoc-plugins/lib/event-extender/event-extender.js delete mode 100644 packages/jsdoc-plugins/lib/export-fixer/export-fixer.js delete mode 100644 packages/jsdoc-plugins/lib/fix-code-snippets.js delete mode 100644 packages/jsdoc-plugins/lib/longname-fixer/fixers/convert-short-refs-to-full-refs.js delete mode 100644 packages/jsdoc-plugins/lib/longname-fixer/fixers/fix-incorrect-class-constructor.js delete mode 100644 packages/jsdoc-plugins/lib/longname-fixer/longname-fixer.js delete mode 100644 packages/jsdoc-plugins/lib/observable-event-provider/addmissingeventdocletsforobservables.js delete mode 100644 packages/jsdoc-plugins/lib/observable-event-provider/index.js delete mode 100644 packages/jsdoc-plugins/lib/purge-private-api-docs.js delete mode 100644 packages/jsdoc-plugins/lib/relation-fixer/addmissingdoclets.js delete mode 100644 packages/jsdoc-plugins/lib/relation-fixer/addtypedefproperties.js delete mode 100644 packages/jsdoc-plugins/lib/relation-fixer/buildrelations.js delete mode 100644 packages/jsdoc-plugins/lib/relation-fixer/filteroutintenraldoclets.js delete mode 100644 packages/jsdoc-plugins/lib/relation-fixer/getmissingdocletsdata.js delete mode 100644 packages/jsdoc-plugins/lib/relation-fixer/index.js delete mode 100644 packages/jsdoc-plugins/lib/utils/doclet-collection.js delete mode 100644 packages/jsdoc-plugins/lib/utils/doclet-logger.js delete mode 100644 packages/jsdoc-plugins/lib/validator/doclet-validator.js delete mode 100644 packages/jsdoc-plugins/lib/validator/types.js delete mode 100644 packages/jsdoc-plugins/lib/validator/validator.js delete mode 100644 packages/jsdoc-plugins/package.json delete mode 100644 packages/jsdoc-plugins/tests/comment-fixer.js delete mode 100644 packages/jsdoc-plugins/tests/convert-short-refs-to-full-refs.js delete mode 100644 packages/jsdoc-plugins/tests/doclet-collection.js delete mode 100644 packages/jsdoc-plugins/tests/doclet-validator.js delete mode 100644 packages/jsdoc-plugins/tests/fix-code-snippets.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/_utils/extract-api-docs.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/_utils/logger.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/class-inheriting-from-mixins.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/class.jsdoc delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/mixin.jsdoc delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/class-static-methods/class-static-methods.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/class-static-methods/input.jsdoc delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/class/class-basics.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/class/input.jsdoc delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/events-basics/event-basics.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/events-basics/input.jsdoc delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/exported-variables/exported-variables.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/exported-variables/input.jsdoc delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/functions/functions.js delete mode 100644 packages/jsdoc-plugins/tests/integration-tests/functions/input.jsdoc delete mode 100644 packages/jsdoc-plugins/tests/observable-event-provider/addmissingeventdocletsforobservables.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/addmissingdoclets.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/buildrelations.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/relationfixerintegration.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-implicit.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-inheritdoc.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/test-data/interface.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/test-data/mixins.js delete mode 100644 packages/jsdoc-plugins/tests/relation-fixer/test-data/unwanted-doclets.js diff --git a/README.md b/README.md index 1e44996f3..16dfab7aa 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,7 @@ This repository is a monorepo. It contains multiple npm packages. | [`@ckeditor/ckeditor5-dev-utils`](/packages/ckeditor5-dev-utils) | [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-dev-utils.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils) | | [`@ckeditor/ckeditor5-dev-translations`](/packages/ckeditor5-dev-translations) | [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-dev-translations.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations) | | [`@ckeditor/ckeditor5-dev-web-crawler`](/packages/ckeditor5-dev-web-crawler) | [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-dev-web-crawler.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler) | -| [`@ckeditor/jsdoc-plugins`](/packages/jsdoc-plugins) | [![npm version](https://badge.fury.io/js/%40ckeditor%2Fjsdoc-plugins.svg)](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins) | -| [`@ckeditor/typedoc-plugins`](/packages/typedoc-plugins) | [![npm version](https://badge.fury.io/js/%40ckeditor%2Ftypedoc-plugins.svg)](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins) | +| [`@ckeditor/typedoc-plugins`](/packages/typedoc-plugins) | [![npm version](https://badge.fury.io/js/%40ckeditor%2Ftypedoc-plugins.svg)](https://www.npmjs.com/package/@ckeditor/typedoc-plugins) | ## Cloning diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index dc6932645..82a2afbd9 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -3,43 +3,92 @@ * For licensing, see LICENSE.md. */ +import glob from 'fast-glob'; +import TypeDoc from 'typedoc'; +import typedocPlugins from '@ckeditor/typedoc-plugins'; + +import validators from './validators/index.js'; + /** - * Builds CKEditor 5 documentation. + * Builds CKEditor 5 documentation using `typedoc`. * - * @param {JSDocConfig|TypedocConfig} config + * @param {TypedocConfig} config * @returns {Promise} */ export default async function build( config ) { - const type = config.type || 'jsdoc'; - - if ( type === 'jsdoc' ) { - return ( await import( './buildjsdoc.js' ) ).default( config ); - } else if ( type === 'typedoc' ) { - return ( await import( './buildtypedoc.js' ) ).default( config ); - } else { - throw new Error( `Unknown documentation tool (${ type }).` ); + const { plugins } = typedocPlugins; + const sourceFilePatterns = config.sourceFiles.filter( Boolean ); + const strictMode = config.strict || false; + const extraPlugins = config.extraPlugins || []; + const validatorOptions = config.validatorOptions || {}; + + const files = await glob( sourceFilePatterns ); + const typeDoc = new TypeDoc.Application(); + + typeDoc.options.addReader( new TypeDoc.TSConfigReader() ); + typeDoc.options.addReader( new TypeDoc.TypeDocReader() ); + + typeDoc.bootstrap( { + tsconfig: config.tsconfig, + excludeExternals: true, + entryPoints: files, + logLevel: 'Warn', + basePath: config.cwd, + blockTags: [ + '@eventName', + '@default' + ], + inlineTags: [ + '@link', + '@glink' + ], + modifierTags: [ + '@publicApi', + '@skipSource', + '@internal' + ], + plugin: [ + // Fixes `"name": 'default" in the output project. + 'typedoc-plugin-rename-defaults', + + plugins[ 'typedoc-plugin-module-fixer' ], + plugins[ 'typedoc-plugin-symbol-fixer' ], + plugins[ 'typedoc-plugin-interface-augmentation-fixer' ], + plugins[ 'typedoc-plugin-tag-error' ], + plugins[ 'typedoc-plugin-tag-event' ], + plugins[ 'typedoc-plugin-tag-observable' ], + plugins[ 'typedoc-plugin-purge-private-api-docs' ], + + // The `event-inheritance-fixer` plugin must be loaded after `tag-event` plugin, as it depends on its output. + plugins[ 'typedoc-plugin-event-inheritance-fixer' ], + + // The `event-param-fixer` plugin must be loaded after `tag-event` and `tag-observable` plugins, as it depends on their output. + plugins[ 'typedoc-plugin-event-param-fixer' ], + + ...extraPlugins + ] + } ); + + console.log( 'Typedoc started...' ); + + const conversionResult = typeDoc.convert(); + + if ( !conversionResult ) { + throw 'Something went wrong with TypeDoc.'; } -} -/** - * @typedef {Object} JSDocConfig - * - * @property {'jsdoc'} type - * - * @property {Array.} sourceFiles Glob pattern with source files. - * - * @property {String} readmePath Path to `README.md`. - * - * @property {Boolean} [validateOnly=false] Whether JSDoc should only validate the documentation and finish - * with error code `1`. If not passed, the errors will be printed to the console but the task will finish with `0`. - * - * @property {Boolean} [strict=false] If `true`, errors found during the validation will finish the process - * and exit code will be changed to `1`. - * - * @property {String} [outputPath='docs/api/output.json'] A path to the place where extracted doclets will be saved. - * - * @property {String} [extraPlugins=[]] An array of path to extra plugins that will be added to JSDoc. - */ + const validationResult = validators.validate( conversionResult, typeDoc, validatorOptions ); + + if ( !validationResult && strictMode ) { + throw 'Something went wrong with TypeDoc.'; + } + + if ( config.outputPath ) { + await typeDoc.generateJson( conversionResult, config.outputPath ); + } + + console.log( `Documented ${ files.length } files!` ); +} /** * @typedef {Object} TypedocConfig diff --git a/packages/ckeditor5-dev-docs/lib/buildjsdoc.js b/packages/ckeditor5-dev-docs/lib/buildjsdoc.js deleted file mode 100644 index def803965..000000000 --- a/packages/ckeditor5-dev-docs/lib/buildjsdoc.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import { createRequire } from 'module'; -import fs from 'fs-extra'; -import tmp from 'tmp'; -import glob from 'fast-glob'; -import { tools } from '@ckeditor/ckeditor5-dev-utils'; - -const require = createRequire( import.meta.url ); - -/** - * Builds CKEditor 5 documentation using `jsdoc`. - * - * @param {JSDocConfig} config - * @returns {Promise} - */ -export default async function build( config ) { - const sourceFilePatterns = [ - config.readmePath, - ...config.sourceFiles - ]; - - const extraPlugins = config.extraPlugins || []; - const outputPath = config.outputPath || 'docs/api/output.json'; - const validateOnly = config.validateOnly || false; - const strictCheck = config.strict || false; - - // Pass options to plugins via env variables. - // Since plugins are added using `require` calls other forms are currently impossible. - process.env.JSDOC_OUTPUT_PATH = outputPath; - - if ( validateOnly ) { - process.env.JSDOC_VALIDATE_ONLY = 'true'; - } - - if ( strictCheck ) { - process.env.JSDOC_STRICT_CHECK = 'true'; - } - - const files = await glob( sourceFilePatterns ); - - const jsDocConfig = { - plugins: [ - require.resolve( 'jsdoc/plugins/markdown' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/purge-private-api-docs' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/export-fixer/export-fixer' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/custom-tags/error' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/custom-tags/observable' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/observable-event-provider' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/longname-fixer/longname-fixer' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/fix-code-snippets' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/relation-fixer' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/event-extender/event-extender' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/cleanup' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/validator/validator' ), - require.resolve( '@ckeditor/jsdoc-plugins/lib/utils/doclet-logger' ), - ...extraPlugins - ], - source: { - include: files - }, - opts: { - encoding: 'utf8', - recurse: true, - access: 'all', - template: 'templates/silent' - } - }; - - const tmpConfig = tmp.fileSync(); - - await fs.writeFile( tmpConfig.name, JSON.stringify( jsDocConfig ) ); - - console.log( 'JSDoc started...' ); - - try { - // Not so beautiful API as for 2020... - // See more in https://github.com/jsdoc/jsdoc/issues/938. - const cmd = require.resolve( 'jsdoc/jsdoc.js' ); - - // The `node` command is used for explicitly needed for Windows. - // See https://github.com/ckeditor/ckeditor5/issues/7212. - tools.shExec( `node ${ cmd } -c ${ tmpConfig.name }`, { verbosity: 'info' } ); - } catch ( error ) { - console.error( 'An error was thrown by JSDoc:' ); - - throw error; - } - - console.log( `Documented ${ files.length } files!` ); -} diff --git a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js b/packages/ckeditor5-dev-docs/lib/buildtypedoc.js deleted file mode 100644 index e952b2cbd..000000000 --- a/packages/ckeditor5-dev-docs/lib/buildtypedoc.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import glob from 'fast-glob'; -import TypeDoc from 'typedoc'; -import typedocPlugins from '@ckeditor/typedoc-plugins'; - -import validators from './validators/index.js'; - -/** - * Builds CKEditor 5 documentation using `typedoc`. - * - * @param {TypedocConfig} config - * @returns {Promise} - */ -export default async function build( config ) { - const { plugins } = typedocPlugins; - const sourceFilePatterns = config.sourceFiles.filter( Boolean ); - const strictMode = config.strict || false; - const extraPlugins = config.extraPlugins || []; - const validatorOptions = config.validatorOptions || {}; - - const files = await glob( sourceFilePatterns ); - const typeDoc = new TypeDoc.Application(); - - typeDoc.options.addReader( new TypeDoc.TSConfigReader() ); - typeDoc.options.addReader( new TypeDoc.TypeDocReader() ); - - typeDoc.bootstrap( { - tsconfig: config.tsconfig, - excludeExternals: true, - entryPoints: files, - logLevel: 'Warn', - basePath: config.cwd, - blockTags: [ - '@eventName', - '@default' - ], - inlineTags: [ - '@link', - '@glink' - ], - modifierTags: [ - '@publicApi', - '@skipSource', - '@internal' - ], - plugin: [ - // Fixes `"name": 'default" in the output project. - 'typedoc-plugin-rename-defaults', - - plugins[ 'typedoc-plugin-module-fixer' ], - plugins[ 'typedoc-plugin-symbol-fixer' ], - plugins[ 'typedoc-plugin-interface-augmentation-fixer' ], - plugins[ 'typedoc-plugin-tag-error' ], - plugins[ 'typedoc-plugin-tag-event' ], - plugins[ 'typedoc-plugin-tag-observable' ], - plugins[ 'typedoc-plugin-purge-private-api-docs' ], - - // The `event-inheritance-fixer` plugin must be loaded after `tag-event` plugin, as it depends on its output. - plugins[ 'typedoc-plugin-event-inheritance-fixer' ], - - // The `event-param-fixer` plugin must be loaded after `tag-event` and `tag-observable` plugins, as it depends on their output. - plugins[ 'typedoc-plugin-event-param-fixer' ], - - ...extraPlugins - ] - } ); - - console.log( 'Typedoc started...' ); - - const conversionResult = typeDoc.convert(); - - if ( !conversionResult ) { - throw 'Something went wrong with TypeDoc.'; - } - - const validationResult = validators.validate( conversionResult, typeDoc, validatorOptions ); - - if ( !validationResult && strictMode ) { - throw 'Something went wrong with TypeDoc.'; - } - - if ( config.outputPath ) { - await typeDoc.generateJson( conversionResult, config.outputPath ); - } - - console.log( `Documented ${ files.length } files!` ); -} diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index 38f278950..bd214341d 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -23,11 +23,9 @@ ], "dependencies": { "@ckeditor/ckeditor5-dev-utils": "^43.0.0", - "@ckeditor/jsdoc-plugins": "^43.0.0", "@ckeditor/typedoc-plugins": "^43.0.0", "fast-glob": "^3.2.4", "fs-extra": "^11.2.0", - "jsdoc": "ckeditor/jsdoc#fixed-trailing-comment-doclets", "tmp": "^0.2.1", "typedoc": "^0.23.15", "typedoc-plugin-rename-defaults": "0.6.6" diff --git a/packages/jsdoc-plugins/CHANGELOG.md b/packages/jsdoc-plugins/CHANGELOG.md deleted file mode 100644 index 36118c29a..000000000 --- a/packages/jsdoc-plugins/CHANGELOG.md +++ /dev/null @@ -1,250 +0,0 @@ -Changelog -========= - -All changes in the package are documented in the main repository. See: https://github.com/ckeditor/ckeditor5-dev/blob/master/CHANGELOG.md. - -Changes for the past releases are available below. - -## [3.0.9](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.8...@ckeditor/jsdoc-plugins@3.0.9) (2020-02-26) - -Internal changes only (updated dependencies, documentation, etc.). - - -## [3.0.8](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.7...@ckeditor/jsdoc-plugins@3.0.8) (2020-01-09) - -Internal changes only (updated dependencies, documentation, etc.). - - -## [3.0.7](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.6...@ckeditor/jsdoc-plugins@3.0.7) (2019-09-09) - -### Other changes - -* Adjusted dev tools to work with "ckeditor-" prefix. See [ckeditor/ckeditor5#924](https://github.com/ckeditor/ckeditor5/issues/924). ([75b226f](https://github.com/ckeditor/ckeditor5-dev/commit/75b226f)) - - -## [3.0.6](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.5...@ckeditor/jsdoc-plugins@3.0.6) (2019-09-02) - -### Bug fixes - -* JSDoc validator will end with proper exit code if, during the validation, any error occurred. Closes [#550](https://github.com/ckeditor/ckeditor5-dev/issues/550). ([30f02e7](https://github.com/ckeditor/ckeditor5-dev/commit/30f02e7)) - - -## [3.0.5](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.4...@ckeditor/jsdoc-plugins@3.0.5) (2019-08-12) - -### Other changes - -* The doclet validator is improved. Now, during parameters linting it logs an error if the doclet cannot be a valid type (e.g. a method, property or a mixin cannot be a valid type). It also checks now whether the [@extends](https://github.com/extends) tag points to the valid reference. Closes [#507](https://github.com/ckeditor/ckeditor5-dev/issues/507). ([c5a8b08](https://github.com/ckeditor/ckeditor5-dev/commit/c5a8b08)) - - -## [3.0.4](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.3...@ckeditor/jsdoc-plugins@3.0.4) (2019-08-09) - -### Other changes - -* Added `ErrorEvent` and `PromiseRejectionEvent` types to the JSDoc validator. Part of [ckeditor/ckeditor5-watchdog#3](https://github.com/ckeditor/ckeditor5-watchdog/issues/3). ([799b272](https://github.com/ckeditor/ckeditor5-dev/commit/799b272)) - - -## [3.0.3](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.2...@ckeditor/jsdoc-plugins@3.0.3) (2019-07-15) - -### Other changes - -* Upgraded dependencies for most of the packages. Merged Lerna + Yarn and they can work together now. Closes [#527](https://github.com/ckeditor/ckeditor5-dev/issues/527). Closes [#466](https://github.com/ckeditor/ckeditor5-dev/issues/466). ([dcc3215](https://github.com/ckeditor/ckeditor5-dev/commit/dcc3215)) - - -## [3.0.2](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.1...@ckeditor/jsdoc-plugins@3.0.2) (2019-07-09) - -### Other changes - -* Enabled doclets generation out of typedef properties. Closes [#504](https://github.com/ckeditor/ckeditor5-dev/issues/504). ([2f480d1](https://github.com/ckeditor/ckeditor5-dev/commit/2f480d1)) - - -## [3.0.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@3.0.0...@ckeditor/jsdoc-plugins@3.0.1) (2019-02-28) - -Internal changes only (updated dependencies, documentation, etc.). - - -## [3.0.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@2.1.1...@ckeditor/jsdoc-plugins@3.0.0) (2019-02-19) - -### Bug fixes - -* Fixed inheritance mechanism and filtered out internal doclets. Closes [#465](https://github.com/ckeditor/ckeditor5-dev/issues/465). ([04a147b](https://github.com/ckeditor/ckeditor5-dev/commit/04a147b)) - -### BREAKING CHANGES - -* Upgraded minimal versions of Node to `8.0.0` and npm to `5.7.1`. See: [ckeditor/ckeditor5#1507](https://github.com/ckeditor/ckeditor5/issues/1507). ([612ea3c](https://github.com/ckeditor/ckeditor5-cloud-services/commit/612ea3c)) - - -## [2.1.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@2.1.0...@ckeditor/jsdoc-plugins@2.1.1) (2019-02-12) - -Internal changes only (updated dependencies, documentation, etc.). - - -## [2.1.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@2.0.2...@ckeditor/jsdoc-plugins@2.1.0) (2018-10-09) - -### Features - -* Added missing `@link` part validation to JSDoc validation. Closes [#433](https://github.com/ckeditor/ckeditor5-dev/issues/433). ([41c3014](https://github.com/ckeditor/ckeditor5-dev/commit/41c3014)) - - -## [2.0.2](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@2.0.1...@ckeditor/jsdoc-plugins@2.0.2) (2018-10-02) - -### Other changes - -* Improved performance of the `relation-fixer` plugin by the factor of 10x. Closes [#438](https://github.com/ckeditor/ckeditor5-dev/issues/438). ([ef1dc21](https://github.com/ckeditor/ckeditor5-dev/commit/ef1dc21)) - - -## [2.0.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@2.0.0...@ckeditor/jsdoc-plugins@2.0.1) (2018-09-24) - -Internal changes only (updated dependencies, documentation, etc.). - - -## [2.0.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.7...@ckeditor/jsdoc-plugins@2.0.0) (2018-08-23) - -Updated required Node.js version to `>=6.9.0`. - - -## [1.9.7](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.6...@ckeditor/jsdoc-plugins@1.9.7) (2018-07-17) - -Internal changes only (updated dependencies, documentation, etc.). - - -## [1.9.6](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.5...@ckeditor/jsdoc-plugins@1.9.6) (2018-07-03) - -### Other changes - -* Added FocusEvent to known types. ([9dd3b90](https://github.com/ckeditor/ckeditor5-dev/commit/9dd3b90)) - - -## [1.9.5](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.4...@ckeditor/jsdoc-plugins@1.9.5) (2018-06-28) - -### Other changes - -* Added NodeList to known types. ([158f60b](https://github.com/ckeditor/ckeditor5-dev/commit/158f60b)) - - -## [1.9.4](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.3...@ckeditor/jsdoc-plugins@1.9.4) (2018-03-27) - -Internal changes only (updated dependencies, documentation, etc.). - - -## [1.9.3](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.2...@ckeditor/jsdoc-plugins@1.9.3) (2018-02-20) - -### Bug fixes - -* Improved links detection and validation. Closes [#365](https://github.com/ckeditor/ckeditor5-dev/issues/365). ([00d5b10](https://github.com/ckeditor/ckeditor5-dev/commit/00d5b10)) - - -## [1.9.2](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.1...@ckeditor/jsdoc-plugins@1.9.2) (2018-02-16) - -### Bug fixes - -* Fixed inheritance in event doclets for observables. Closes [#368](https://github.com/ckeditor/ckeditor5-dev/issues/368). ([3fc1acc](https://github.com/ckeditor/ckeditor5-dev/commit/3fc1acc)) - - -## [1.9.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.9.0...@ckeditor/jsdoc-plugins@1.9.1) (2018-02-12) - -### Bug fixes - -* Fixed JSDoc relation-fixer plugin to not include interfaces in inheritance hierarchy. Closes [#361](https://github.com/ckeditor/ckeditor5-dev/issues/361). ([e46efbf](https://github.com/ckeditor/ckeditor5-dev/commit/e46efbf) and [d22fd7d](https://github.com/ckeditor/ckeditor5-dev/commit/d22fd7d)) - - -## [1.9.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.8.3...@ckeditor/jsdoc-plugins@1.9.0) (2018-01-22) - -### Features - -* JSDoc: Added support for using `@extends` with `@typedef`s. Closes [#355](https://github.com/ckeditor/ckeditor5-dev/issues/355). ([a3dd8c5](https://github.com/ckeditor/ckeditor5-dev/commit/a3dd8c5)) - - -## [1.8.3](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.8.2...@ckeditor/jsdoc-plugins@1.8.3) (2017-11-28) - -### Bug fixes - -* Improved linting the `memberof` property. Closes [ckeditor/ckeditor5-utils#209](https://github.com/ckeditor/ckeditor5-utils/issues/209). ([b57fe29](https://github.com/ckeditor/ckeditor5-dev/commit/b57fe29)) - - -## [1.8.2](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.8.1...@ckeditor/jsdoc-plugins@1.8.2) (2017-11-24) - -### Bug fixes - -* Optimized doclet validator plugin. Closes [#306](https://github.com/ckeditor/ckeditor5-dev/issues/306). ([664fb2d](https://github.com/ckeditor/ckeditor5-dev/commit/664fb2d)) -* Optimized Relation Fixer plugin. Closes [#312](https://github.com/ckeditor/ckeditor5-dev/issues/312). ([f575d77](https://github.com/ckeditor/ckeditor5-dev/commit/f575d77)) - - -## [1.8.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.8.0...@ckeditor/jsdoc-plugins@1.8.1) (2017-11-10) - -### Bug fixes - -* Add access property to events generated from observables. Closes [#300](https://github.com/ckeditor/ckeditor5-dev/issues/300). ([a80fe6e](https://github.com/ckeditor/ckeditor5-dev/commit/a80fe6e)) - - -## [1.8.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.7.1...@ckeditor/jsdoc-plugins@1.8.0) (2017-10-10) - -### Features - -* Provided support for `@observable` tag. Closes [#285](https://github.com/ckeditor/ckeditor5-dev/issues/285). ([fed57af](https://github.com/ckeditor/ckeditor5-dev/commit/fed57af)) - - -## [1.7.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.7.0...@ckeditor/jsdoc-plugins@1.7.1) (2017-10-01) - -Internal changes only (updated dependencies, documentation, etc.). - -## [1.7.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.6.1...@ckeditor/jsdoc-plugins@1.7.0) (2017-09-20) - -### Features - -* Provided `[@error](https://github.com/error)` tag detection in JSDoc. Closes [#280](https://github.com/ckeditor/ckeditor5-dev/issues/280). ([b7a0cf5](https://github.com/ckeditor/ckeditor5-dev/commit/b7a0cf5)) - - -## [1.6.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.6.0...@ckeditor/jsdoc-plugins@1.6.1) (2017-08-23) - -### Bug fixes - -* Remove duplicates in JSDoc relation-fixer plugin. Closes [#261](https://github.com/ckeditor/ckeditor5-dev/issues/261). ([e2a921a](https://github.com/ckeditor/ckeditor5-dev/commit/e2a921a)) - - -## [1.6.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.5.2...@ckeditor/jsdoc-plugins@1.6.0) (2017-08-18) - -### Features - -* All events will be automatically extended with `EventInfo` parameter. Closes [#257](https://github.com/ckeditor/ckeditor5-dev/issues/257). ([3968bd0](https://github.com/ckeditor/ckeditor5-dev/commit/3968bd0)) - - -## [1.5.2](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.5.0...@ckeditor/jsdoc-plugins@1.5.2) (2017-08-16) - -### Other changes - -* Added Window type to JSDoc validator. Closes [#250](https://github.com/ckeditor/ckeditor5-dev/issues/250). ([fdec4a0](https://github.com/ckeditor/ckeditor5-dev/commit/fdec4a0)) - - -## [1.5.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.4.0...@ckeditor/jsdoc-plugins@1.5.0) (2017-06-20) - -### Features - -* Static and mixed method, properties and events will be inherited too. Closes [#226](https://github.com/ckeditor/ckeditor5-dev/issues/226). ([a160840](https://github.com/ckeditor/ckeditor5-dev/commit/a160840)) - - -## [1.4.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.3.1...@ckeditor/jsdoc-plugins@1.4.0) (2017-05-29) - -### Features - -* Allow breaking the process if API docs validation fails. Closes [#221](https://github.com/ckeditor/ckeditor5-dev/issues/221). ([e2f0dec](https://github.com/ckeditor/ckeditor5-dev/commit/e2f0dec)) - - -## [1.3.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.3.0...@ckeditor/jsdoc-plugins@1.3.1) (2017-05-24) - -Internal changes only (updated dependencies, documentation, etc.). - -## [1.3.0](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.2.1...@ckeditor/jsdoc-plugins@1.3.0) (2017-05-18) - -### Features - -* Introduced JSDoc plugin enabling inheritance of static members. Closes [#201](https://github.com/ckeditor/ckeditor5-dev/issues/201). ([08c0a21](https://github.com/ckeditor/ckeditor5-dev/commit/08c0a21)) - - -## [1.2.1](https://github.com/ckeditor/ckeditor5-dev/compare/@ckeditor/jsdoc-plugins@1.2.0...@ckeditor/jsdoc-plugins@1.2.1) (2017-04-27) - -Internal changes only (updated dependencies, documentation, etc.). - - -## 1.2.0 - -The big bang. diff --git a/packages/jsdoc-plugins/LICENSE.md b/packages/jsdoc-plugins/LICENSE.md deleted file mode 100644 index e536047e7..000000000 --- a/packages/jsdoc-plugins/LICENSE.md +++ /dev/null @@ -1,39 +0,0 @@ -Software License Agreement -========================== - -@ckeditor/jsdoc-plugins – Various JSDoc plugins developed by the CKEditor 5 team. -Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - -Licensed under the terms of the MIT License (see Appendix A): - - http://en.wikipedia.org/wiki/MIT_License - -Sources of Intellectual Property Included in CKEditor ------------------------------------------------------ - -Where not otherwise indicated, all @ckeditor/jsdoc-plugins content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, @ckeditor/jsdoc-plugins will incorporate work done by developers outside of CKSource with their express permission. - -Appendix A: The MIT License ---------------------------- - -The MIT License (MIT) - -Copyright (c) 2003-2024, CKSource Holding sp. z o.o. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/packages/jsdoc-plugins/README.md b/packages/jsdoc-plugins/README.md deleted file mode 100644 index ee2e2f224..000000000 --- a/packages/jsdoc-plugins/README.md +++ /dev/null @@ -1,217 +0,0 @@ -# JSDoc plugins overview - -[![npm version](https://badge.fury.io/js/%40ckeditor%2Fjsdoc-plugins.svg)](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins) -[![CircleCI](https://circleci.com/gh/ckeditor/ckeditor5-dev.svg?style=shield)](https://app.circleci.com/pipelines/github/ckeditor/ckeditor5-dev?branch=master) - -## Overview - -This repository consists of few plugins which extend the capabilities of [JSDoc](https://github.com/jsdoc3/jsdoc). - -The list of generic plugins: - -* `lib/validator/validator.js` - validates if references to types used in params lists or property types are defined elsewhere, whether links point to existing properties/classes/modules, etc. -* `lib/export-fixer/export-fixer.js` - fixes an error with `export default` syntax -* `lib/custom-tags/error.js` - provides support for the custom `@error` tag -* `lib/relation-fixer.js` - fixes problem with inheritance (extends child classes with properties from parent classes) -* `lib/longname-fixer/longname-fixer.js` - enables short notation in links -* `lib/utils/doclet-logger.js` - enables logging output into the `/docs/api/output.json` - -CKEditor 5 specific plugins - -* `lib/event-extender/event-extender.js` - CKEditor 5 specific util that inserts parameter to all events. -* `lib/custom-tags/observable.js` - CKEditor 5 specific tag for observable properties -* `lib/observable-event-provider.js` - CKEditor 5 specific - -## Usage - -### JSDoc configuration - -To enable above plugins you need to list them in the `plugins` array of JSDoc config file. - -```json -{ - "plugins": [ - "node_modules/@ckeditor/jsdoc-plugins/lib/validator/validator", - "node_modules/@ckeditor/jsdoc-plugins/lib/longname-fixer/longname-fixer" - ], - // ... -} -``` - -Then, having the https://github.com/jsdoc3/jsdoc installed, the only thing to do is to call the command with the `-c` flag pointing to the configuration file. - -```bash -jsdoc -c path/to/config.json -``` - -## Types of references - -The reference system is improved by the `lib/longname-fixer/longname-fixer.js` plugin. - -### Overview - -There're 2 types of references available inside JSDoc comments. First of them are called `Full references`. They work everywhere but the con of using them are long names. The second ones are short references that can be used to point symbols that are present in the same file. - -### Full references - -Full references start with `module:`. The first part of that syntax needs to match the `@module` tag in the corresponding class. - -Here are a few examples: - -* `@param {module:command~Command}` -* `{@link module:core/editor/editor~Editor editor}` -* `@member {module:core/editor/editor~Editor}` - -### Short references - -There are two types of short references available. - -References starting with `#` are available for members methods and events, e.g. `{@link #create}`. These refs can be used to point members and methods inside the same class or interface. - -References starting with `~` are available to point classes and interfaces, e.g. `{@link ~Editor}`. **There's a known issue with pointing things that are declared later, the full ref should be used then instead.** - -```js -class Editor { - /** - * This property represents {@link ~Editor editor} name. - * - * @member {String} #name - */ - - /** - * This property represents editor version. - * See editor {@link #name name}. - * - * @member {String} #version - */ - - /** - * Get version of the editor - * Note - '@method getVersion' isn't needed here because the method is declared. - */ - getVersion() { - return this.version; - } -} -``` - - -## Events - -Events can be assigned to the classes. - -The `@fires` and `@event` tags work well with full references and with short references to the current `class` / `interface` / `mixin`. - -Events can be declared in class bodies and after them (short references are available in both cases). - -Full reference to the events must have the following syntax: `module:somemodule~SomeClass#event:eventName`. Note that `event:` part is required here. -Short reference can be either `event:eventName` or `eventName`. - -```js -class FocusTracker { - /** - * @private - * @fires blur - */ - _blur() { } - - /** - * @fires focus - * @fires module:focustracker~FocusTracker#event:focus // this will link to the same event as above - */ - - /** - * @event blur - */ -} - -// Note: the following event will be bound to the `FocusTracker` class. -/** - * @event focus - */ -``` - -## Validation - -### Overview - -The `lib/validator/validator.js` plugin is supposed to validate references and check types in JSDoc comments. - -Note that the `@module` tag is required on top of each file to make the validation possible. Without that tag, the validator will complain about it with the message: `Memberof property should start with 'module:'`. - -During the JSDoc compilation, the errors are thrown to the standard output for each invalid reference or type. - -### Validated tags - -References for the following tags are validated: - -* `@link` - matches *references* -* `@member` - matches *types* -* `@fires` – matches *events* -* `@param` - matches *types* -* `@implements` – matches *interfaces* -* `@returns` - matches *types* -* `@see` - matches *references* - -### Available types - -There're few kind of types available with following syntax: - -* Wildcard: `*`. -* Basic types: basic ES and DOM types, e.g. `String`, `Boolean`, `Error`, `Node`. -* String literal types: `'someString'`. -* Union types: `Type1|Type2`, `Type1|Type2|Type3`, etc. -* Generic types: `GenericType.`, `GenericType`, etc. -* References: declared interfaces, objects, events, etc. E.g. `module:somemodule~SomeInteface`. - -These types can be used together, e.g.: - -```js -/** - * @param {Map.<*>} map - * @param {'left'|'right'} direction - * @param {Object.} dictionary - * @returns {Array.} - */ -``` - -Basic types and generic types are listed in the `jsdoc/validator/types.js`. - -## TODO - -### Unsupported short references - -For now, there are still few unsupported tags, which require full references: - -* `@param` -* `@typedef` - -### Missing validation - -There're also tags which are not validated: - -* `@mixes` -* `@augments` / `@extends` - -## Inheritance of class members - -This feature is implemented by the `lib/relation-fixer/index.js` plugin. - -### Overview - -As of version 3.4.3 JSDoc [does not support inheritance of static members](https://github.com/jsdoc3/jsdoc/issues/1229). - -If class B extends class A and class A has a static property, JSDoc will not output documentation of that static property to documentation of class B. - -The `lib/relation-fixer` plugin checks for such cases and updates documentation of child classes with documentation of inherited, implemented or mixed static members. - -It also adds additional properties to doclets of classes, interfaces, and mixins to show related doclets. These properties are: - -* `augmentsNested` - an array of references to all parent classes, -* `implementsNested` - an array of references to implemented interfaces, -* `mixesNested` - an array of references to mixed mixins, -* `descendants` - an array of references to entities which implement, mix or extend the doclet. - -## Changelog - -See the [`CHANGELOG.md`](https://github.com/ckeditor/ckeditor5-dev/blob/master/packages/jsdoc-plugins/CHANGELOG.md) file. diff --git a/packages/jsdoc-plugins/lib/cleanup.js b/packages/jsdoc-plugins/lib/cleanup.js deleted file mode 100644 index a34a6cf99..000000000 --- a/packages/jsdoc-plugins/lib/cleanup.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -const { isEqual } = require( 'lodash' ); - -exports.handlers = { - processingComplete( e ) { - const knownDoclets = new Map(); - - for ( const doclet of e.doclets ) { - if ( doclet.name && doclet.name.startsWith( '#' ) ) { - doclet.name = doclet.name.slice( 1 ); - } - - if ( doclet.longname.includes( '#' ) ) { - doclet.scope = 'instance'; - } - - // Remove duplicates. - if ( knownDoclets.has( doclet.longname ) && isEqual( doclet, knownDoclets.get( doclet.longname ) ) ) { - doclet.ignore = true; - } - - knownDoclets.set( doclet.longname, doclet ); - - // Delete props that we don't use. - delete doclet.overrides; - } - - /** @type {Map.>} */ - const docletMap = new Map(); - - for ( const doclet of e.doclets ) { - const otherDocletsWithSameName = docletMap.get( doclet.longname ); - - if ( !otherDocletsWithSameName ) { - docletMap.set( doclet.longname, [ doclet ] ); - } else { - docletMap.set( doclet.longname, [ ...otherDocletsWithSameName, doclet ] ); - } - } - - // Delete doclets that are inherited and should be overwritten. - for ( const doclets of docletMap.values() ) { - const inheritedDoclets = doclets.filter( doclet => doclet.inherited ); - - if ( doclets.find( doclet => !doclet.inherited && !doclet.ignore ) ) { - for ( const d of inheritedDoclets ) { - d.ignore = true; - } - } - } - - e.doclets = e.doclets - // Filter out the package doclet. - .filter( doclet => doclet.kind !== 'package' ) - .filter( doclet => { - // Filter out doclet that should be ignored. - if ( doclet.ignore ) { - return false; - } - - // Filter out undocumented doclets. - if ( doclet.undocumented ) { - return ( - !!doclet.description || - !!doclet.comment || - !!doclet.classdesc - ); - } - - return true; - } ); - } -}; diff --git a/packages/jsdoc-plugins/lib/custom-tags/error.js b/packages/jsdoc-plugins/lib/custom-tags/error.js deleted file mode 100644 index 9f708ea0e..000000000 --- a/packages/jsdoc-plugins/lib/custom-tags/error.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -module.exports = { - // See http://usejsdoc.org/about-plugins.html#tag-definition. - defineTags( dictionary ) { - dictionary.defineTag( 'error', { - mustHaveValue: true, - mustNotHaveDescription: false, - canHaveType: true, - canHaveName: true, - onTagged( doclet, tag ) { - Object.assign( doclet, { - name: tag.value.name, - longname: `module:errors/${ tag.value.name }`, - kind: 'error', - memberof: 'module:errors', - scope: 'inner' - } ); - } - } ); - } -}; diff --git a/packages/jsdoc-plugins/lib/custom-tags/observable.js b/packages/jsdoc-plugins/lib/custom-tags/observable.js deleted file mode 100644 index 1de60f9ec..000000000 --- a/packages/jsdoc-plugins/lib/custom-tags/observable.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -module.exports = { - // See http://usejsdoc.org/about-plugins.html#tag-definitions. - defineTags( dictionary ) { - dictionary.defineTag( 'observable', { - onTagged( doclet ) { - doclet.observable = true; - } - } ); - } -}; diff --git a/packages/jsdoc-plugins/lib/doclet.jsdoc b/packages/jsdoc-plugins/lib/doclet.jsdoc deleted file mode 100644 index 0d94bce5a..000000000 --- a/packages/jsdoc-plugins/lib/doclet.jsdoc +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * Licensed under the terms of the MIT License (see LICENSE.md). - */ - -/** - * Doclets are atomic parts of the JSDoc output that describe each symbol of parsed input. - * - * @typedef {Object} Doclet - * - * @property {String} longname Long name of the doclet (including the `module:` part). - * - * @property {String} name Short name of the doclet (e.g. the name of the method). - * - * @property {Type} [type] A type of the member. - * - * @property {Array.} [returns] The metadata for the function or method return type and description. - * - * @property {String} memberof Where the doclet belongs to (parent of the symbol). - * - * @property {'class'|'interface'|'mixin'|'function'|'typedef'|'event'|'member'|'constant'|'module'|'error'} kind - * The kind of the doclet's symbol. - * - * @property {Boolean} [ignore] `true` for internal doclets which should not be published - * - * @property {Boolean} [undocumented] `true` when a doclet's symbol does not have API docs written above the declaration. - * - * @property {String} [inheritdoc] Specifies whether the `@inheritdoc` tag is present. Warning: When the `@inheritdoc` tag is - * present without no additional docs, then this property becomes an empty string! - * - * @property {Boolean} [overrides] - * - * @property {Array.} [properties] Typedef properties. - * - * @property {Boolean} [inherited] `true` for a property / method which is inherited from the parent class. - * - * @property {String} [inherits] The longname of the parent class / interface method. - * - * @property {String} [comment] Raw API comment. - * - * @property {String} [description] API Comment wrapped in HTML tags. - * - * @property {String} [classdesc] Description for the class. - * - * @property {Array.} [see] `@see {}` tag contents. - * - * @property {Array.} [params] Event and function params. - * - * @property {'instance'|'inner'|'static'|'global'} [scope] - * - * @property {Array.} [fires] An array of events that a method or a property can fire. - * - * @property {{ filename: String; lineno: Number; path: String }} meta Doclet's metadata - filename, line number, etc. - * - * @property {Array.} [augments] An array of classes that the doclet's symbol extends. - * Applies for `@class`, `@mixin`, `@interface`. - * - * @property {Array.} [mixes] An array of mixins that the doclet's symbol mixes. - * Applies for `@class` and `@mixin`. - * - * @property {Boolean} [mixed] `true` when a property or method comes from a mixin. - * - * @property {Array.} [implements] An array of interfaces that the doclet's symbol implements. - * Applies for `@class` and `@mixin`. - * - * @property {Array.} [augmentsNested] [A custom property used by the relation fixer] - - * an array of all classes in the inheritance chain augmenting the current doclet's symbol. - * - * @property {Array.} [mixesNested] [A custom property used by the relation fixer] - - * an array of all mixins in the inheritance chain augmenting the current doclet's symbol. - * - * @property {Array.} [implementsNested] [A custom property used by the relation fixer] - - * an array of all interfaces in the inheritance chain, which the current doclet's symbol implements. - * - * @property {Array.} [descendants] [A custom property used by the relation fixer] - - * an array of doclets which inherits / implements / mixes the doclet's symbol. - */ - -/** - * @typedef {Object} Property - * - * @property {String} name - * - * @property {Type} type - * - * @property {String} [description] - * - * @property {Boolean} [inherited] [A custom property used by the relation fixer] - */ - -/** - * @typedef {Object} Type - * - * @property {Array.} names -*/ - -/** - * @typedef {Object} ReturnData - * - * @property {Type} type Type of the return statement. I.e. what the function should return. - * It's gathered from the text after the `@returns` tag. - * - * @property {String} [description] Description for the return statement (after the `@returns` tag and type). - */ - -/** - * @typedef {Object} Parameter - * - * @property {Type} type Type of the parameter. - * - * @property {String} name Name of the paramter. - * - * @property {String} [description] Description of the parameter. - */ diff --git a/packages/jsdoc-plugins/lib/event-extender/event-extender.js b/packages/jsdoc-plugins/lib/event-extender/event-extender.js deleted file mode 100644 index 8d2768fe2..000000000 --- a/packages/jsdoc-plugins/lib/event-extender/event-extender.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -module.exports = { - handlers: { - /** - * @see http://usejsdoc.org/about-plugins.html#event-newdoclet - * @param evt - */ - newDoclet( evt ) { - const { doclet } = evt; - - if ( doclet.kind !== 'event' ) { - return; - } - - if ( !doclet.params ) { - doclet.params = []; - } - - doclet.params.unshift( { - type: { - names: [ - 'module:utils/eventinfo~EventInfo' - ] - }, - description: '

An object containing information about the fired event.

', - name: 'eventInfo' - } ); - } - } -}; diff --git a/packages/jsdoc-plugins/lib/export-fixer/export-fixer.js b/packages/jsdoc-plugins/lib/export-fixer/export-fixer.js deleted file mode 100644 index 374a35013..000000000 --- a/packages/jsdoc-plugins/lib/export-fixer/export-fixer.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -module.exports = { - handlers: { - /** - * @see http://usejsdoc.org/about-plugins.html#event-beforeparse - * @param evt - */ - beforeParse( evt ) { - // See https://github.com/ckeditor/ckeditor5-design/blob/jsdoc-module-test/jsdoc/plugins/export-fix.js - // and the contents of that branch for better understanding of wht these replacements do. - - evt.source = evt.source - .replace( /(\n\t*)export default class /, '$1class ' ) - .replace( /(\n\t*)export class /g, '$1class ' ) - .replace( /(\n\t*)export default function /, '$1export function ' ); - } - } -}; diff --git a/packages/jsdoc-plugins/lib/fix-code-snippets.js b/packages/jsdoc-plugins/lib/fix-code-snippets.js deleted file mode 100644 index fd8178936..000000000 --- a/packages/jsdoc-plugins/lib/fix-code-snippets.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/** - * A fixer for doclet descriptions including code snippets. - * After upgrading JSDoc to v. 3.6.4 snippets started to contain leading tabs or spaces. - * This plugin removes that left padding. - */ -exports.handlers = { - parseComplete: e => { - /** @type {Array.} */ - const doclets = e.doclets; - - for ( const doclet of doclets ) { - if ( doclet.description ) { - doclet.description = fixDescription( doclet.description ); - } - - if ( doclet.classdesc ) { - doclet.classdesc = fixDescription( doclet.classdesc ); - } - - if ( doclet.params ) { - for ( const param of doclet.params ) { - if ( param.description ) { - param.description = fixDescription( param.description ); - } - } - } - - if ( doclet.returns ) { - for ( const returnObject of doclet.returns ) { - if ( returnObject.description ) { - returnObject.description = fixDescription( returnObject.description ); - } - } - } - - if ( doclet.properties ) { - for ( const property of doclet.properties ) { - if ( property.description ) { - property.description = fixDescription( property.description ); - } - } - } - } - } -}; - -function fixDescription( desc ) { - return desc.replace( /
(.*?)<\/code><\/pre>/gs, ( _match, codeSnippetContent ) => {
-		const codeRows = codeSnippetContent.split( '\n' );
-
-		let paddingSize = 0;
-
-		while ( true ) {
-			const paddingText = codeRows[ 0 ].slice( 0, paddingSize + 1 );
-
-			// When some row starts with a different text or
-			// padding contains some non-whitespace characters
-			// then it means that it is no loner a padding.
-			if (
-				( codeRows.some( row => /\S/.test( row ) && !row.startsWith( paddingText ) ) ) ||
-				/\S/.test( paddingText )
-			) {
-				break;
-			}
-
-			paddingSize++;
-		}
-
-		return (
-			'
' +
-			codeRows.map( row => row.slice( paddingSize ) ).join( '\n' ) +
-			'
' - ); - } ); -} diff --git a/packages/jsdoc-plugins/lib/longname-fixer/fixers/convert-short-refs-to-full-refs.js b/packages/jsdoc-plugins/lib/longname-fixer/fixers/convert-short-refs-to-full-refs.js deleted file mode 100644 index 8de5b6418..000000000 --- a/packages/jsdoc-plugins/lib/longname-fixer/fixers/convert-short-refs-to-full-refs.js +++ /dev/null @@ -1,227 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const assign = Object.assign; - -/** - * @param {Array.} doclets - */ -function convertShortRefsToFullRefs( doclets ) { - addMissingModulePart( doclets ); - convertShortRefsInLongnameAndMemberof( doclets ); - convertShortRefsInFireTag( doclets ); - convertShortRefsInSeeTag( doclets ); - convertShortRefsInLinks( doclets ); -} - -/** @param {Array.} doclets */ -function addMissingModulePart( doclets ) { - /** @type {Record} */ - const fileNameModuleDoclets = {}; - - for ( const doclet of doclets ) { - if ( doclet.kind === 'module' ) { - fileNameModuleDoclets[ doclet.meta.path + '/' + doclet.meta.filename ] = doclet; - } - } - - for ( const doclet of doclets ) { - if ( [ 'interface', 'class', 'mixin' ].includes( doclet.kind ) ) { - if ( - !doclet.longname.startsWith( 'module:' ) && - fileNameModuleDoclets[ doclet.meta.path + '/' + doclet.meta.filename ] - ) { - const module = fileNameModuleDoclets[ doclet.meta.path + '/' + doclet.meta.filename ]; - - assign( doclet, { - scope: 'inner', - memberof: module.longname, - longname: module.longname + '~' + doclet.longname - } ); - } - } - } -} - -/** @param {Array.} doclets */ -function convertShortRefsInLongnameAndMemberof( doclets ) { - const fileDoclets = groupDocletsByFiles( doclets ); - - for ( const doclet of doclets ) { - const parentDoclet = getCorrespondingParent( fileDoclets[ doclet.meta.path + '/' + doclet.meta.filename ], doclet ); - - const firstNameChar = doclet.longname[ 0 ]; - - if ( firstNameChar === '~' ) { - assign( doclet, { - memberof: parentDoclet.memberof + '~' + parentDoclet.name, - longname: parentDoclet.memberof + doclet.longname - } ); - } else if ( firstNameChar === '#' ) { - assign( doclet, { - memberof: parentDoclet.longname, - longname: parentDoclet.longname + doclet.longname - } ); - } - - // Fixes longname in events containing ':' in their names (e.g. change:attribute) - if ( doclet.kind === 'event' ) { - if ( doclet.longname.includes( '~' ) && doclet.longname.includes( '#' ) ) { - continue; - } - - doclet.memberof = parentDoclet.longname; - - if ( !doclet.name.includes( 'event' ) ) { - doclet.longname = parentDoclet.longname + '#event:' + doclet.name; - } else { - doclet.longname = parentDoclet.longname + '#' + doclet.name; - } - } - } -} - -/** @param {Array.} doclets */ -function convertShortRefsInFireTag( doclets ) { - for ( const doclet of doclets ) { - if ( !doclet.fires ) { - continue; - } - - doclet.fires = doclet.fires.map( event => { - if ( event.includes( 'module:' ) ) { - return event; - } - - if ( !event.includes( 'event:' ) ) { - event = 'event:' + event; - } - - if ( doclet.memberof.includes( '~' ) ) { - return doclet.memberof + '#' + event; - } - - return doclet.longname + '#' + event; - } ); - } -} - -/** @param {Array.} doclets */ -function convertShortRefsInSeeTag( doclets ) { - /** @type {Doclet} */ - let lastInterfaceOrClass; - - for ( const doclet of doclets ) { - if ( [ 'interface', 'class', 'mixin' ].includes( doclet.kind ) ) { - lastInterfaceOrClass = doclet; - } - - if ( !doclet.see ) { - continue; - } - - doclet.see = doclet.see.map( see => { - if ( see[ 0 ] === '#' ) { - return lastInterfaceOrClass.longname + see; - } - - if ( see[ 0 ] === '~' ) { - return lastInterfaceOrClass.memberof + see; - } - - return see; - } ); - } -} - -function convertShortRefsInLinks( doclets ) { - const fileDoclets = groupDocletsByFiles( doclets ); - - for ( const doclet of doclets ) { - const parentDoclet = getCorrespondingParent( fileDoclets[ doclet.meta.path + '/' + doclet.meta.filename ], doclet ); - - let memberof = doclet.memberof; - - // Errors have their own module 'module/errors'. - // Shortened links in error descriptions should link to the class items, not the error module. - if ( doclet.kind === 'error' && parentDoclet ) { - memberof = parentDoclet.longname; - } - - const linkRegExp = /{@link *([~#][^}]+)}/g; - const replacer = ( _fullLink, linkContent ) => { - const [ ref, ...linkDescription ] = linkContent.split( ' ' ); - const [ className, methodName ] = ref.split( '#' ); - - let result = '{@link ' + memberof; - - if ( !memberof.includes( className ) ) { - return result + linkContent + '}'; - } - - if ( methodName ) { - result += '#' + methodName; - } - - result += linkDescription.map( word => ' ' + word ).join( ' ' ); - - return result + '}'; - }; - - const comment = doclet.comment.replace( linkRegExp, replacer ); - - let description = doclet.description; - - if ( description ) { - description = doclet.description.replace( linkRegExp, replacer ); - } - - Object.assign( doclet, { comment, description } ); - } -} - -/** - * @param {Array.} doclets - */ -function groupDocletsByFiles( doclets ) { - /** @type {Record.>}*/ - const files = {}; - - for ( const doclet of doclets ) { - if ( !files[ doclet.meta.path + '/' + doclet.meta.filename ] ) { - files[ doclet.meta.path + '/' + doclet.meta.filename ] = []; - } - - files[ doclet.meta.path + '/' + doclet.meta.filename ].push( doclet ); - } - - return files; -} - -/** - * Finds within the same file the parent doclet (`class`, `interface` or `mixin`). - * - * @param {Array.} fileDoclets - * @param {Doclet} doclet -*/ -function getCorrespondingParent( fileDoclets, doclet ) { - let closestParent = null; - let closestLine = -1; - - for ( const fileDoclet of fileDoclets ) { - if ( [ 'interface', 'class', 'mixin' ].includes( fileDoclet.kind ) ) { - if ( fileDoclet.meta.lineno > closestLine && fileDoclet.meta.lineno <= doclet.meta.lineno ) { - closestParent = fileDoclet; - closestLine = fileDoclet.meta.lineno; - } - } - } - - return closestParent; -} - -module.exports = convertShortRefsToFullRefs; diff --git a/packages/jsdoc-plugins/lib/longname-fixer/fixers/fix-incorrect-class-constructor.js b/packages/jsdoc-plugins/lib/longname-fixer/fixers/fix-incorrect-class-constructor.js deleted file mode 100644 index 8b6dc7045..000000000 --- a/packages/jsdoc-plugins/lib/longname-fixer/fixers/fix-incorrect-class-constructor.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { isEqual } = require( 'lodash' ); - -/** - * This function is supposed to fix both constructor and class doclets. - * - * JSDoc completely messes up doclets for constructors and classes. - * They are duplicated (where the only one contains valuable data), have invalid descriptions, etc. - * - * @param {Array.} doclets - */ -module.exports = function fixIncorrectClassConstructor( doclets ) { - const knownConstructorDoclets = new Set(); - const knownDoclets = new Map(); - - for ( const doclet of doclets ) { - // Constructor doclets have the same longname as class doclets. - if ( doclet.kind === 'class' && doclet.params ) { - Object.assign( doclet, { - longname: doclet.longname + '#constructor', - memberof: doclet.longname, - kind: 'function', - scope: 'instance', - name: 'constructor' - } ); - - if ( doclet.comment ) { - delete doclet.undocumented; - } - } - - if ( doclet.kind === 'function' && doclet.name === 'constructor' ) { - if ( knownConstructorDoclets.has( doclet.longname ) ) { - doclet.ignore = true; - } - - knownConstructorDoclets.add( doclet.longname ); - } - - if ( doclet.kind === 'class' && doclet.classdesc ) { - if ( doclet.description && doclet.description !== doclet.classdesc ) { - doclet.ignore = true; - } else { - if ( doclet.classdesc ) { - delete doclet.undocumented; - } - } - - if ( !doclet.comment || doclet.comment.includes( '@inheritDoc' ) ) { - doclet.ignore = true; - } - } - - // Remove duplicates (mostly they are created by the relation-fixer). - // The whole relation-fixer's logic should be rewritten. - if ( knownDoclets.has( doclet.longname ) && isEqual( doclet, knownDoclets.get( doclet.longname ) ) ) { - doclet.ignore = true; - } - - knownDoclets.set( doclet.longname, doclet ); - } -}; diff --git a/packages/jsdoc-plugins/lib/longname-fixer/longname-fixer.js b/packages/jsdoc-plugins/lib/longname-fixer/longname-fixer.js deleted file mode 100644 index 3c6b526aa..000000000 --- a/packages/jsdoc-plugins/lib/longname-fixer/longname-fixer.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/** - * @see http://usejsdoc.org/about-plugins.html - */ - -'use strict'; - -const convertShortRefsToFullRefs = require( './fixers/convert-short-refs-to-full-refs' ); -const fixIncorrectClassConstructor = require( './fixers/fix-incorrect-class-constructor' ); - -const modulePattern = /module:[\w-]+(\/[\w-]+)*$/; - -exports.handlers = { - parseComplete: e => { - const doclets = e.doclets; - - fixIncorrectClassConstructor( doclets ); - convertShortRefsToFullRefs( doclets ); - - // Fix exported functions. - // All exported functions should be marked as module's `inner`. - for ( const doclet of doclets ) { - if ( doclet.kind === 'function' && doclet.scope === 'static' && modulePattern.test( doclet.memberof ) ) { - doclet.scope = 'inner'; - doclet.longname = doclet.longname.replace( /\./, '~' ); - } - - if ( doclet.kind === 'constant' && doclet.scope === 'static' && modulePattern.test( doclet.memberof ) ) { - doclet.scope = 'inner'; - doclet.longname = doclet.longname.replace( /\./, '~' ); - } - } - - // Filter out incorrect doclets. - e.doclets = doclets.filter( doclet => !doclet.ignore ); - } -}; diff --git a/packages/jsdoc-plugins/lib/observable-event-provider/addmissingeventdocletsforobservables.js b/packages/jsdoc-plugins/lib/observable-event-provider/addmissingeventdocletsforobservables.js deleted file mode 100644 index d898730b7..000000000 --- a/packages/jsdoc-plugins/lib/observable-event-provider/addmissingeventdocletsforobservables.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { cloneDeep } = require( 'lodash' ); - -/** - * Creates event doclets for observable properties if they're missing. - * See #285. - * - * @param {Array.} doclets - * @returns {Array.} An array of enhanced doclets. - */ -module.exports = function addMissingEventDocletsForObservables( doclets ) { - doclets = markFiredEvents( doclets ); - - const newEventDoclets = createMissingEventDoclets( doclets ); - - return doclets.concat( newEventDoclets ); -}; - -// @param {Array.} doclets -// @returns {Array.} An array of doclets with fixed fires property. -function markFiredEvents( doclets ) { - return doclets.map( doclet => { - if ( !doclet.observable ) { - return doclet; - } - - const newDoclet = cloneDeep( doclet ); - const eventName = newDoclet.memberof + '#event:change:' + newDoclet.name; - - if ( !newDoclet.fires ) { - newDoclet.fires = []; - } - - if ( !newDoclet.fires.includes( eventName ) ) { - newDoclet.fires.push( eventName ); - } - - return newDoclet; - } ); -} - -// @param {Array.} doclets -// @returns {Array.} An array of new event doclets. -function createMissingEventDoclets( doclets ) { - const eventLongNames = doclets.filter( d => d.kind === 'event' ).map( d => d.longname ); - const observableEvents = getObservableEvents( doclets ); - - return observableEvents - // Skip for existing events. - .filter( observableEvent => !eventLongNames.includes( observableEvent.name ) ) - .map( observableEvent => { - const originalProperty = observableEvent.property; - - const typeNames = originalProperty.type ? - originalProperty.type.names : - [ '*' ]; - - const eventDoclet = { - comment: '', - meta: cloneDeep( originalProperty.meta ), - description: `

Fired when the ${ originalProperty.name } property changed value.

`, - kind: 'event', - name: 'change:' + originalProperty.name, - params: [ { - type: { - names: [ 'module:utils/eventinfo~EventInfo' ] - }, - description: '

An object containing information about the fired event.

', - name: 'eventInfo' - }, - { - type: { - names: [ 'String' ] - }, - description: `

Name of the changed property (${ originalProperty.name }).

`, - name: 'name' - }, - { - type: { - names: [ ...typeNames ] - }, - description: [ - `

New value of the ${ originalProperty.name } property with given key or null, `, - 'if operation should remove property.

' - ].join( '' ), - name: 'value' - }, - { - type: { - names: [ ...typeNames ] - }, - description: [ - `

Old value of the ${ originalProperty.name } property with given key or null, `, - 'if property was not set before.

' - ].join( '' ), - name: 'oldValue' - } ], - memberof: originalProperty.memberof, - longname: observableEvent.name, - scope: 'instance', - access: originalProperty.access ? originalProperty.access : 'public' - }; - - if ( originalProperty.inherited ) { - eventDoclet.inherited = true; - } - - if ( originalProperty.mixed ) { - eventDoclet.mixed = true; - } - - return eventDoclet; - } ); -} - -function getObservableEvents( doclets ) { - return doclets - .filter( doclet => doclet.observable ) - .map( observableDoclet => { - const eventName = observableDoclet.memberof + '#event:change:' + observableDoclet.name; - - return { - name: eventName, - property: observableDoclet - }; - } ); -} diff --git a/packages/jsdoc-plugins/lib/observable-event-provider/index.js b/packages/jsdoc-plugins/lib/observable-event-provider/index.js deleted file mode 100644 index 83285112a..000000000 --- a/packages/jsdoc-plugins/lib/observable-event-provider/index.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const addMissingEventDocletsForObservables = require( './addmissingeventdocletsforobservables' ); - -module.exports = { - handlers: { - processingComplete( e ) { - e.doclets = addMissingEventDocletsForObservables( e.doclets ); - } - } -}; diff --git a/packages/jsdoc-plugins/lib/purge-private-api-docs.js b/packages/jsdoc-plugins/lib/purge-private-api-docs.js deleted file mode 100644 index 79586ffad..000000000 --- a/packages/jsdoc-plugins/lib/purge-private-api-docs.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -// A plugin for JSDoc that should purge non-public API docs. -// Public API docs should contain the `@publicApi` tag below the `@module` tag. - -const path = require( 'path' ); -const fs = require( 'fs' ); - -module.exports = { - handlers: { - /** - * @see http://usejsdoc.org/about-plugins.html#event-beforeparse - * @param {any} evt - */ - beforeParse( evt ) { - // Skip public packages. - if ( !isPrivatePackageFile( evt.filename ) ) { - return; - } - - // Do not emit any JSDoc doclet if the `@publicApi` tag is missing in that file. - if ( !evt.source.includes( '@publicApi' ) ) { - evt.source = ''; - - return; - } - - // Do not emit any JSDoc doclet if the '@module' tag is missing and log a warning. - if ( !evt.source.includes( '@module' ) ) { - evt.source = ''; - - const filename = path.relative( process.cwd(), evt.filename ); - - console.warn( `File ${ filename } did not start with '@module' tag and hence it will be ignored while building docs.` ); - } - }, - - processingComplete( evt ) { - for ( const doclet of evt.doclets ) { - if ( doclet.meta && doclet.meta.path ) { - if ( isPrivatePackageFile( doclet.meta.path ) ) { - doclet.skipSource = true; - } - } - } - - // Filter out protected and private doclets. - // It's a simple and naive approach, this way private and protected - // doclets from inherited public resources are also filtered out. - evt.doclets = evt.doclets.filter( doclet => { - if ( !doclet.skipSource ) { - return true; - } - - if ( doclet.access === 'private' ) { - return false; - } - - if ( doclet.access === 'protected' ) { - return false; - } - - return true; - } ); - } - }, - - /** - * See http://usejsdoc.org/about-plugins.html#tag-definition. - * - * @param {any} dictionary - */ - defineTags( dictionary ) { - dictionary.defineTag( 'publicApi', { - mustHaveValue: false, - canHaveType: false, - canHaveName: false, - - /** - * @param {any} doclet - * @param {any} tag - */ - onTagged( doclet ) { - Object.assign( doclet, { - publicApi: true - } ); - } - } ); - } -}; - -function isPrivatePackageFile( fileName ) { - let dirName = path.dirname( fileName ); - - while ( true ) { - const pathToPackageJson = path.join( dirName, 'package.json' ); - - if ( fs.existsSync( pathToPackageJson ) ) { - return !!JSON.parse( fs.readFileSync( pathToPackageJson ).toString() ).private; - } - - dirName = path.dirname( dirName ); - - // Root's dirname is equal to the root, - // So if this check passes, then we should break this endless loop. - if ( dirName === path.dirname( dirName ) ) { - throw new Error( `${ fileName } is not placed inside the NPM project.` ); - } - } -} diff --git a/packages/jsdoc-plugins/lib/relation-fixer/addmissingdoclets.js b/packages/jsdoc-plugins/lib/relation-fixer/addmissingdoclets.js deleted file mode 100644 index 127beb890..000000000 --- a/packages/jsdoc-plugins/lib/relation-fixer/addmissingdoclets.js +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const getMissingDocletsData = require( './getmissingdocletsdata' ); -const DocletCollection = require( '../utils/doclet-collection' ); - -module.exports = addMissingDoclets; - -/** - * Adds missing doclets for members coming from implemented interfaces, extended classes, mixins and typedefs. - * It does also support inheriting static members and typedef inheritance, which both are not supported by the JSDoc. - * This function requires input to be preprocessed by the `buildRelations()` function. - * - * @param {Array.} doclets - * @returns {Array.} - */ -function addMissingDoclets( doclets ) { - const docletCollection = new DocletCollection(); - - for ( const doclet of doclets ) { - // Group doclets by memberof property. - docletCollection.add( `memberof:${ doclet.memberof }`, doclet ); - } - - const extensibleDoclets = doclets.filter( doclet => { - return ( - doclet.kind === 'class' || - doclet.kind === 'interface' || - doclet.kind === 'mixin' - ); - } ); - - /** @type {Array.} */ - const newDocletsToAdd = []; - - /** @type {Array.} */ - const docletsToIgnore = []; - - /** - * @type {Array.} - **/ - const options = [ - // Missing inherited items: - // - statics methods - // - methods - // - properties - // - events - { - relation: 'augmentsNested' - }, - - // Everything mixed, except existing mixed items. - { - relation: 'mixesNested', - onlyImplicitlyInherited: true - }, - - // Everything from implemented interfaces. - { - relation: 'implementsNested' - } - ]; - - const docletMap = createDocletMap( docletCollection ); - - for ( const extensibleDoclet of extensibleDoclets ) { - for ( const option of options ) { - const missingDocletsData = getMissingDocletsData( - docletMap, - docletCollection, - extensibleDoclet, - option - ); - - newDocletsToAdd.push( ...missingDocletsData.newDoclets ); - docletsToIgnore.push( ...missingDocletsData.docletsWhichShouldBeIgnored ); - } - } - - // Ignore doclets that shouldn't be used anymore. They will be removed afterward. - for ( const docletToIgnore of docletsToIgnore ) { - docletToIgnore.ignore = true; - } - - doclets = doclets.filter( doclet => !doclet.ignore ); - - const docletToAddMap = new Map( - newDocletsToAdd - .filter( d => !d.ignore ) - .map( d => [ d.longname, d ] ) - ); - - const existingDoclets = new Map( doclets.map( d => [ d.longname, d ] ) ); - - // The code here is hackish... - // We need to smartly determine which doclet should be get from the native JSDoc inheritance - // and which should be added from our custom inheritance mechanism. - return [ - ...doclets.filter( doclet => { - const willDocletBeAdded = docletToAddMap.has( doclet.longname ); - - // If the doclet has inherited property don't output it - // as it should be replaced by the parent's class/interface/mixin method doclet. - if ( willDocletBeAdded && doclet.inheritdoc === undefined ) { - return false; - } - - return true; - } ), - - // Do not output doclets that have its own documentation. - ...Array.from( docletToAddMap.values() ).filter( doclet => { - const existingDoclet = existingDoclets.get( doclet.longname ); - - if ( !existingDoclet ) { - return true; - } - - if ( existingDoclet.inheritdoc === undefined ) { - return true; - } - - return false; - } ) - ]; -} - -/** - * Creates a map. - * - * @param {DocletCollection} doclets - * @returns {Object} - */ -function createDocletMap( doclets ) { - const docletMap = {}; - - for ( const doclet of doclets.getAll() ) { - if ( !docletMap[ doclet.longname ] ) { - docletMap[ doclet.longname ] = doclet; - } - } - - return docletMap; -} diff --git a/packages/jsdoc-plugins/lib/relation-fixer/addtypedefproperties.js b/packages/jsdoc-plugins/lib/relation-fixer/addtypedefproperties.js deleted file mode 100644 index 08af430be..000000000 --- a/packages/jsdoc-plugins/lib/relation-fixer/addtypedefproperties.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { cloneDeep } = require( 'lodash' ); - -module.exports = addTypedefProperties; - -/** - * It adds missing properties to doclet when in extends another doclets (with the `@extends` tag). - * Without modification on the original doclets it returns extended original doclets - * and new member doclets for each typedef property. - * - * @param {Array.} doclets - * @returns {Array.} - */ -function addTypedefProperties( doclets ) { - const typedefDoclets = doclets.filter( doclet => doclet.kind === 'typedef' ); - - // 1. Extend typedefs with missing properties. - doclets = doclets.map( doclet => { - if ( doclet.kind == 'typedef' ) { - return extendTypedef( doclet, typedefDoclets ); - } - - return doclet; - } ); - - // 2. Add doclets for typedef properties. - const typedefPropertyDoclets = createTypedefPropertyDoclets( typedefDoclets ); - - return [ - ...doclets, - ...typedefPropertyDoclets - ]; -} - -/** - * Copy properties from parent typedefs to typedefs which extend them. - * - * @param {Doclet} typedef - * @param {Array.} typedefDoclets - */ -function extendTypedef( typedef, typedefDoclets ) { - if ( !typedef.properties || !typedef.augmentsNested || !typedef.augmentsNested.length ) { - return typedef; - } - - // Prevent possible mutations. - typedef = cloneDeep( typedef ); - - for ( const parentLongname of typedef.augmentsNested ) { - const parentDoclet = typedefDoclets.find( doclet => doclet.longname === parentLongname ); - - if ( !parentDoclet ) { - console.error( `Base typedef (${ parentLongname }) for the ${ typedef.longname } typedef is missing.` ); - } - - if ( !parentDoclet.properties ) { - continue; - } - - for ( const parentProperty of parentDoclet.properties ) { - if ( !typedef.properties.find( p => p.name === parentProperty.name ) ) { - const inheritedProperty = cloneDeep( parentProperty ); - inheritedProperty.inherited = true; - typedef.properties.push( inheritedProperty ); - } - } - } - - return typedef; -} - -/** - * Creates and returns doclets for `@typedef` properties. - * - * @param {Array.} typedefDoclets - */ -function createTypedefPropertyDoclets( typedefDoclets ) { - const typedefPropertyDoclets = []; - - for ( const typedefDoclet of typedefDoclets ) { - for ( const property of typedefDoclet.properties || [] ) { - /** @type Doclet */ - const propertyDoclet = { - comment: property.description || '', - description: property.description, - - // Use the typedef's metadata. - meta: cloneDeep( typedefDoclet.meta ), - kind: 'member', - name: property.name, - type: property.type, - longname: typedefDoclet.longname + '#' + property.name, - scope: 'instance', - memberof: typedefDoclet.longname - }; - - typedefPropertyDoclets.push( propertyDoclet ); - } - } - - return typedefPropertyDoclets; -} diff --git a/packages/jsdoc-plugins/lib/relation-fixer/buildrelations.js b/packages/jsdoc-plugins/lib/relation-fixer/buildrelations.js deleted file mode 100644 index 91b20de74..000000000 --- a/packages/jsdoc-plugins/lib/relation-fixer/buildrelations.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { cloneDeep, uniq } = require( 'lodash' ); -const DocletCollection = require( '../utils/doclet-collection' ); -const RELATIONS = { - implements: 'implementsNested', - mixes: 'mixesNested', - augments: 'augmentsNested' -}; - -module.exports = buildRelations; - -/** - * Checks ascendants of every doclet and adds them to the relation array. - * Handles nested inheritance, mixins and implementation of interfaces. - * Also adds descendants to doclet. Descendants are entities that extend, implement or mix a doclet. - * For example: If ClassB extends ClassA and ClassA implements InterfaceC, - * ClassB and ClassA will have a property 'implementsNested': [ 'InterfaceC' ], - * also InterfaceC will have a property 'descendants': [ 'ClassA', 'ClassB' ] etc. - * - * @param {Array.} doclets - * @returns {Array.} - */ -function buildRelations( doclets ) { - // Preserve original doclets from modification for easier testing. - doclets = cloneDeep( doclets ); - - /** - * Doclets grouped by their longnames. - */ - const docletCollection = new DocletCollection(); - - for ( const doclet of doclets ) { - docletCollection.add( doclet.longname, doclet ); - } - - /** - * An array of doclets, for which we want to create relation arrays. - */ - const subjectDoclets = doclets.filter( item => { - return ( - item.kind === 'class' || - item.kind === 'interface' || - item.kind === 'mixin' || - item.kind === 'typedef' - ); - } ); - - for ( const doclet of subjectDoclets ) { - const related = getAncestors( docletCollection, doclet, { - relations: [ 'augments', 'implements', 'mixes' ] - } ); - - // Remove duplicates. - for ( const relation of Object.keys( related ) ) { - related[ relation ] = uniq( related[ relation ] ); - } - - Object.assign( doclet, related ); - } - - for ( const doclet of subjectDoclets ) { - doclet.descendants = uniq( getDescendants( subjectDoclets, doclet ) ); - } - - return doclets; -} - -/** - * Gets long names of the current doclet ancestors (classes it extends, interfaces it implements and so on). - * - * @param {DocletCollection} docletCollection Doclets grouped by their longnames - * @param {Doclet} currentDoclet - * @param {Object} options - * @param {Array.<'augments'|'implements'|'mixes'>} options.relations An array of relation names which should be used. - * @returns {Object} An object containing arrays of ancestors' longnames. - */ -function getAncestors( docletCollection, currentDoclet, options ) { - const { relations } = options; - - /** @type {Object} */ - const resultRelations = {}; - - // Initialize the returned object. - for ( const baseRelation of relations ) { - resultRelations[ RELATIONS[ baseRelation ] ] = []; - } - - // For every relation take doclets which are related to current doclet and run `getAncestors` function on them recursively. - for ( const baseRelation of relations ) { - const relation = RELATIONS[ baseRelation ]; - - if ( isEmpty( currentDoclet[ baseRelation ] ) ) { - continue; - } - - resultRelations[ relation ].push( ...currentDoclet[ baseRelation ] ); - - for ( const longname of currentDoclet[ baseRelation ] ) { - const ancestors = docletCollection.get( longname ); - - for ( const ancestor of ancestors ) { - const ancestorsResultRelations = getAncestors( docletCollection, ancestor, { - relations - } ); - - // Push relation arrays of doclet's ancestors to current doclet resultRelations. - for ( const key of Object.keys( resultRelations ) ) { - // Only items of same kind can be put in inheritance tree. See #361. - if ( key === 'augmentsNested' && ancestor.kind !== currentDoclet.kind ) { - continue; - } - - resultRelations[ key ].push( ...ancestorsResultRelations[ key ] ); - } - } - } - } - - return resultRelations; -} - -/** - * Returns `true` when the input in equal to `undefined` or is an empty array. - * - * @param {Array|undefined} arr - */ -function isEmpty( arr ) { - return !arr || arr.length === 0; -} - -/** - * Gets long names of descendants – i.e. entities which extend, implement or mix a doclet. - * - * @param {Array.} searchedDoclets - * @param {Doclet} currentDoclet - * @returns {Array.} An array of long names. - */ -function getDescendants( searchedDoclets, currentDoclet ) { - /** @type {Set.} */ - const descendants = new Set(); - - for ( const doclet of searchedDoclets ) { - for ( const baseRelation in RELATIONS ) { - const relation = RELATIONS[ baseRelation ]; - - if ( - !isEmpty( doclet[ relation ] ) && - doclet[ relation ].includes( currentDoclet.longname ) - ) { - descendants.add( doclet.longname ); - break; - } - } - } - - return Array.from( descendants ); -} diff --git a/packages/jsdoc-plugins/lib/relation-fixer/filteroutintenraldoclets.js b/packages/jsdoc-plugins/lib/relation-fixer/filteroutintenraldoclets.js deleted file mode 100644 index 2414572e1..000000000 --- a/packages/jsdoc-plugins/lib/relation-fixer/filteroutintenraldoclets.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** - * Filters out doclet that won't be displayed. - * - * @param {Array.} doclets - */ -module.exports = function filterOutInternalDoclets( doclets ) { - return doclets - .filter( doclet => !doclet.ignore ) - .filter( doclet => doclet.memberof != '' ); -}; diff --git a/packages/jsdoc-plugins/lib/relation-fixer/getmissingdocletsdata.js b/packages/jsdoc-plugins/lib/relation-fixer/getmissingdocletsdata.js deleted file mode 100644 index e695d6bb7..000000000 --- a/packages/jsdoc-plugins/lib/relation-fixer/getmissingdocletsdata.js +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { cloneDeep } = require( 'lodash' ); - -module.exports = getMissingDocletsData; - -/** - * Gets missing doclets of members coming from implemented interfaces, extended classes, mixins and typedefs. - * It returns also doclets which should be ignored as no longer necessary. - * This function requires input to be preprocessed by the `buildRelations()` function. - * - * @param {Map.} docletMap - * @param {DocletCollection} docletCollection - * @param {Doclet} interfaceClassOrMixinDoclet Doclet representing an entity which might have some inherited members missing. - * @param {Options} options - * @returns {{newDoclets: Array., docletsWhichShouldBeIgnored: Array.}} - */ -function getMissingDocletsData( docletMap, docletCollection, interfaceClassOrMixinDoclet, options ) { - const newDoclets = []; - const docletsWhichShouldBeIgnored = []; - - const docletsToAdd = getDocletsToAdd( docletCollection, interfaceClassOrMixinDoclet, options ); - - for ( const docletToAdd of docletsToAdd ) { - const clonedDoclet = cloneDeep( docletToAdd ); - - clonedDoclet.longname = getLongnameForNewDoclet( docletToAdd, interfaceClassOrMixinDoclet ); - clonedDoclet.memberof = interfaceClassOrMixinDoclet.longname; - - // Add property `inherited` or `mixed`. - const relationProperty = getRelationProperty( docletMap, interfaceClassOrMixinDoclet, docletToAdd, options.relation ); - - if ( relationProperty ) { - clonedDoclet[ relationProperty ] = true; - } - - const docletsOfSameMember = docletCollection.get( `memberof:${ clonedDoclet.memberof }` ).filter( d => { - // Different types, so avoid comparing their names. - if ( d.kind !== clonedDoclet.kind ) { - return false; - } - - // Static members are separated using the dot character. - const docletName = d.name.replace( /^[#.]/, '' ); - const clonedDocletName = clonedDoclet.name.replace( /^[#.]/, '' ); - - return docletName === clonedDocletName; - } ); - - if ( docletsOfSameMember.length === 0 ) { - // If there was no doclet for that member, simply add it to new doclets. - newDoclets.push( clonedDoclet ); - } else if ( doAllParentsExplicitlyInherit( docletsOfSameMember ) && !options.onlyImplicitlyInherited ) { - // If all doclets in the chain for that member already existed and used `inheritdoc` or `overrides`. - // Add `ignore` property to existing doclets. Unless 'onlyImplicitlyInherited' option is set. - docletsWhichShouldBeIgnored.push( ...docletsOfSameMember ); - - newDoclets.push( clonedDoclet ); - } else if ( docletsOfSameMember.length >= 2 ) { - const correctDoclet = cloneDeep( docletsOfSameMember[ 0 ] ); - - correctDoclet[ relationProperty ] = true; - - docletsWhichShouldBeIgnored.push( ...docletsOfSameMember ); - newDoclets.push( correctDoclet ); - } - } - - return { - newDoclets, - docletsWhichShouldBeIgnored - }; -} - -/** - * Gets doclets from entities related to current doclet (e.g. implemented by it) - * and matching criteria given via the `options.filter` query. - * - * @param {DocletCollection} docletCollection - * @param {Doclet} childDoclet - * @param {Options} options - * @returns {Array.} - */ -function getDocletsToAdd( docletCollection, childDoclet, options ) { - // Longnames of doclets which are related ( extended, mixed, implemented ) to childDoclet. - const ancestors = childDoclet[ options.relation ] || []; - - /** @type {Array.} */ - const docletsToAdd = []; - - for ( const ancestor of ancestors ) { - docletsToAdd.push( - ...docletCollection - .get( `memberof:${ ancestor }` ) - .filter( doclet => shouldDocletBeAdded( doclet, options ) ) - ); - } - - return docletsToAdd; -} - -/** - * @param {Doclet} doclet - * @param {Object} options - */ -function shouldDocletBeAdded( doclet, options ) { - // Filter out ignored, inherited, undocumented. - if ( - doclet.ignore || - doclet.undocumented || - typeof doclet.inheritdoc == 'string' - ) { - return false; - } - - for ( const key of Object.keys( options.filter || {} ) ) { - if ( doclet[ key ] !== options.filter[ key ] ) { - return false; - } - } - - return true; -} - -/** - * @param {Doclet} parentDoclet - * @param {Doclet} childDoclet - * @returns {String} - */ -function getLongnameForNewDoclet( parentDoclet, childDoclet ) { - const dotIndex = parentDoclet.longname.lastIndexOf( '.' ); - const hashIndex = parentDoclet.longname.lastIndexOf( '#' ); - const name = parentDoclet.longname.slice( Math.max( dotIndex, hashIndex ) ); - - return childDoclet.longname + name; -} - -/** - * Gets property which should be added to the new doclet (e.g. `inherited` or `mixed`). - * - * @param {Map.} docletMap - * @param {Doclet} childDoclet - * @param {Doclet} memberDoclet - * @param {'augmentsNested'|'mixesNested'|'implementsNested'} relation - * @returns {'inherited'|'mixed'|null} - */ -function getRelationProperty( docletMap, childDoclet, memberDoclet, relation ) { - if ( relation === 'augmentsNested' ) { - return 'inherited'; - } - - if ( relation === 'mixesNested' ) { - return 'mixed'; - } - - const memberDocletParent = docletMap[ memberDoclet.memberof ]; - - let isInherited = false; - let isMixed = false; - - // If doclet is a child of a mixin, it's 'mixed'. Else if it's a child of another class, it's 'inherited'. - for ( const longname of memberDocletParent.descendants || [] ) { - const doclet = docletMap[ longname ]; - - if ( !doclet || !doclet.descendants || !doclet.descendants.includes( childDoclet.longname ) ) { - continue; - } - - if ( doclet.kind === 'mixin' ) { - isMixed = true; - } else if ( doclet.kind === 'class' ) { - isInherited = true; - } - } - - if ( isMixed ) { - return 'mixed'; - } else if ( isInherited ) { - return 'inherited'; - } else { - return null; - } -} - -/** - * @param {Array.} doclets - * @returns {Boolean} - */ -function doAllParentsExplicitlyInherit( doclets ) { - for ( const doclet of doclets ) { - if ( doclet.inheritdoc === undefined && doclet.overrides === undefined ) { - return false; - } - } - - return true; -} - -/** - * @typedef {Object} Options - * - * @property {'augmentsNested'|'mixesNested'|'implementsNested'} relation Name of relation between child entity - * and its ancestors. - * @property {Partial} [filter] Object used to filter missing doclets (e.g. { scope: 'static' }). - * @property {Boolean} [onlyImplicitlyInherited] - */ diff --git a/packages/jsdoc-plugins/lib/relation-fixer/index.js b/packages/jsdoc-plugins/lib/relation-fixer/index.js deleted file mode 100644 index 7602c8b84..000000000 --- a/packages/jsdoc-plugins/lib/relation-fixer/index.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const buildRelations = require( './buildrelations' ); -const addMissingDoclets = require( './addmissingdoclets' ); -const filterOutInternalDoclets = require( './filteroutintenraldoclets' ); -const addTypedefProperties = require( './addtypedefproperties' ); - -exports.handlers = { - processingComplete( e ) { - e.doclets = precleanInheritance( e.doclets ); - e.doclets = buildRelations( e.doclets ); - e.doclets = addTypedefProperties( e.doclets ); - e.doclets = addMissingDoclets( e.doclets ); - e.doclets = filterOutInternalDoclets( e.doclets ); - } -}; - -/** - * Revert missing doclets that were marked as ignored. - * Remove ignored doclets. - * - * @param {Array.} doclets - */ -function precleanInheritance( doclets ) { - // Group doclet by longnames - /** @type {Map.>} */ - const docletMap = new Map(); - - for ( const doclet of doclets ) { - const docletsWithTheSameName = docletMap.get( doclet.longname ) || []; - - docletMap.set( doclet.longname, [ ...docletsWithTheSameName, doclet ] ); - } - - for ( const doclets of docletMap.values() ) { - if ( doclets.length === 1 ) { - continue; - } - - const ignoredOriginalDoclet = doclets.find( d => d.ignore && !d.mixed && !d.inherited ); - const restDoclets = doclets.filter( d => d !== ignoredOriginalDoclet ); - - if ( ignoredOriginalDoclet && restDoclets.every( d => d.inherited || d.mixed ) ) { - for ( const doclet of restDoclets ) { - doclet.ignore = true; - } - - delete ignoredOriginalDoclet.ignore; - } - - if ( doclets.some( d => !!d.comment ) ) { - for ( const doclet of doclets ) { - if ( !doclet.comment ) { - doclet.ignore = true; - } - } - } - } - - // Filter out member doclets that miss their comments. - for ( const doclet of doclets ) { - if ( ( doclet.kind === 'member' || doclet.kind === 'function' ) && ( doclet.inheritdoc === '' || !doclet.comment ) ) { - if ( doclet.name !== 'constructor' ) { - doclet.ignore = true; - } - } - } - - doclets = doclets.filter( d => !d.ignore ); - - return doclets; -} diff --git a/packages/jsdoc-plugins/lib/utils/doclet-collection.js b/packages/jsdoc-plugins/lib/utils/doclet-collection.js deleted file mode 100644 index 8a8dfb441..000000000 --- a/packages/jsdoc-plugins/lib/utils/doclet-collection.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** - * Collection of doclets as > pairs. Also stores all doclets and their longnames as arrays. - */ -module.exports = class DocletCollection { - /** - * Creates collection of doclets. - */ - constructor() { - this._data = {}; - this._allData = []; - this._allLongnames = []; - } - - /** - * Adds doclet to collection. - * - * @param {String} category - * @param {Doclet} doclet - */ - add( category, doclet ) { - if ( !this._data[ category ] ) { - this._data[ category ] = []; - } - - this._data[ category ].push( doclet ); - this._allData.push( doclet ); - - if ( doclet.longname ) { - this._allLongnames.push( doclet.longname ); - } - } - - /** - * Returns doclets filtered by category. - * - * @param {String} category - * @returns {Array.} - */ - get( category ) { - return this._data[ category ] || []; - } - - /** - * Returns all doclets. - * - * @returns {Array.} - */ - getAll() { - return this._allData; - } - - /** - * Returns all longnames. - * - * @returns {Array.} - */ - getAllLongnames() { - return this._allLongnames; - } -}; diff --git a/packages/jsdoc-plugins/lib/utils/doclet-logger.js b/packages/jsdoc-plugins/lib/utils/doclet-logger.js deleted file mode 100644 index ada4c59b4..000000000 --- a/packages/jsdoc-plugins/lib/utils/doclet-logger.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const fsExtra = require( 'fs-extra' ); -const path = require( 'path' ); - -/** - * Created for testing purpose. - * You can add / remove this plugin in src/tasks/build-api-docs.js - */ -exports.handlers = { - processingComplete( e ) { - const outputPath = process.env.JSDOC_OUTPUT_PATH; - - fsExtra.outputFileSync( path.resolve( outputPath ), JSON.stringify( e, null, 4 ) ); - } -}; diff --git a/packages/jsdoc-plugins/lib/validator/doclet-validator.js b/packages/jsdoc-plugins/lib/validator/doclet-validator.js deleted file mode 100644 index e3ee96b89..000000000 --- a/packages/jsdoc-plugins/lib/validator/doclet-validator.js +++ /dev/null @@ -1,487 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const DocletCollection = require( '../utils/doclet-collection' ); -const { ALL_TYPES, GENERIC_TYPES } = require( './types' ); - -const complexTypeRegExp = /^([\w]+)\.<\(?([^)^>]+)\)?>$/; - -/** - * Main validation class - */ -class DocletValidator { - /** - * Creates DocletLinter - */ - constructor( doclets ) { - /** - * Errors found during the validation. - * - * @type {Array.} - * @public - * @readonly - * */ - this.errors = []; - - /** - * Doclets grouped by doclet's `kind` property. - * - * @private - */ - this._collection = this._createDocletCollection( doclets ); - - /** - * The `longname` -> `doclet` map of doclets. - * - * @type {Record.} - */ - this._docletMap = createDocletMap( doclets ); - } - - /** - * Creates doclets grouped by doclet kind. - * - * @private - * @returns {DocletCollection} - */ - _createDocletCollection( doclets ) { - const collection = new DocletCollection(); - - for ( const doclet of doclets ) { - collection.add( doclet.kind, doclet ); - } - - return collection; - } - - /** - * Returns errors found during the validation. - * - * @public - * @returns {Array.} - */ - validate() { - this.errors.length = 0; - - this._validateMembers(); - this._validateMemberofProperty(); - this._validateLongnamePropertyInClasses(); - this._validateParameters(); - this._validateLinks(); - this._validateEvents(); - this._validateInterfaces(); - this._validateModuleDocumentedExports(); - this._validateReturnTypes(); - this._validateSeeReferences(); - this._validateTypedefs(); - this._validateExtensibility(); - this._checkDuplicatedDoclets(); - - return this.errors; - } - - /** - * Finds incorrectly documented members. - * - * @protected - */ - _validateMembers() { - this._collection.get( 'member' ) - .filter( member => member.name.startsWith( 'module:' ) ) - .filter( member => member.scope === 'inner' ) - .forEach( member => this._addError( member, `Incorrect member name: ${ member.name }` ) ); - } - - /** - * @protected - */ - _validateMemberofProperty() { - this._collection.getAll() - .filter( doclet => doclet.memberof && !doclet.memberof.startsWith( 'module:' ) ) - .forEach( doclet => { - this._addError( doclet, `Memberof property should start with 'module:'. Got '${ doclet.memberof }' instead.` ); - } ); - } - - /** - * @protected - */ - _validateLongnamePropertyInClasses() { - this._collection.getAll() - .filter( doclet => doclet.longname ) - .filter( doclet => { - const match = doclet.longname.match( /~([\w]+)\.([\w]+)$/ ); // e.g module:utils/ckeditorerror~CKEditorError.CKEditorError - - return match && match[ 1 ] === match[ 2 ]; - } ) - .forEach( doclet => { - this._addError( doclet, `Incorrect class reference name. No doclet exists with the following name: ${ doclet.longname }` ); - } ); - } - - /** - * @protected - */ - _validateLongnameProperty() { - this._collection.getAll() - .filter( doclet => doclet.longname && !doclet.longname.startsWith( 'module:' ) ) - .forEach( doclet => { - this._addError( doclet, `Longname property should start with the 'module:' part. Got ${ doclet.longname } instead.` ); - } ); - } - - /** - * Finds errors in parameter types. - * - * @protected - */ - _validateParameters() { - for ( const doclet of this._collection.getAll() ) { - for ( const param of doclet.params || [] ) { - this._validateParameter( doclet, param ); - } - } - } - - /** - * @private - * - * @param {Doclet} doclet - * @param {Object} param - */ - _validateParameter( doclet, param ) { - if ( !param.type ) { - // Skip not typed parameters. - return; - } - - for ( const paramFullName of param.type.names || [] ) { - if ( !this._isCorrectType( paramFullName ) ) { - this._addError( doclet, `Incorrect param type: ${ paramFullName }` ); - } - } - } - - /** - * Finds errors in links. - * - * @protected - */ - _validateLinks() { - const allLinkRegExp = /\{@link\s+[^}]+\}/g; - const pathRegExp = /^\{@link\s+([^}\s]+)[^}]*\}$/; - - const optionalTagWithBracedContentRegExp = /(@[a-z]+ )?\{[^}]+\}/g; - - for ( const doclet of this._collection.getAll() ) { - if ( !doclet.comment ) { - continue; - } - - // Find all missing `@link` parts inside comments. - for ( const commentPart of doclet.comment.match( optionalTagWithBracedContentRegExp ) || [] ) { - if ( commentPart.startsWith( '{module:' ) ) { - // If the comment part starts with the '{module:' it means that: - // * it's not a normal tag (tags starts with `@` and the tagName). - // * it's not a link (the part misses the `@link` part), but it supposed to be (it contains the `module:` part). - this._addError( doclet, `Link misses the '@link' part: ${ commentPart }` ); - } - } - - const refs = ( doclet.comment.match( allLinkRegExp ) || [] ) - .map( link => link.match( pathRegExp )[ 1 ] ); - - for ( const ref of refs ) { - if ( !this._isCorrectReference( ref ) ) { - this._addError( doclet, `Incorrect link: ${ ref }` ); - } - } - } - } - - /** - * Finds errors in the 'fires' tag. - * - * @protected - */ - _validateEvents() { - const eventNames = this._collection.get( 'event' ).map( event => event.longname ); - - for ( const doclet of this._collection.getAll() ) { - for ( const event of doclet.fires || [] ) { - if ( !eventNames.includes( event ) ) { - this._addError( doclet, `Incorrect event name: ${ event } in @fires tag` ); - } - } - } - } - - /** - * Finds errors in the 'implements' tag. - * - * @protected - */ - _validateInterfaces() { - const classesAndMixins = [ ...this._collection.get( 'class' ), ...this._collection.get( 'mixin' ) ]; - const interfaceLongNames = this._collection.get( 'interface' ) - .map( i => i.longname ); - - for ( const someClassOrMixin of classesAndMixins ) { - for ( const someInterface of someClassOrMixin.implements || [] ) { - if ( !interfaceLongNames.includes( someInterface ) ) { - this._addError( someClassOrMixin, `Incorrect interface name: ${ someInterface }` ); - } - } - } - } - - /** - * @protected - */ - _validateModuleDocumentedExports() { - const memberDoclets = this._collection.get( 'member' ) - .filter( member => member.scope === 'inner' ) - .filter( member => !member.undocumented ); - - for ( const member of memberDoclets ) { - if ( this._docletMap[ member.memberof ] && this._docletMap[ member.memberof ].kind === 'module' ) { - this._addError( member, `Module ${ member.memberof } exports member: ${ member.name }` ); - } - } - } - - /** - * @protected - */ - _validateReturnTypes() { - const doclets = this._collection.getAll() - .filter( doclet => !!doclet.returns ); - - for ( const doclet of doclets ) { - if ( !doclet.returns[ 0 ].type ) { - this._addError( doclet, 'Invalid return type.' ); - continue; - } - - for ( const typeName of doclet.returns[ 0 ].type.names ) { - if ( !this._isCorrectType( typeName ) ) { - this._addError( doclet, `Invalid return type: ${ typeName }.` ); - } - } - } - } - - /** - * @protected - */ - _validateSeeReferences() { - for ( const doclet of this._collection.getAll() ) { - for ( const seeReference of doclet.see || [] ) { - if ( !this._isCorrectReference( seeReference ) ) { - this._addError( doclet, `Invalid @see reference: ${ seeReference }.` ); - } - } - } - } - - /** - * @protected - */ - _validateTypedefs() { - for ( const doclet of this._collection.getAll() ) { - for ( const prop of doclet.properties || [] ) { - for ( const typeName of prop.type.names ) { - if ( !this._isCorrectType( typeName ) ) { - this._addError( doclet, `Invalid @property type: ${ typeName }.` ); - } - } - } - } - } - - /** - * Checks whether the reference in the `@extends` tag is correct. - * - * @protected - */ - _validateExtensibility() { - for ( const doclet of this._collection.getAll() ) { - for ( const base of doclet.augments || [] ) { - if ( !this._isCorrectReference( base ) && !this._isValidBuiltInType( base ) ) { - this._addError( - doclet, - `Invalid @extends reference: ${ base }.` - ); - } - } - } - - for ( const doclet of this._collection.getAll() ) { - for ( const base of doclet.implements || [] ) { - const baseDoclet = this._docletMap[ base ]; - - // TODO: We should only allow interfaces here. - if ( !baseDoclet || ![ 'interface', 'function' ].includes( baseDoclet.kind ) ) { - this._addError( - doclet, - `Invalid @implements reference: ${ base } - no found doclet or doclet is not an interface.` - ); - } - } - } - } - - _checkDuplicatedDoclets() { - const docletLongNames = new Set(); - - for ( const doclet of this._collection.getAll() ) { - // Skip modules. - // Module descriptions are mergeable. - if ( doclet.kind === 'module' ) { - continue; - } - - if ( docletLongNames.has( doclet.longname ) ) { - this._addError( doclet, 'Duplicated doclets with longname: ' + doclet.longname ); - } - - docletLongNames.add( doclet.longname ); - } - } - - /** - * @private - * @param {Doclet} doclet - * @param {string} errorMessage - */ - _addError( doclet, errorMessage ) { - this.errors.push( Object.assign( { - message: errorMessage - }, this._getErrorData( doclet ) ) ); - } - - /** - * @private - * @param {Doclet} doclet - */ - _getErrorData( doclet ) { - return { - parent: doclet.memberof, - line: doclet.meta.lineno, - file: doclet.meta.path + '/' + doclet.meta.filename - }; - } - - /** - * Naive implementation of simple parser. - * - * @protected - * @param {String} type to assert - * - * @returns {Boolean} - */ - _isCorrectType( type ) { - // JSDoc converts `Type.` to `Type.` for some reason... - type = type.replace( 'function()', 'function' ); - - if ( complexTypeRegExp.test( type ) ) { - const [ , genericType, innerType ] = type.match( complexTypeRegExp ); - - return GENERIC_TYPES.includes( genericType ) && this._isCorrectType( innerType ); - } - - if ( type.includes( '|' ) ) { - return type.split( '|' ).every( unionType => this._isCorrectType( unionType ) ); - } - - if ( type.includes( ',' ) ) { - return type.split( ',' ).every( type => this._isCorrectType( type ) ); - } - - if ( type.includes( 'module:' ) ) { - return this._isCorrectReferenceType( type ); - } - - type = type.trim(); - - return this._isValidBuiltInType( type ) || - this._isStringLiteralType( type ); - } - - /** @private */ - _isValidBuiltInType( type ) { - return ALL_TYPES.includes( type ); - } - - /** - * A string literal type - e.g. 'forward' or 'backward'; - * - * @private - */ - _isStringLiteralType( type ) { - return /^'[^']+'$/.test( type ); - } - - /** - * @private - * @param {String} type - */ - _isCorrectReference( type ) { - type = type.trim(); - - if ( !type.includes( 'module:' ) ) { - return false; - } - - return !!this._docletMap[ type ]; - } - - /** - * Returns `true` when the reference points to the symbol which is one of: - * * `class` - * * `interface` - * * `typedef` - * * `function` - * - * @private - * @param {String} type - */ - _isCorrectReferenceType( type ) { - type = type.trim(); - - if ( !type.includes( 'module:' ) ) { - return false; - } - - const doclet = this._docletMap[ type ]; - - if ( !doclet ) { - return false; - } - - return doclet.kind === 'class' || - doclet.kind === 'interface' || - doclet.kind === 'typedef' || - doclet.kind === 'function'; - } -} - -/** - * @param {Array.} doclets - */ -function createDocletMap( doclets ) { - /** @type {Record.} */ - const docletMap = {}; - - for ( const doclet of doclets ) { - docletMap[ doclet.longname ] = doclet; - } - - return docletMap; -} - -module.exports = DocletValidator; diff --git a/packages/jsdoc-plugins/lib/validator/types.js b/packages/jsdoc-plugins/lib/validator/types.js deleted file mode 100644 index 7ffb53ab5..000000000 --- a/packages/jsdoc-plugins/lib/validator/types.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const BASIC_TYPES = [ - // Wildcard - '*', - - // ES types - 'null', - 'undefined', - 'String', - 'Number', - 'Boolean', - 'Object', - 'Error', - 'Function', - 'function', // JSDoc renames Function into function. - 'RegExp', - 'Symbol', - 'Date', - - // DOM API - 'Node', - 'NodeList', - 'HTMLElement', - 'Document', - 'Element', - 'DocumentFragment', - 'Text', - 'Range', - 'Selection', - 'Event', - 'FocusEvent', - 'ClientRect', - 'DOMRect', - 'Window', - 'ErrorEvent', - 'PromiseRejectionEvent', - 'Blob', - - // Web APIs - 'File', - 'DataTransfer', - 'FormData', - 'URL' -]; - -const GENERIC_TYPES = [ - 'Array', - 'Set', - 'Map', - 'WeakMap', - 'WeakSet', - 'Promise', - 'Uint8Array', - 'Uint16Array', - 'Uint32Array', - 'Int8Array', - 'Int16Array', - 'Int32Array', - - // Object treated as a dictionary, e.g. Object.. - 'Object', - - // Object that contains a [Symbol.iterator]() method. - 'Iterable', - - // Object that contains next() method and satisfies the Iterator protocol. - // https://developer.mozilla.org/pl/docs/Web/JavaScript/Reference/Iteration_protocols - 'Iterator' -]; - -module.exports = { - BASIC_TYPES, - GENERIC_TYPES, - ALL_TYPES: [ ...BASIC_TYPES, ...GENERIC_TYPES ] -}; diff --git a/packages/jsdoc-plugins/lib/validator/validator.js b/packages/jsdoc-plugins/lib/validator/validator.js deleted file mode 100644 index 4b7109a04..000000000 --- a/packages/jsdoc-plugins/lib/validator/validator.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** - * @see http://usejsdoc.org/about-plugins.html - */ - -const DocletValidator = require( './doclet-validator' ); -const logger = require( 'jsdoc/util/logger' ); - -exports.handlers = { - processingComplete( e ) { - const isValidateOnly = Boolean( process.env.JSDOC_VALIDATE_ONLY ); - const isStrictCheck = Boolean( process.env.JSDOC_STRICT_CHECK ); - - const validator = new DocletValidator( e.doclets ); - const errors = validator.validate(); - - // Prints only first 50 errors to stdout. - printErrors( errors, 50 ); - - // Mark the process as ended with error. - if ( errors.length ) { - process.exitCode = 1; - - if ( isValidateOnly || isStrictCheck ) { - return logger.fatal( 'Since the process is being executed in strict or checking mode, aborting due to errors.' ); - } - } - - if ( isValidateOnly ) { - process.exit(); - } - } -}; - -function printErrors( errors, maxSize ) { - const errorMessages = []; - - if ( errors.length === 0 ) { - process.stdout.write( 'No errors found during the validation.' ); - - return; - } - - for ( const error of errors.slice( 0, maxSize ) ) { - errorMessages.push( error.message ); - errorMessages.push( `\tat ${ error.file }:${ error.line }\n` ); - } - - errorMessages.push( '\nValidation Summary:' ); - errorMessages.push( `* Validator found ${ errors.length } errors.\n` ); - - if ( errors.length > maxSize ) { - errorMessages.push( `* ${ errors.length - maxSize } errors not shown in the console.` ); - } - - process.stderr.write( errorMessages.join( '\n' ) ); -} diff --git a/packages/jsdoc-plugins/package.json b/packages/jsdoc-plugins/package.json deleted file mode 100644 index 3b5d6846e..000000000 --- a/packages/jsdoc-plugins/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@ckeditor/jsdoc-plugins", - "version": "43.0.0", - "description": "Various JSDoc plugins developed by the CKEditor 5 team.", - "keywords": [ - "jsdoc", - "jsdoc3", - "plugins", - "ckeditor", - "validator", - "validation", - "short references", - "inheritance" - ], - "author": "CKSource (http://cksource.com/)", - "license": "MIT", - "homepage": "https://github.com/ckeditor/ckeditor5-dev/tree/master/packages/jsdoc-plugins", - "bugs": "https://github.com/ckeditor/ckeditor5/issues", - "repository": { - "type": "git", - "url": "https://github.com/ckeditor/ckeditor5-dev.git", - "directory": "packages/jsdoc-plugins" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=5.7.1" - }, - "files": [ - "lib" - ], - "dependencies": { - "fs-extra": "^11.2.0", - "lodash": "^4.17.15", - "jsdoc": "ckeditor/jsdoc#fixed-trailing-comment-doclets" - }, - "devDependencies": { - "chai": "^4.2.0", - "glob": "^10.2.5", - "mocha": "^7.1.2", - "tmp": "^0.2.1", - "upath": "^2.0.1" - }, - "scripts": { - "test": "mocha './tests/**/*.js' --timeout 10000", - "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test" - } -} diff --git a/packages/jsdoc-plugins/tests/comment-fixer.js b/packages/jsdoc-plugins/tests/comment-fixer.js deleted file mode 100644 index 01c297b73..000000000 --- a/packages/jsdoc-plugins/tests/comment-fixer.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; diff --git a/packages/jsdoc-plugins/tests/convert-short-refs-to-full-refs.js b/packages/jsdoc-plugins/tests/convert-short-refs-to-full-refs.js deleted file mode 100644 index c72fb85de..000000000 --- a/packages/jsdoc-plugins/tests/convert-short-refs-to-full-refs.js +++ /dev/null @@ -1,529 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const convertShortRefsToFullRefs = require( '../lib/longname-fixer/fixers/convert-short-refs-to-full-refs' ); - -describe( 'jsdoc-plugins/longname-fixers/convert-short-refs-to-full-refs', () => { - it( 'should convert interface method short refs to full refs', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'interface', - memberof: 'module:editor/editorinterface', - longname: 'module:editor/editorinterface~EditorInterface', - name: 'EditorInterface', - meta: { - path: 'editor/', - filename: 'interface.js', - lineno: 20 - }, - comment: '', - description: '' - }, { - kind: 'function', - meta: { - path: 'editor/', - filename: 'interface.js', - lineno: 30 - }, - longname: '#destroy', - memberof: 'module:editor/editorinterface~EditorInterface', - name: 'destroy', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].memberof ).to.be.equal( 'module:editor/editorinterface~EditorInterface' ); - expect( doclets[ 1 ].longname ).to.be.equal( 'module:editor/editorinterface~EditorInterface#destroy' ); - } ); - - it( 'should convert mixin method short refs to full refs', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'mixin', - memberof: 'module:editor/editormixin', - longname: 'module:editor/editormixin~EditorMixin', - name: 'EditorMixin', - meta: { - path: 'editor/', - filename: 'editormixin.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'function', - meta: { - path: 'editor/', - filename: 'editormixin.js', - lineno: 30 - }, - longname: '#destroy', - memberof: 'module:editor/editormixin~EditorMixin', - name: 'destroy', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].memberof ).to.be.equal( 'module:editor/editormixin~EditorMixin' ); - expect( doclets[ 1 ].longname ).to.be.equal( 'module:editor/editormixin~EditorMixin#destroy' ); - } ); - - it( 'should not convert short refs if the memberof differs', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'interface', - memberof: 'module:editor/editorinterface', - longname: 'module:editor/editorinterface~EditorInterface', - name: 'EditorInterface', - meta: { - path: '/', - filename: 'interface.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'function', - meta: { - path: '/', - filename: 'interface.js', - lineno: 30 - }, - longname: 'module:someModule~SomeOtherClass#destroy', - memberof: 'module:someModule~SomeOtherClass', - name: 'destroy', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].memberof ).to.be.equal( 'module:someModule~SomeOtherClass' ); - expect( doclets[ 1 ].longname ).to.be.equal( 'module:someModule~SomeOtherClass#destroy' ); - } ); - - it( 'should fix class members starting with the `~` character', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'class', - memberof: 'module:editor', - longname: 'module:editor~Editor', - name: 'Editor', - meta: { - path: '/', - filename: 'editor.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'member', - meta: { - path: '/', - filename: 'editor.js', - lineno: 30 - }, - longname: '~Editor#name', - memberof: 'module:editor~Editor', - name: 'name', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].longname ).to.be.equal( 'module:editor~Editor#name' ); - expect( doclets[ 1 ].memberof ).to.be.equal( 'module:editor~Editor' ); - } ); - - it( 'should convert short event names to full names if the event is declared after the class/mixin/interface', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'class', - memberof: 'module:editor', - longname: 'module:editor~Editor', - name: 'Editor', - meta: { - path: '/', - filename: 'editor.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'event', - meta: { - path: '/', - filename: 'editor.js', - lineno: 30 - }, - name: 'blur', - longname: 'event:blur', - memberof: 'module:editor~Editor', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].longname ).to.be.equal( - 'module:editor~Editor#event:blur' - ); - } ); - - it( 'should not convert event name if there is no class/mixin/interface defined above the event', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'class', - memberof: 'module:editor', - longname: 'module:editor~Editor', - name: 'Editor', - meta: { - path: '/', - filename: 'editor.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'event', - meta: { - path: '/', - filename: 'editor.js', - lineno: 30 - }, - name: 'event:blur', - longname: 'module:editor2~Editor#event:blur', - memberof: 'module:editor2~Editor', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].longname ).to.be.equal( - 'module:editor2~Editor#event:blur' - ); - } ); - - it( 'should convert short refs in fires tag to full refs if a class/mixin/interface is declared above', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'class', - memberof: 'module:editor', - longname: 'module:editor~Editor', - name: 'Editor', - meta: { - path: '/', - filename: 'editor.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'function', - meta: { - path: '/', - filename: 'editor.js', - lineno: 30 - }, - name: 'execute', - fires: [ 'event:execute' ], - longname: 'module:editor~Editor#execute', - memberof: 'module:editor~Editor', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].fires[ 0 ] ).to.be.equal( - 'module:editor~Editor#event:execute' - ); - } ); - - it( 'should convert short refs in fires tag to full refs if a class/mixin/interface is declared above #2', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'class', - memberof: 'module:editor', - longname: 'module:editor~Editor', - name: 'Editor', - meta: { - path: '/', - filename: 'editor.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'function', - meta: { - path: '/', - filename: 'editor.js', - lineno: 30 - }, - name: 'attr', - fires: [ 'change:attribute' ], - longname: 'module:editor~Editor#attr', - memberof: 'module:editor~Editor', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].fires[ 0 ] ).to.be.equal( - 'module:editor~Editor#event:change:attribute' - ); - } ); - - it( 'should convert short refs in see tags to full refs', () => { - /** @type {Array.} */ - const doclets = [ { - kind: 'class', - memberof: 'module:editor', - longname: 'module:editor~Editor', - name: 'Editor', - meta: { - path: '/', - filename: 'editor.js', - lineno: 20 - }, - comment: '', - description: '' - }, - { - kind: 'function', - meta: { - path: '/', - filename: 'editor.js', - lineno: 30 - }, - name: 'attr', - see: [ '#create' ], - longname: 'module:editor~Editor#attr', - memberof: 'module:editor~Editor', - comment: '', - description: '' - } ]; - - convertShortRefsToFullRefs( doclets ); - - expect( doclets[ 1 ].see[ 0 ] ).to.be.equal( - 'module:editor~Editor#create' - ); - } ); -} ); - -describe( 'jsdoc-plugins/longname-fixer/fix-links', () => { - it( 'formatLinks()', () => { - /** @type {Doclet} */ - const doclet = { - name: 'EditorInterface', - comment: 'Creates {@link ~EditorInterface} instance', - description: '

Creates {@link ~EditorInterface} instance

', - memberof: 'module:ckeditor5/editor/editorinterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface', - kind: 'interface', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.comment ).to.be.equal( - 'Creates {@link module:ckeditor5/editor/editorinterface~EditorInterface} instance' - ); - expect( doclet.description ).to.be.equal( - '

Creates {@link module:ckeditor5/editor/editorinterface~EditorInterface} instance

' - ); - } ); - - it( 'formatLinks() hash', () => { - /** @type {Doclet} */ - const doclet = { - name: '#create', - comment: 'Method {@link #create} creates instance', - memberof: 'module:ckeditor5/editor/editorinterface~EditorInterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface#create', - kind: 'function', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.comment ).to.be.equal( - 'Method {@link module:ckeditor5/editor/editorinterface~EditorInterface#create} creates instance' - ); - } ); - - it( 'formatLinks() with link description', () => { - /** @type {Doclet} */ - const doclet = { - name: 'EditorInterface', - comment: 'Creates {@link ~EditorInterface editor} instance with a given name.', - memberof: 'module:ckeditor5/editor/editorinterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface', - kind: 'interface', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.comment ).to.be.equal( - 'Creates {@link module:ckeditor5/editor/editorinterface~EditorInterface editor} instance with a given name.' - ); - } ); - - it( 'formatLinks() with more complicated path', () => { - /** @type {Doclet} */ - const doclet = { - name: 'EditorInterface', - comment: 'Method {@link ~EditorInterface#create create} creates Editor', - memberof: 'module:ckeditor5/editor/editorinterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface', - kind: 'interface', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.comment ).to.be.equal( - 'Method {@link module:ckeditor5/editor/editorinterface~EditorInterface#create create} creates Editor' - ); - } ); - - it( 'formatLinks() in description', () => { - /** @type {Doclet} */ - const doclet = { - name: 'EditorInterface', - comment: '', - description: 'You can later destroy it with {@link ~EditorInterface#destroy}', - memberof: 'module:ckeditor5/editor/editorinterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface', - kind: 'interface', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.description ).to.be.equal( - 'You can later destroy it with {@link module:ckeditor5/editor/editorinterface~EditorInterface#destroy}' - ); - } ); - - it( 'formatLinks() multiple links', () => { - /** @type {Doclet} */ - const doclet = { - name: 'EditorInterface', - comment: '{@link #destroy} {@link #destroy}', - memberof: 'module:editor/editorinterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface', - kind: 'interface', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.comment ).to.be.equal( - '{@link module:editor/editorinterface#destroy} {@link module:editor/editorinterface#destroy}' - ); - } ); - - it( 'formatLinks() link to parent: class / interface', () => { - /** @type {Doclet} */ - const doclet = { - name: 'EditorInterface', - comment: '{@link ~EditorInterface}', - memberof: 'module:editor/editorinterface~EditorInterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface', - kind: 'interface', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.comment ).to.be.equal( - '{@link module:editor/editorinterface~EditorInterface}' - ); - } ); - - it( 'formatLinks() with multi-word link', () => { - /** @type {Doclet} */ - const doclet = { - name: 'EditorInterface', - comment: 'Creates {@link ~EditorInterface some editor} instance with a given name.', - memberof: 'module:ckeditor5/editor/editorinterface', - longname: 'module:ckeditor5/editor/editorinterface~EditorInterface', - kind: 'interface', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 0 } - }; - - convertShortRefsToFullRefs( [ doclet ] ); - - expect( doclet.comment ).to.be.equal( - 'Creates {@link module:ckeditor5/editor/editorinterface~EditorInterface some editor} ' + - 'instance with a given name.' - ); - } ); - - it( 'should fix links in error doclets', () => { - /** @type {Array.} */ - const doclets = [ { - name: 'some-error', - longname: 'module:errors~some-error', - memberof: 'module:errors', - kind: 'error', - comment: 'The {@link #constructor source} of a rect in an HTML element', - description: '

The {@link #constructor source} of a rect in an HTML element

', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 40 } - }, { - name: 'Rect', - memberof: 'module:ckeditor5-utils/dom/rect', - longname: 'module:ckeditor5-utils/dom/rect~Rect', - kind: 'class', - comment: '', - description: '', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 30 } - }, { - name: 'constructor', - longname: 'module:ckeditor5-utils/dom/rect~Rect#constructor', - memberof: 'module:ckeditor5-utils/dom/rect~Rect', - kind: 'function', - comment: '', - description: '', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 32 } - } ]; - - convertShortRefsToFullRefs( doclets ); - - const errorDoclet = doclets.find( d => d.longname === 'module:errors~some-error' ); - - expect( errorDoclet ).to.deep.equal( { - name: 'some-error', - longname: 'module:errors~some-error', - memberof: 'module:errors', - kind: 'error', - comment: 'The {@link module:ckeditor5-utils/dom/rect~Rect#constructor source} of a rect in an HTML element', - description: '

The {@link module:ckeditor5-utils/dom/rect~Rect#constructor source} of a rect in an HTML element

', - meta: { filename: 'foo.js', path: 'foo/bar', lineno: 40 } - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/doclet-collection.js b/packages/jsdoc-plugins/tests/doclet-collection.js deleted file mode 100644 index e570b79c0..000000000 --- a/packages/jsdoc-plugins/tests/doclet-collection.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const DocletCollection = require( '../lib/utils/doclet-collection' ); - -describe( 'collection', () => { - let collection; - - beforeEach( () => { - const docletA = { - name: 'ClassA', - longname: 'module:M~ClassA', - kind: 'class' - }; - const docletB = { - name: 'methodB', - longname: 'module:M~ClassA#methodB', - kind: 'method' - }; - collection = new DocletCollection(); - collection.add( docletA.kind, docletA ); - collection.add( docletB.kind, docletB ); - } ); - - it( 'should return doclets filtered by category', () => { - expect( collection.get( 'class' ) ).to.deep.equal( - [ - { - name: 'ClassA', - longname: 'module:M~ClassA', - kind: 'class' - } - ] - ); - } ); - - it( 'should return all doclets', () => { - expect( collection.getAll() ).to.deep.equal( - [ - { - name: 'ClassA', - longname: 'module:M~ClassA', - kind: 'class' - }, - { - name: 'methodB', - longname: 'module:M~ClassA#methodB', - kind: 'method' - } - ] - ); - } ); - - it( 'should return all longnames of stored doclets', () => { - expect( collection.getAllLongnames() ).to.deep.equal( - [ - 'module:M~ClassA', - 'module:M~ClassA#methodB' - ] - ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/doclet-validator.js b/packages/jsdoc-plugins/tests/doclet-validator.js deleted file mode 100644 index 07962c989..000000000 --- a/packages/jsdoc-plugins/tests/doclet-validator.js +++ /dev/null @@ -1,600 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const DocletValidator = require( '../lib/validator/doclet-validator.js' ); - -// TODO - These tests should be rewritten to use doclets created from the API docs extraction. -describe( 'jsdoc-plugins/Validator', () => { - it( '_validateMembers()', () => { - const validator = new DocletValidator( [ { - kind: 'member', - name: 'module:ckeditor5/wrong_path', - scope: 'inner', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateMembers(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - describe( '_validateMemberofProperty()', () => { - it( 'should emit error if the doclet comes from inner variable', () => { - const validator = new DocletValidator( [ { - kind: 'member', - name: 'module:ckeditor5/path', - memberof: '', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateMemberofProperty(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - it( 'should emit error if the doclet contains invalid `memberof` property', () => { - const validator = new DocletValidator( [ { - kind: 'member', - name: 'module:ckeditor5/wrong_path', - memberof: 'wrongMemberof', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateMemberofProperty(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - it( 'should not emit error when the doclets\' `memberof` starts with `module:`', () => { - const validator = new DocletValidator( [ { - kind: 'member', - name: 'module:ckeditor5/editor', - memberof: 'module:ckeditor5/editor', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateMemberofProperty(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should emit error if the doclet comes from undocumented code', () => { - const validator = new DocletValidator( [ { - kind: 'member', - undocumented: 'true', - name: '⌘', - memberof: 'macGlyphsToModifiers', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateMemberofProperty(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - } ); - - describe( 'linting function parameters', () => { - it( 'should handle not existing types', () => { - const validator = new DocletValidator( [ { - kind: 'function', - params: [ { - type: { - names: [ 'module:engine/ckeditor5/editor' ] - } - } ], - longname: 'abc', - scope: 'inner', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateParameters(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - it( 'should log an error if a module is passed as parameter type', () => { - const validator = new DocletValidator( [ { - kind: 'class', - params: [ { - type: { - names: [ 'module:engine/ckeditor5/editor' ] - } - } ], - meta: { fileName: '', path: '' } - }, { - kind: 'module', - longname: 'module:engine/ckeditor5/editor', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateParameters(); - - expect( validator.errors.length ).to.be.equal( 1 ); - expect( validator.errors[ 0 ].message ).to.equal( 'Incorrect param type: module:engine/ckeditor5/editor' ); - } ); - - it( 'should not log an error if an allowed member is passed as parameter type', () => { - const validator = new DocletValidator( [ { - kind: 'class', - params: [ { - type: { - names: [ 'module:engine/foo~ClassFoo' ] - } - }, { - type: { - names: [ 'module:engine/foo~InterfaceBar' ] - } - }, { - type: { - names: [ 'module:engine/foo~TypedefBaz' ] - } - }, { - type: { - names: [ 'module:engine/foo~FunctionAbc' ] - } - } ], - meta: { fileName: '', path: '' } - }, { - kind: 'class', - longname: 'module:engine/foo~ClassFoo', - meta: { fileName: '', path: '' } - }, { - kind: 'interface', - longname: 'module:engine/foo~InterfaceBar', - meta: { fileName: '', path: '' } - }, { - kind: 'typedef', - longname: 'module:engine/foo~TypedefBaz', - meta: { fileName: '', path: '' } - }, { - kind: 'function', - longname: 'module:engine/foo~FunctionAbc', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateParameters(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should handle built-in types', () => { - const validator = new DocletValidator( [ { - kind: 'class', - params: [ { - type: { - names: [ - 'String', - 'Array', - 'Number' - ] - } - } ], - meta: { fileName: '', path: '' } - } ] ); - - validator._validateParameters(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should log an error if an incorrect type is passed', () => { - const validator = new DocletValidator( [ { - kind: 'class', - params: [ { - type: { - names: [ - 'String', - 'Array', - 'Wrong' - ] - } - } ], - meta: { fileName: '', path: '' } - } ] ); - - validator._validateParameters(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - } ); - - describe( 'linting links', () => { - it( 'should validate links and adds errors if they are incorrect', () => { - const validator = new DocletValidator( [ { - comment: - `* {@link module:utils/a~A#method1} - * {@link module:utils/b~Some1} `, - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.be.equal( 2 ); - } ); - - it( 'should not produce errors if links are correct', () => { - const validator = new DocletValidator( [ { - comment: - `/** Linking test:\n *\n * * a:\n * - * {@link module:ckeditor5/a~A} `, - meta: { fileName: '', path: '' } - }, { - comment: '', - longname: 'module:ckeditor5/a~A', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should validate links with name', () => { - const validator = new DocletValidator( [ { - comment: ' {@link module:ckeditor5/a~A classA} ', - meta: { fileName: '', path: '' } - }, { - comment: '', - longname: 'module:ckeditor5/a~A', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should validate links with white spaces', () => { - const validator = new DocletValidator( [ { - comment: ' {@link \n module:ckeditor5/a~A \n\t classA} ', - meta: { fileName: '', path: '' } - }, { - comment: '', - longname: 'module:ckeditor5/a~A', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should validate links with multi-word link', () => { - const validator = new DocletValidator( [ { - comment: ' {@link module:ckeditor5/a~A with multi word link} ', - meta: { fileName: '', path: '' } - }, { - comment: '', - longname: 'module:ckeditor5/a~A', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should validate links that contain double link keyword', () => { - const validator = new DocletValidator( [ { - comment: '{@link @link module:ckeditor5/a~A with multi word link} ', - meta: { fileName: '', path: '' } - }, { - comment: '', - longname: 'module:ckeditor5/a~A', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.be.equal( 1 ); - expect( validator.errors[ 0 ].message ).to.match( /Incorrect link:/ ); - } ); - - it( 'should validate links that does not contain the @link part', () => { - const validator = new DocletValidator( [ { - comment: ` - @param {module:ckeditor5/a~A} - @returns {module:ckeditor5/a~A} - Correct link: {@link module:ckeditor5/a~A} - Random comment: {priority: 'high'} - Invalid link: {module:ckeditor5/a~B} - `, - meta: { fileName: '', path: '' } - }, { - comment: '', - longname: 'module:ckeditor5/a~A', - meta: { fileName: '', path: '' } - }, { - comment: '', - longname: 'module:ckeditor5/a~B', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.be.equal( 1 ); - expect( validator.errors[ 0 ].message ).to.match( /Link misses the '@link' part: \{module:ckeditor5\/a~B\}/ ); - } ); - - it( 'should validate comment without any link', () => { - const validator = new DocletValidator( [ { - comment: 'Some comment without any valid nor invalid link', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateLinks(); - - expect( validator.errors.length ).to.equal( 0 ); - } ); - } ); - - describe( 'linting extensibility', () => { - it( 'should assert that the type in the `@extends` tag exsits (positive)', () => { - const validator = new DocletValidator( [ { - kind: 'class', - longname: 'module:abc/SomeClass', - augments: [ 'module:abc/SomeOtherClass' ], - meta: { fileName: '', path: '' } - }, { - kind: 'class', - longname: 'module:abc/SomeOtherClass', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateExtensibility(); - - expect( validator.errors.length ).to.equal( 0 ); - } ); - - it( 'should assert that the type in the `@extends` tag exsits (negative)', () => { - const validator = new DocletValidator( [ { - kind: 'class', - longname: 'module:abc/SomeClass', - augments: [ 'module:abc/SomeOtherClass' ], - meta: { fileName: '', path: '' } - } ] ); - - validator._validateExtensibility(); - - expect( validator.errors.length ).to.equal( 1 ); - expect( validator.errors[ 0 ].message ).to.equal( 'Invalid @extends reference: module:abc/SomeOtherClass.' ); - } ); - } ); - - describe( 'linting events', () => { - it( 'should assert that references in `fires` exist (positive)', () => { - const validator = new DocletValidator( [ { - kind: 'class', - longname: 'module:abc/SomeClass', - meta: { fileName: '', path: '' }, - fires: [ 'someEvent' ] - } ] ); - - validator._validateEvents(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - it( 'should assert that references in `fires` exist (negative)', () => { - const validator = new DocletValidator( [ { - kind: 'class', - longname: 'module:abc/SomeClass', - meta: { fileName: '', path: '' }, - fires: [ 'module:abc/SomeClass#event:someEvent' ] - }, { - kind: 'event', - longname: 'module:abc/SomeClass#event:someEvent' - } ] ); - - validator._validateEvents(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'should assert that references in `fires` are only events', () => { - const validator = new DocletValidator( [ { - kind: 'class', - longname: 'module:abc/SomeClass', - meta: { fileName: '', path: '' }, - fires: [ 'module:abc/SomeClass#event:someEvent' ] - }, { - kind: 'not-event', - longname: 'module:abc/SomeClass#event:someEvent' - } ] ); - - validator._validateEvents(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - } ); - - it( '_validateModuleDocumentedExports() - show errors', () => { - const validator = new DocletValidator( [ { - kind: 'member', - scope: 'inner', - memberof: 'module:utils/emittermixin', - meta: { fileName: '', path: '' } - }, { - kind: 'module', - longname: 'module:utils/emittermixin', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateModuleDocumentedExports(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - it( '_validateModuleDocumentedExports()', () => { - const validator = new DocletValidator( [ { - kind: 'member', - scope: 'inner', - memberof: 'module:utils/emittermixin~EmitterMixin', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateModuleDocumentedExports(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'lintSeeReferences()', () => { - const validator = new DocletValidator( [ { - kind: 'class', - longname: 'module:utils/emittermixin~EmitterMixin', - see: [ - 'module:utils/emittermixin~EmitterMixin#constructor' - ], - meta: { fileName: '', path: '' } - }, { - kind: 'member', - longname: 'module:utils/emittermixin~EmitterMixin#constructor', - meta: { fileName: '', path: '' } - } ] ); - - validator._validateSeeReferences(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( 'lintSeeReferences() - invalid', () => { - const validator = new DocletValidator( [ { - kind: 'class', - see: [ - 'module:utils/emittermixin~EmitterMixin' - ], - meta: { fileName: '', path: '' } - } ] ); - - validator._validateSeeReferences(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - it( '_validateTypedefs()', () => { - const validator = new DocletValidator( [ { - type: 'typedef', - properties: [ { - type: { - names: [ - 'String', - 'Map.' - ] - } - } ], - meta: { fileName: '', path: '' } - } ] ); - - validator._validateTypedefs(); - - expect( validator.errors.length ).to.be.equal( 0 ); - } ); - - it( '_validateTypedefs() - invalid', () => { - const validator = new DocletValidator( [ { - type: 'typedef', - properties: [ { - type: { - names: [ - 'Abc' - ] - } - } ], - meta: { fileName: '', path: '' } - } ] ); - - validator._validateTypedefs(); - - expect( validator.errors.length ).to.be.equal( 1 ); - } ); - - describe( '_isCorrectType', () => { - it( 'Should validate union type', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'String|Array' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should validate generic type (Array.)', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'Array.' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should validate generic union type', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'Array.' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should validate union type with parenthesis', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'Array.<(Object|String)>' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should validate incorrect union type', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'Array.<(Object|IncorrectType)>' ); - - expect( result ).to.be.equal( false ); - } ); - - it( 'Should validate full path to class', () => { - const validator = new DocletValidator( [ { - longname: 'module:core/editor~Editor', - kind: 'function', - meta: { fileName: '', path: '' } - } ] ); - - const result = validator._isCorrectType( 'Array.' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should validate generic type (Array)', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'Array.<*>' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should validate invalid generic type', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'String.' ); - - expect( result ).to.be.equal( false ); - } ); - - it( 'Should validate string literal type', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( '\'forward\'' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should validate generic type with more than one template type', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( 'Object.' ); - - expect( result ).to.be.equal( true ); - } ); - - it( 'Should accept spaces', () => { - const validator = new DocletValidator( [] ); - const result = validator._isCorrectType( ' String | Number | Boolean ' ); - - expect( result ).to.be.equal( true ); - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/fix-code-snippets.js b/packages/jsdoc-plugins/tests/fix-code-snippets.js deleted file mode 100644 index eaf88c364..000000000 --- a/packages/jsdoc-plugins/tests/fix-code-snippets.js +++ /dev/null @@ -1,194 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { expect } = require( 'chai' ); -const fixCodeSnippets = require( '../lib/fix-code-snippets' ); - -describe( 'jsdoc-plugins/fix-code-snippets', () => { - it( 'should remove leading tabs ina multi-line code snippets', () => { - const doclet = { - description: '
\tfoo\n\tbar
' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.description ).to.equal( - '
foo\nbar
' - ); - } ); - - it( 'should remove leading tabs in a single-line code snippet', () => { - const doclet = { - description: '
\tfoo
' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.description ).to.equal( - '
foo
' - ); - } ); - - it( 'should remove leading tabs in a description containing multiple code snippets', () => { - const doclet = { - description: - '
\tfoo
' + - 'foo' + - '
\tbar
' + - 'bar' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.description ).to.equal( - '
foo
' + - 'foo' + - '
bar
' + - 'bar' - ); - } ); - - it( 'should remove multiple leading tabs', () => { - const doclet = { - description: - '
\t\tfoo\n\t\tbar
' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.description ).to.equal( - '
foo\nbar
' - ); - } ); - - it( 'should remove leading spaces', () => { - const doclet = { - description: - '
   foo\n   bar
' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.description ).to.equal( - '
foo\nbar
' - ); - } ); - - it( 'should remove leading tabs in code snippet containing empty lines', () => { - const doclet = { - description: - '
\t\tfoo\n\n\t\tbar
' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.description ).to.equal( - '
foo\n\nbar
' - ); - } ); - - it( 'should not remove correct code snippet tabs', () => { - const doclet = { - description: - '
\tfoo\n\t\tbar\n\t\t\tbaz
' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.description ).to.equal( - '
foo\n\tbar\n\t\tbaz
' - ); - } ); - - it( 'should fix classdesc in classes', () => { - const doclet = { - classdesc: - '
\tfoo\n\tbar
' - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.classdesc ).to.equal( - '
foo\nbar
' - ); - } ); - - it( 'should fix property descriptions', () => { - const doclet = { - properties: [ { - name: 'foo', - description: '
\tfoo\n\tbar
' - }, { - name: 'bar', - description: '
\tfoo\n\tbar
' - }, { - name: 'baz' - // The `baz` property has no description. - } ] - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.properties[ 0 ].description ).to.equal( - '
foo\nbar
' - ); - - expect( doclet.properties[ 1 ].description ).to.equal( - '
foo\nbar
' - ); - } ); - - it( 'should fix descriptions of the return tags', () => { - const doclet = { - returns: [ { - type: 'String', - description: '
\tfoo\n\tbar
' - }, { - type: 'Array', - description: '
\tfoo\n\tbar
' - }, { - type: 'Boolean' - // Third return overload has no description. - } ] - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.returns[ 0 ].description ).to.equal( - '
foo\nbar
' - ); - - expect( doclet.returns[ 1 ].description ).to.equal( - '
foo\nbar
' - ); - } ); - - it( 'should fix descriptions of parameters', () => { - const doclet = { - params: [ { - name: 'foo', - description: '
\tfoo\n\tbar
' - }, { - name: 'bar', - description: '
\tfoo\n\tbar
' - }, { - name: 'baz' - // The `baz` parameter has no description. - } ] - }; - - fixCodeSnippets.handlers.parseComplete( { doclets: [ doclet ] } ); - - expect( doclet.params[ 0 ].description ).to.equal( - '
foo\nbar
' - ); - - expect( doclet.params[ 1 ].description ).to.equal( - '
foo\nbar
' - ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/integration-tests/_utils/extract-api-docs.js b/packages/jsdoc-plugins/tests/integration-tests/_utils/extract-api-docs.js deleted file mode 100644 index 1ec5133dc..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/_utils/extract-api-docs.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -const tmp = require( 'tmp' ); -const fs = require( 'fs' ); -const { execSync } = require( 'child_process' ); -const { globSync } = require( 'glob' ); -const upath = require( 'upath' ); - -module.exports = async function extractApiDocs( dirname ) { - const filePattern1 = upath.join( dirname, '/input/**/*.jsdoc' ); - const filePattern2 = upath.join( dirname, '/input.jsdoc' ); - - const files = [ - ...globSync( filePattern1 ), - ...globSync( filePattern2 ) - ]; - - if ( files.length === 0 ) { - throw new Error( - `No file matching the '${ filePattern1 }' pattern was found by the 'extractApiDocs' test utility.` - ); - } - - const jsDocConfig = { - plugins: [ - require.resolve( '../../../lib/export-fixer/export-fixer' ), - require.resolve( '../../../lib/custom-tags/error' ), - require.resolve( '../../../lib/custom-tags/observable' ), - require.resolve( '../../../lib/observable-event-provider' ), - require.resolve( '../../../lib/relation-fixer' ), - require.resolve( '../../../lib/longname-fixer/longname-fixer' ), - require.resolve( '../../../lib/event-extender/event-extender' ), - require.resolve( '../../../lib/cleanup' ), - - // The logger prints the JSON to stdout. - // This way the generated structure can be fetched by integration tests. - require.resolve( './logger' ) - ], - source: { - include: files - }, - opts: { - encoding: 'utf8', - recurse: true, - access: 'all', - template: 'templates/silent' - } - }; - - const tmpConfig = tmp.fileSync(); - - fs.writeFileSync( tmpConfig.name, JSON.stringify( jsDocConfig ) ); - - const cmd = require.resolve( 'jsdoc/jsdoc.js' ); - - const rawOutput = execSync( `node ${ cmd } -c ${ tmpConfig.name }` ).toString(); - - const doclets = JSON.parse( rawOutput ).doclets; - - // Preserve only filename for doclet identification metadata. - for ( const doclet of doclets ) { - if ( doclet.meta ) { - doclet.meta = { - filename: doclet.meta.filename - }; - } - } - - return doclets; -}; diff --git a/packages/jsdoc-plugins/tests/integration-tests/_utils/logger.js b/packages/jsdoc-plugins/tests/integration-tests/_utils/logger.js deleted file mode 100644 index 8a852b3bb..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/_utils/logger.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -exports.handlers = { - processingComplete( e ) { - console.log( JSON.stringify( e, null, 4 ) ); - } -}; diff --git a/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/class-inheriting-from-mixins.js b/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/class-inheriting-from-mixins.js deleted file mode 100644 index 476113be2..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/class-inheriting-from-mixins.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const extractApiDocs = require( '../_utils/extract-api-docs' ); -const { cloneDeep } = require( 'lodash' ); -const { expect } = require( 'chai' ); - -describe( 'integration test/class inheriting from mixins', () => { - /** @type {Array.} */ - let originalApiDocs; - - /** @type {Array.} */ - let apiDocs; - - before( async () => { - originalApiDocs = await extractApiDocs( __dirname ); - } ); - - beforeEach( () => { - apiDocs = cloneDeep( originalApiDocs ); - } ); - - describe( 'interfaces and mixins', () => { - it( 'EmitterMixin doclet should contain correct longname', () => { - const emitterMixinDoclet = apiDocs.find( d => d.name === 'EmitterMixin' ); - - expect( emitterMixinDoclet.longname ).to.equal( 'module:utils/emittermixin~EmitterMixin' ); - expect( emitterMixinDoclet.memberof ).to.equal( 'module:utils/emittermixin' ); - } ); - - it( 'EmitterMixin doclet should be generated only once', () => { - const emitterMixinDoclets = apiDocs.filter( d => d.name === 'EmitterMixin' ); - - expect( emitterMixinDoclets.length ).to.equal( 1 ); - } ); - - it( 'Emitter#on doclet should be generated for the `on` method', () => { - const emitterOnDoclet = apiDocs.find( d => d.longname == 'module:utils/emittermixin~Emitter#on' ); - - expect( emitterOnDoclet ).to.be.an( 'object' ); - expect( emitterOnDoclet.description ).to.equal( - 'Registers a callback function to be executed when an event is fired.' - ); - - expect( emitterOnDoclet.scope ).to.equal( 'instance' ); - expect( emitterOnDoclet.memberof ).to.equal( 'module:utils/emittermixin~Emitter' ); - } ); - - it( 'EmitterMixin#on doclet should be generated for the `on` method and should inherit docs ', () => { - const emitterMixinOnDoclet = apiDocs.find( d => d.longname == 'module:utils/emittermixin~EmitterMixin#on' ); - - expect( emitterMixinOnDoclet ).to.be.an( 'object' ); - - expect( emitterMixinOnDoclet.description ).to.equal( - 'Registers a callback function to be executed when an event is fired.' - ); - - expect( emitterMixinOnDoclet.scope ).to.equal( 'instance' ); - expect( emitterMixinOnDoclet.memberof ).to.equal( 'module:utils/emittermixin~EmitterMixin' ); - } ); - } ); - - describe( 'class extending mixins that implements interfaces', () => { - it( 'doclet for the Emitter class should be generated', () => { - const emitterDoclet = apiDocs.find( d => d.longname == 'module:engine/emitter~Emitter' ); - - expect( emitterDoclet ).to.be.an( 'object' ); - - expect( emitterDoclet.implementsNested ).to.deep.equal( [ - 'module:utils/emittermixin~Emitter' - ] ); - - expect( emitterDoclet.mixesNested ).to.deep.equal( [ - 'module:utils/emittermixin~EmitterMixin' - ] ); - } ); - - it( 'doclet for the `on` method should be generated', () => { - const emitterOnDoclet = apiDocs.find( d => d.longname == 'module:engine/emitter~Emitter#on' ); - - expect( emitterOnDoclet ).to.be.an( 'object' ); - - expect( emitterOnDoclet.scope ).to.equal( 'instance' ); - expect( emitterOnDoclet.memberof ).to.equal( 'module:engine/emitter~Emitter' ); - - expect( emitterOnDoclet.mixed ).to.equal( true ); - } ); - } ); -} ); - diff --git a/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/class.jsdoc b/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/class.jsdoc deleted file mode 100644 index c99f10436..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/class.jsdoc +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @module engine/emitter - */ - -/** - * A class mixing EmitterMixin - * - * @mixes module:utils/emittermixin~EmitterMixin - */ -export default class Emitter { - /** - * @inheritDoc - */ - on() {} -} diff --git a/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/mixin.jsdoc b/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/mixin.jsdoc deleted file mode 100644 index 6e8a30a4e..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/class-inheriting-from-mixins/input/mixin.jsdoc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @module utils/emittermixin - */ - -/** - * Mixin that injects the {@link ~Emitter events API} into its host. - * - * @mixin EmitterMixin - * @implements module:utils/emittermixin~Emitter - */ -const EmitterMixin = { - /** - * @inheritDoc - */ - on( event, callback, options = {} ) { - this.listenTo( this, event, callback, options ); - } -}; - -export default EmitterMixin; - -/** - * Emitter/listener interface. - * - * @interface Emitter - */ - -/** - * Registers a callback function to be executed when an event is fired. - * - * @method #on - * @param {String} event The name of the event. - * @param {Function} callback The function to be called on event. - * @param {Object} [options={}] Additional options. - */ diff --git a/packages/jsdoc-plugins/tests/integration-tests/class-static-methods/class-static-methods.js b/packages/jsdoc-plugins/tests/integration-tests/class-static-methods/class-static-methods.js deleted file mode 100644 index a7976e107..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/class-static-methods/class-static-methods.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const extractApiDocs = require( '../_utils/extract-api-docs' ); -const { cloneDeep } = require( 'lodash' ); -const { expect } = require( 'chai' ); - -describe( 'integration test/class-static-methods', () => { - /** @type {Array.} */ - let originalApiDocs; - - /** @type {Array.} */ - let apiDocs; - - before( async () => { - originalApiDocs = await extractApiDocs( __dirname ); - } ); - - beforeEach( () => { - apiDocs = cloneDeep( originalApiDocs ); - } ); - - describe( 'class static methods', () => { - it( 'doclets should be generated', () => { - expect( apiDocs.length ).to.equal( 3 ); - } ); - - it( 'doclet for static property (getter + setter) should be generated', () => { - const doclet = apiDocs.find( doclet => doclet.longname === 'module:foo/bar~Foo.foo' ); - - expect( doclet ).to.deep.equal( { - comment: '/**\n\t * Static foo property.\n\t */', - meta: { filename: 'input.jsdoc' }, - description: 'Static foo property.', - name: 'foo', - longname: 'module:foo/bar~Foo.foo', - kind: 'member', - memberof: 'module:foo/bar~Foo', - scope: 'static', - params: [] - } ); - } ); - - it( 'doclet for static method should be generated', () => { - const doclet = apiDocs.find( doclet => doclet.longname === 'module:foo/bar~Foo.get' ); - - expect( doclet ).to.deep.equal( { - comment: '/**\n\t * Gets some value.\n\t */', - meta: { filename: 'input.jsdoc' }, - description: 'Gets some value.', - name: 'get', - longname: 'module:foo/bar~Foo.get', - kind: 'function', - memberof: 'module:foo/bar~Foo', - scope: 'static', - params: [] - } ); - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/integration-tests/class-static-methods/input.jsdoc b/packages/jsdoc-plugins/tests/integration-tests/class-static-methods/input.jsdoc deleted file mode 100644 index f0eed5aa1..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/class-static-methods/input.jsdoc +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @module foo/bar - */ - -export default class Foo { - /** - * Gets some value. - */ - static get() { - - } - - /** - * Static foo property. - */ - static get foo() { - return []; - } - - static set foo( value ) { - - } -} diff --git a/packages/jsdoc-plugins/tests/integration-tests/class/class-basics.js b/packages/jsdoc-plugins/tests/integration-tests/class/class-basics.js deleted file mode 100644 index 07f86dd8d..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/class/class-basics.js +++ /dev/null @@ -1,157 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const extractApiDocs = require( '../_utils/extract-api-docs' ); -const { cloneDeep } = require( 'lodash' ); -const { expect } = require( 'chai' ); - -describe( 'integration test/exported-class', () => { - /** @type {Array.} */ - let originalApiDocs; - - /** @type {Array.} */ - let apiDocs; - - before( async () => { - originalApiDocs = await extractApiDocs( __dirname ); - } ); - - beforeEach( () => { - apiDocs = cloneDeep( originalApiDocs ); - } ); - - it( 'should generate a doclet for class', () => { - const classDoclets = apiDocs.filter( doclet => doclet.longname === 'module:foo/bar/baz~Foo' ); - - expect( classDoclets.length ).to.equal( 1 ); - - expect( classDoclets[ 0 ] ).to.deep.equal( { - comment: '/**\n * Docs for class `Foo`.\n */', - meta: { filename: 'input.jsdoc' }, - classdesc: 'Docs for class `Foo`.', - name: 'Foo', - longname: 'module:foo/bar/baz~Foo', - kind: 'class', - scope: 'inner', - memberof: 'module:foo/bar/baz', - augmentsNested: [], - implementsNested: [], - mixesNested: [], - descendants: [ - 'module:foo/bar/baz~Bar', - 'module:foo/bar/baz~Baz' - ] - } ); - } ); - - it( 'should generate a doclet for a class constructor', () => { - const constructorDoclets = apiDocs.filter( doclet => doclet.longname === 'module:foo/bar/baz~Foo#constructor' ); - - expect( constructorDoclets.length ).to.equal( 1 ); - - expect( constructorDoclets[ 0 ] ).to.deep.equal( { - comment: '/**\n\t * Docs for the constructor.\n\t */', - meta: { filename: 'input.jsdoc' }, - description: 'Docs for the constructor.', - name: 'constructor', - longname: 'module:foo/bar/baz~Foo#constructor', - kind: 'function', - memberof: 'module:foo/bar/baz~Foo', - scope: 'instance', - params: [] - } ); - } ); - - it( 'should generate a doclet for an inherited class', () => { - const classDoclets = apiDocs.filter( doclet => doclet.longname === 'module:foo/bar/baz~Bar' ); - - expect( classDoclets.length ).to.equal( 1 ); - - expect( classDoclets[ 0 ] ).to.deep.equal( { - comment: '/**\n * Docs for the `Bar` class.\n *\n * @extends module:foo/bar/baz~Foo\n */', - meta: { filename: 'input.jsdoc' }, - classdesc: 'Docs for the `Bar` class.', - name: 'Bar', - longname: 'module:foo/bar/baz~Bar', - kind: 'class', - scope: 'inner', - memberof: 'module:foo/bar/baz', - augments: [ - 'module:foo/bar/baz~Foo' - ], - augmentsNested: [ - 'module:foo/bar/baz~Foo' - ], - implementsNested: [], - mixesNested: [], - descendants: [] - } ); - } ); - - it( 'should generate a doclet for an inherited class constructor', () => { - const constructorDoclets = apiDocs.filter( doclet => doclet.longname === 'module:foo/bar/baz~Bar#constructor' ); - - expect( constructorDoclets.length ).to.equal( 1 ); - - expect( constructorDoclets[ 0 ] ).to.deep.equal( { - comment: '/**\n\t * Docs for the constructor.\n\t */', - description: 'Docs for the constructor.', - meta: { filename: 'input.jsdoc' }, - inherited: true, - name: 'constructor', - longname: 'module:foo/bar/baz~Bar#constructor', - kind: 'function', - memberof: 'module:foo/bar/baz~Bar', - scope: 'instance', - params: [] - } ); - } ); - - it( 'should generate a doclet for an inherited class with its own docs', () => { - const classDoclets = apiDocs.filter( doclet => doclet.longname === 'module:foo/bar/baz~Baz' ); - - expect( classDoclets.length ).to.equal( 1 ); - - expect( classDoclets[ 0 ] ).to.deep.equal( { - comment: '/**\n * Docs for the `Baz` class.\n *\n * @extends module:foo/bar/baz~Foo\n */', - meta: { filename: 'input.jsdoc' }, - classdesc: 'Docs for the `Baz` class.', - name: 'Baz', - longname: 'module:foo/bar/baz~Baz', - kind: 'class', - scope: 'inner', - memberof: 'module:foo/bar/baz', - augments: [ - 'module:foo/bar/baz~Foo' - ], - augmentsNested: [ - 'module:foo/bar/baz~Foo' - ], - implementsNested: [], - mixesNested: [], - descendants: [] - } ); - } ); - - it( 'should generate a doclet for an inherited class constructor with its own docs', () => { - const constructorDoclets = apiDocs.filter( doclet => doclet.longname === 'module:foo/bar/baz~Baz#constructor' ); - - expect( constructorDoclets.length ).to.equal( 1 ); - - expect( constructorDoclets[ 0 ] ).to.deep.equal( { - comment: '/**\n\t * Docs for the `Baz` class constructor.\n\t */', - description: 'Docs for the `Baz` class constructor.', - meta: { filename: 'input.jsdoc' }, - name: 'constructor', - longname: 'module:foo/bar/baz~Baz#constructor', - kind: 'function', - memberof: 'module:foo/bar/baz~Baz', - scope: 'instance', - params: [] - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/integration-tests/class/input.jsdoc b/packages/jsdoc-plugins/tests/integration-tests/class/input.jsdoc deleted file mode 100644 index b27992e17..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/class/input.jsdoc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @module foo/bar/baz - */ - -/** - * Docs for class `Foo`. - */ -export class Foo { - /** - * Docs for the constructor. - */ - constructor() { - this.undocumentedVariable = true; - } -} - -/** - * Docs for the `Bar` class. - * - * @extends module:foo/bar/baz~Foo - */ -export class Bar extends Foo { - /** - * @inheritDoc - */ - constructor() { - super(); - - this.undocumentedVariable = false; - } -} - -/** - * Docs for the `Baz` class. - * - * @extends module:foo/bar/baz~Foo - */ -export class Baz extends Foo { - /** - * Docs for the `Baz` class constructor. - */ - constructor() { - super(); - - this.undocumentedVariable = false; - } -} - diff --git a/packages/jsdoc-plugins/tests/integration-tests/events-basics/event-basics.js b/packages/jsdoc-plugins/tests/integration-tests/events-basics/event-basics.js deleted file mode 100644 index 0ce95e5ff..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/events-basics/event-basics.js +++ /dev/null @@ -1,145 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const extractApiDocs = require( '../_utils/extract-api-docs' ); -const { cloneDeep } = require( 'lodash' ); -const { expect } = require( 'chai' ); - -describe( 'integration test/event basics', () => { - /** @type {Array.} */ - let originalApiDocs; - - /** @type {Array.} */ - let apiDocs; - - before( async () => { - originalApiDocs = await extractApiDocs( __dirname ); - } ); - - beforeEach( () => { - apiDocs = cloneDeep( originalApiDocs ); - } ); - - describe( 'integration/events basics', () => { - it( 'should contain 5 doclets', () => { - expect( apiDocs.length ).to.equal( 5 ); - } ); - - it( 'should contain the module doclet', () => { - const doclet = apiDocs.find( d => d.longname === 'module:foo' ); - - expect( doclet ).to.deep.equal( { - 'comment': '/**\n * @module foo\n */', - 'meta': { - 'filename': 'input.jsdoc' - }, - 'kind': 'module', - 'name': 'foo', - 'longname': 'module:foo' - } ); - } ); - - it( 'should include the class doclet', () => { - const doclet = apiDocs.find( d => d.longname === 'module:foo~Foo' ); - - expect( doclet ).to.deep.equal( { - 'comment': '/**\n * The `Foo` class documentation.\n */', - 'meta': { - 'filename': 'input.jsdoc' - }, - 'classdesc': 'The `Foo` class documentation.', - 'name': 'Foo', - 'longname': 'module:foo~Foo', - 'kind': 'class', - 'memberof': 'module:foo', - 'scope': 'inner', - 'descendants': [], - 'augmentsNested': [], - 'mixesNested': [], - 'implementsNested': [] - } ); - } ); - - it( 'should include a doclet for event declared outside the class', () => { - const doclet = apiDocs.find( d => d.longname === 'module:foo~Foo#event:outside' ); - - expect( doclet ).to.deep.equal( { - 'comment': '/**\n * An event documented outside the class.\n *\n * @event outside\n */', - 'meta': { - 'filename': 'input.jsdoc' - }, - 'description': 'An event documented outside the class.', - 'kind': 'event', - 'name': 'outside', - 'longname': 'module:foo~Foo#event:outside', - 'scope': 'instance', - 'memberof': 'module:foo~Foo', - 'params': [ - { - 'type': { - 'names': [ - 'module:utils/eventinfo~EventInfo' - ] - }, - 'description': '

An object containing information about the fired event.

', - 'name': 'eventInfo' - } - ] - } ); - } ); - - it( 'should include a doclet for event declared at the bottom of the class body', () => { - const doclet = apiDocs.find( d => d.longname === 'module:foo~Foo#event:inside' ); - - expect( doclet ).to.deep.equal( { - 'comment': '/**\n\t * An event documented inside the class.\n\t *\n\t * @event inside\n\t */', - 'meta': { - 'filename': 'input.jsdoc' - }, - 'description': 'An event documented inside the class.', - 'kind': 'event', - 'name': 'inside', - 'longname': 'module:foo~Foo#event:inside', - 'scope': 'instance', - 'memberof': 'module:foo~Foo', - 'params': [ - { - 'type': { - 'names': [ - 'module:utils/eventinfo~EventInfo' - ] - }, - 'description': '

An object containing information about the fired event.

', - 'name': 'eventInfo' - } - ] - } ); - } ); - - it( 'should include the Foo#bar function firing two events', () => { - const doclet = apiDocs.find( d => d.longname === 'module:foo~Foo#bar' ); - - expect( doclet ).to.deep.equal( { - 'comment': '/**\n\t * Fires two events.\n\t *\n\t * @fires inside\n\t * @fires outside\n\t */', - 'meta': { - 'filename': 'input.jsdoc' - }, - 'description': 'Fires two events.', - 'fires': [ - 'module:foo~Foo#event:inside', - 'module:foo~Foo#event:outside' - ], - 'name': 'bar', - 'longname': 'module:foo~Foo#bar', - 'kind': 'function', - 'scope': 'instance', - 'memberof': 'module:foo~Foo', - 'params': [] - } ); - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/integration-tests/events-basics/input.jsdoc b/packages/jsdoc-plugins/tests/integration-tests/events-basics/input.jsdoc deleted file mode 100644 index 238947774..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/events-basics/input.jsdoc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @module foo - */ - -/** - * The `Foo` class documentation. - */ -export default class Foo { - /** - * Fires two events. - * - * @fires inside - * @fires outside - */ - bar() { - - } - - /** - * An event documented inside the class. - * - * @event inside - */ -} - -/** - * An event documented outside the class. - * - * @event outside - */ diff --git a/packages/jsdoc-plugins/tests/integration-tests/exported-variables/exported-variables.js b/packages/jsdoc-plugins/tests/integration-tests/exported-variables/exported-variables.js deleted file mode 100644 index 9f1160fd1..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/exported-variables/exported-variables.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const extractApiDocs = require( '../_utils/extract-api-docs' ); -const { cloneDeep } = require( 'lodash' ); -const { expect } = require( 'chai' ); - -describe( 'integration test/exported-variables', () => { - /** @type {Array.} */ - let originalApiDocs; - - /** @type {Array.} */ - let apiDocs; - - before( async () => { - originalApiDocs = await extractApiDocs( __dirname ); - } ); - - beforeEach( () => { - apiDocs = cloneDeep( originalApiDocs ); - } ); - - describe( 'exported constants and variables', () => { - it( 'doclet for MAGIC_CONSTANT should be generated', () => { - const magicConstantDoclet = apiDocs.find( d => d.longname == 'module:engine/magic~MAGIC_CONSTANT' ); - - expect( magicConstantDoclet ).to.deep.equal( { - comment: '/**\n * Magic constant\n */', - description: 'Magic constant', - kind: 'constant', - longname: 'module:engine/magic~MAGIC_CONSTANT', - memberof: 'module:engine/magic', - meta: { - filename: 'input.jsdoc' - }, - name: 'MAGIC_CONSTANT', - scope: 'inner' - } ); - } ); - - it( 'doclet for magicVariable should be generated', () => { - const magicVariableDoclet = apiDocs.find( d => d.longname == 'module:engine/magic.magicVariable' ); - - expect( magicVariableDoclet ).to.deep.equal( { - comment: '/**\n * Magic variable\n */', - description: 'Magic variable', - kind: 'member', - longname: 'module:engine/magic.magicVariable', - memberof: 'module:engine/magic', - meta: { - filename: 'input.jsdoc' - }, - name: 'magicVariable', - scope: 'static' - } ); - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/integration-tests/exported-variables/input.jsdoc b/packages/jsdoc-plugins/tests/integration-tests/exported-variables/input.jsdoc deleted file mode 100644 index 7b0c5de2d..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/exported-variables/input.jsdoc +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/** - * @module engine/magic - */ - -/** - * Magic constant - */ -export const MAGIC_CONSTANT = 7; - -/** - * Magic variable - */ -export let magicVariable = 3; - -magicVariable++; diff --git a/packages/jsdoc-plugins/tests/integration-tests/functions/functions.js b/packages/jsdoc-plugins/tests/integration-tests/functions/functions.js deleted file mode 100644 index a454d7b14..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/functions/functions.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const extractApiDocs = require( '../_utils/extract-api-docs' ); -const { cloneDeep } = require( 'lodash' ); -const { expect } = require( 'chai' ); - -describe( 'integration test/exported-functions', () => { - /** @type {Array.} */ - let originalApiDocs; - - /** @type {Array.} */ - let apiDocs; - - before( async () => { - originalApiDocs = await extractApiDocs( __dirname ); - } ); - - beforeEach( () => { - apiDocs = cloneDeep( originalApiDocs ); - } ); - - describe( 'exported functions', () => { - it( '3 doclets should be generated', () => { - expect( apiDocs.length ).to.equal( 3 ); - } ); - - it( 'doclet for the default export function should be generated', () => { - const fooDoclet = apiDocs.find( d => d.longname === 'module:foo~foo' ); - - expect( fooDoclet ).to.be.an( 'object' ); - - expect( fooDoclet ).to.deep.equal( { - comment: '/**\n * Function `foo`.\n */', - meta: { - filename: 'input.jsdoc' - }, - description: 'Function `foo`.', - name: 'foo', - longname: 'module:foo~foo', - kind: 'function', - memberof: 'module:foo', - scope: 'inner' - } ); - } ); - - it( 'doclet for the exported function should be generated', () => { - const barDoclet = apiDocs.find( d => d.longname === 'module:foo~bar' ); - - expect( barDoclet ).to.be.an( 'object' ); - - expect( barDoclet ).to.deep.equal( { - comment: '/**\n * Helper function `bar`.\n */', - meta: { - filename: 'input.jsdoc' - }, - description: 'Helper function `bar`.', - name: 'bar', - longname: 'module:foo~bar', - kind: 'function', - memberof: 'module:foo', - scope: 'inner' - } ); - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/integration-tests/functions/input.jsdoc b/packages/jsdoc-plugins/tests/integration-tests/functions/input.jsdoc deleted file mode 100644 index c7971139a..000000000 --- a/packages/jsdoc-plugins/tests/integration-tests/functions/input.jsdoc +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @module foo - */ - -/** - * Function `foo`. - */ -export default function foo() { - bar(); -} - -/** - * Helper function `bar`. - */ -export function bar() { - -} diff --git a/packages/jsdoc-plugins/tests/observable-event-provider/addmissingeventdocletsforobservables.js b/packages/jsdoc-plugins/tests/observable-event-provider/addmissingeventdocletsforobservables.js deleted file mode 100644 index 6c2c9a569..000000000 --- a/packages/jsdoc-plugins/tests/observable-event-provider/addmissingeventdocletsforobservables.js +++ /dev/null @@ -1,287 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const { expect } = require( 'chai' ); -const addMissingEventDocletsForObservables = require( '../../lib/observable-event-provider/addmissingeventdocletsforobservables' ); - -describe( 'jsdoc-plugins/observable-event-provider', () => { - describe( 'addMissingEventDocletsForObservables()', () => { - it( 'should provide new event doclet for the observable property', () => { - const inputDoclets = [ { - comment: '...', - meta: { - range: [ - 1861, - 2061 - ], - filename: 'command.js', - lineno: 56, - path: '/workspace/ckeditor5/packages/ckeditor5-core/src', - code: {} - }, - description: '

Flag indicating whether a command is enabled or disabled.

', - observable: true, - readonly: true, - kind: 'member', - name: 'isEnabled', - type: { - names: [ - 'Boolean' - ] - }, - longname: 'module:core/command~Command#isEnabled', - scope: 'instance', - memberof: 'module:core/command~Command', - access: 'protected' - } ]; - - const outputDoclets = addMissingEventDocletsForObservables( inputDoclets ); - - expect( outputDoclets.length ).to.equal( 2 ); - expect( outputDoclets[ 1 ] ).to.deep.equal( { - comment: '', - meta: { - range: [ - 1861, - 2061 - ], - filename: 'command.js', - lineno: 56, - path: '/workspace/ckeditor5/packages/ckeditor5-core/src', - code: {} - }, - description: '

Fired when the isEnabled property changed value.

', - kind: 'event', - name: 'change:isEnabled', - longname: 'module:core/command~Command#event:change:isEnabled', - params: [ { - type: { - names: [ 'module:utils/eventinfo~EventInfo' ] - }, - description: '

An object containing information about the fired event.

', - name: 'eventInfo' - }, - { - type: { - names: [ 'String' ] - }, - description: '

Name of the changed property (isEnabled).

', - name: 'name' - }, - { - type: { - names: [ 'Boolean' ] - }, - description: [ - '

New value of the isEnabled property with given key or null, ', - 'if operation should remove property.

' - ].join( '' ), - name: 'value' - }, - { - type: { - names: [ 'Boolean' ] - }, - description: [ - '

Old value of the isEnabled property with given key or null, ', - 'if property was not set before.

' - ].join( '' ), - name: 'oldValue' - } ], - scope: 'instance', - memberof: 'module:core/command~Command', - access: 'protected' - } ); - } ); - - it( 'should not provide event doclet for an observable property if the doclet already exists', () => { - const inputDoclets = [ { - comment: '...', - meta: { - range: [ - 1861, - 2061 - ], - filename: 'command.js', - lineno: 56, - path: '/workspace/ckeditor5/packages/ckeditor5-core/src', - code: {} - }, - description: '

Flag indicating whether a command is enabled or disabled.

', - observable: true, - readonly: true, - kind: 'member', - name: 'isEnabled', - type: { - names: [ - 'Boolean' - ] - }, - longname: 'module:core/command~Command#isEnabled', - scope: 'instance', - memberof: 'module:core/command~Command' - }, { - kind: 'event', - longname: 'module:core/command~Command#event:change:isEnabled' - } ]; - - const outputDoclets = addMissingEventDocletsForObservables( inputDoclets ); - - expect( outputDoclets.length ).to.equal( 2 ); - } ); - - it( 'should add a proper event name to the fires property', () => { - const inputDoclets = [ { - comment: '/**\n\t\t * Flag indicating whether a command is enabled or disabled.', - meta: { - range: [ - 1861, - 2061 - ], - filename: 'command.js', - lineno: 56, - path: '/workspace/ckeditor5/packages/ckeditor5-core/src', - code: {} - }, - description: '

Flag indicating whether a command is enabled or disabled.

', - observable: true, - readonly: true, - kind: 'member', - name: 'isEnabled', - type: { - names: [ - 'Boolean' - ] - }, - longname: 'module:core/command~Command#isEnabled', - scope: 'instance', - memberof: 'module:core/command~Command', - access: 'public' - } ]; - - const outputDoclets = addMissingEventDocletsForObservables( inputDoclets ); - - expect( outputDoclets[ 0 ].fires ).to.deep.equal( [ - 'module:core/command~Command#event:change:isEnabled' - ] ); - } ); - - it( 'should provide new event doclet with `*` type for the observable property that does not specify type', () => { - const inputDoclets = [ { - comment: '...', - meta: { - range: [ - 1861, - 2061 - ], - filename: 'command.js', - lineno: 56, - path: '/workspace/ckeditor5/packages/ckeditor5-core/src', - code: {} - }, - description: '

Flag indicating whether a command is enabled or disabled.

', - observable: true, - readonly: true, - kind: 'member', - name: 'isEnabled', - longname: 'module:core/command~Command#isEnabled', - scope: 'instance', - memberof: 'module:core/command~Command' - } ]; - - const outputDoclets = addMissingEventDocletsForObservables( inputDoclets ); - - expect( outputDoclets.length ).to.equal( 2 ); - expect( outputDoclets[ 1 ] ).to.deep.equal( { - comment: '', - meta: { - range: [ - 1861, - 2061 - ], - filename: 'command.js', - lineno: 56, - path: '/workspace/ckeditor5/packages/ckeditor5-core/src', - code: {} - }, - description: '

Fired when the isEnabled property changed value.

', - kind: 'event', - name: 'change:isEnabled', - longname: 'module:core/command~Command#event:change:isEnabled', - params: [ { - type: { - names: [ 'module:utils/eventinfo~EventInfo' ] - }, - description: '

An object containing information about the fired event.

', - name: 'eventInfo' - }, - { - type: { - names: [ 'String' ] - }, - description: '

Name of the changed property (isEnabled).

', - name: 'name' - }, - { - type: { - names: [ '*' ] - }, - description: [ - '

New value of the isEnabled property with given key or null, ', - 'if operation should remove property.

' - ].join( '' ), - name: 'value' - }, - { - type: { - names: [ '*' ] - }, - description: [ - '

Old value of the isEnabled property with given key or null, ', - 'if property was not set before.

' - ].join( '' ), - name: 'oldValue' - } ], - scope: 'instance', - memberof: 'module:core/command~Command', - access: 'public' - } ); - } ); - - it( 'should mark new event doclet as inherited if the observable property inherited from other class', () => { - const inputDoclets = [ { - comment: '...', - meta: { - range: [ - 1861, - 2061 - ], - filename: 'command.js', - lineno: 56, - path: '/workspace/ckeditor5/packages/ckeditor5-core/src', - code: {} - }, - description: '

Flag indicating whether a command is enabled or disabled.

', - observable: true, - readonly: true, - kind: 'member', - name: 'isEnabled', - longname: 'module:core/command~Command#isEnabled', - scope: 'instance', - memberof: 'module:core/command~Command', - inherited: true, - mixed: true - } ]; - - const outputDoclets = addMissingEventDocletsForObservables( inputDoclets ); - - expect( outputDoclets.length ).to.equal( 2 ); - expect( outputDoclets[ 1 ].inherited ).to.be.true; - expect( outputDoclets[ 1 ].mixed ).to.be.true; - } ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/relation-fixer/addmissingdoclets.js b/packages/jsdoc-plugins/tests/relation-fixer/addmissingdoclets.js deleted file mode 100644 index 3cddc95a2..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/addmissingdoclets.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const addMissingDoclets = require( '../../lib/relation-fixer/addmissingdoclets' ); -const interfaceTestDoclets = require( './test-data/interface' ); -const inheritanceImplicitTestDoclets = require( './test-data/inheritance-implicit' ); -const inheritanceInheritdocTestDoclets = require( './test-data/inheritance-inheritdoc' ); -const unwantedTestDoclets = require( './test-data/unwanted-doclets' ); -const mixinTestDoclets = require( './test-data/mixins' ); - -describe( 'jsdoc-plugins/relation-fixer/addMissingDoclets()', () => { - it( 'should add missing doclets coming from interfaces', () => { - const expectedDoclet = { - name: 'intAProperty', - longname: 'classB.intAProperty', - kind: 'member', - memberof: 'classB', - description: 'intAProp description', - inherited: true - }; - - const newDoclets = addMissingDoclets( interfaceTestDoclets ); - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); - - it( 'should add missing doclets coming from extended classes implicitly', () => { - const expectedDoclet = { - name: 'classAProp', - longname: 'classB.prop', - kind: 'member', - scope: 'static', - memberof: 'classB', - inherited: true - }; - - const newDoclets = addMissingDoclets( inheritanceImplicitTestDoclets ); - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); - - it( 'should add missing doclets coming from extended classes with use of `inheritdoc`', () => { - const expectedDoclet = { - name: 'classAProp', - longname: 'classB.prop', - kind: 'member', - scope: 'static', - memberof: 'classB', - description: 'Class A prop description', - inherited: true - }; - - const newDoclets = addMissingDoclets( inheritanceInheritdocTestDoclets ); - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); - - it( 'should ignore existing doclets when `inheritdoc` was used', () => { - const newDoclets = addMissingDoclets( inheritanceInheritdocTestDoclets ); - const classBPropDoclets = newDoclets.filter( d => d.longname === 'classB.prop' ); - - expect( classBPropDoclets.length ).to.equal( 1 ); - expect( classBPropDoclets[ 0 ].inheritdoc ).to.equal( undefined ); - } ); - - it( 'should add missing doclets of mixed stuff', () => { - const expectedDoclet = { - name: 'mixedProp', - longname: 'classB.mixedProp', - kind: 'event', - memberof: 'classB', - description: 'mixedProp description', - mixed: true - }; - - const newDoclets = addMissingDoclets( mixinTestDoclets ); - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); - - it( 'should not add doclets of mixed stuff which already have own doclets', () => { - const expectedDoclet = { - name: 'mixedProp', - longname: 'classA.mixedProp', - kind: 'event', - memberof: 'classA', - description: 'mixedProp description', - mixed: true - }; - - const newDoclets = addMissingDoclets( mixinTestDoclets ); - expect( newDoclets ).to.not.deep.include( expectedDoclet ); - } ); - - it( 'should not add doclets which were already inherited (they have inheritdoc property)', () => { - const newDoclets = addMissingDoclets( unwantedTestDoclets ); - const expectedDoclet = newDoclets.find( d => d.longname === 'classB.propA' ); - - expect( expectedDoclet ).to.not.exist; - } ); - - it( 'should not add doclets which have `ignore: true` property', () => { - const newDoclets = addMissingDoclets( unwantedTestDoclets ); - const expectedDoclet = newDoclets.find( d => d.longname === 'classB.propB' ); - - expect( expectedDoclet ).to.not.exist; - } ); - - it( 'should not add doclets which have `undocumented: true` property', () => { - const newDoclets = addMissingDoclets( unwantedTestDoclets ); - const expectedDoclet = newDoclets.find( d => d.longname === 'classB.propC' ); - - expect( expectedDoclet ).to.not.exist; - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/relation-fixer/buildrelations.js b/packages/jsdoc-plugins/tests/relation-fixer/buildrelations.js deleted file mode 100644 index 60a9ea898..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/buildrelations.js +++ /dev/null @@ -1,172 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const buildRelations = require( '../../lib/relation-fixer/buildrelations' ); - -describe( 'JSDoc relation-fixer buildrelations module', () => { - let testDoclets; - let testDoclets2; - - beforeEach( () => { - testDoclets = [ - { - name: 'interfaceA', - longname: 'interfaceA', - kind: 'interface' - }, - { - name: 'interfaceB', - longname: 'interfaceB', - kind: 'interface' - }, - { - name: 'mixinA', - longname: 'mixinA', - kind: 'mixin', - implements: [ - 'interfaceB', - 'interfaceB' - ] - }, - { - name: 'classA', - longname: 'classA', - kind: 'class', - implements: [ - 'interfaceA' - ], - mixes: [ - 'mixinA', - 'mixinA' - ] - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA', - 'classA' - ] - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA', - 'classA' - ] - } - ]; - - testDoclets2 = [ - { - name: 'interfaceA', - longname: 'interfaceA', - kind: 'interface' - }, - { - name: 'interfaceB', - longname: 'interfaceB', - kind: 'interface', - augments: [ 'interfaceA' ] - }, - { - name: 'classA', - longname: 'classA', - kind: 'class', - implements: [ - 'interfaceB' - ] - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ 'classA' ] - } - ]; - } ); - - it( 'should add implementation chain array to doclets', () => { - const newDoclets = buildRelations( testDoclets ); - const testedDoclet = newDoclets.find( d => d.longname === 'classB' ); - const expectedProp = [ - 'interfaceA', - 'interfaceB' - ]; - - expectedProp.forEach( prop => { - expect( testedDoclet ).to.have.property( 'implementsNested' ).and.to.include( prop ); - } ); - } ); - - it( 'should add mixing chain array to doclets', () => { - const newDoclets = buildRelations( testDoclets ); - const testedDoclet = newDoclets.find( d => d.longname === 'classB' ); - const expectedProp = [ - 'mixinA' - ]; - - expectedProp.forEach( prop => { - expect( testedDoclet ).to.have.property( 'mixesNested' ).and.to.include( prop ); - } ); - } ); - - it( 'should add inheritance chain array to doclets', () => { - const newDoclets = buildRelations( testDoclets ); - const testedDoclet = newDoclets.find( d => d.longname === 'classB' ); - const expectedProp = [ - 'classA' - ]; - - expectedProp.forEach( prop => { - expect( testedDoclet ).to.have.property( 'augmentsNested' ).and.to.include( prop ); - } ); - } ); - - it( 'should add descendants chain array to doclets', () => { - const newDoclets = buildRelations( testDoclets ); - const testedDoclet = newDoclets.find( d => d.longname === 'interfaceB' ); - const expectedProp = [ - 'mixinA', - 'classA', - 'classB' - ]; - - expectedProp.forEach( prop => { - expect( testedDoclet ).to.have.property( 'descendants' ).and.to.include( prop ); - } ); - } ); - - it( 'should not allow duplicates in relation arrays', () => { - const newDoclets = buildRelations( testDoclets ); - const testedClass = newDoclets.find( d => d.longname === 'classB' ); - const testedInterface = newDoclets.find( d => d.longname === 'interfaceB' ); - - // Removing duplicates. - const expectedImplementsNested = Array.from( new Set( testedClass.implementsNested ) ); - const expectedMixesNested = Array.from( new Set( testedClass.mixesNested ) ); - const expectedAugmentsNested = Array.from( new Set( testedClass.augmentsNested ) ); - const expectedDescendants = Array.from( new Set( testedInterface.descendants ) ); - - expect( testedClass ).to.have.property( 'implementsNested' ).and.to.deep.equal( expectedImplementsNested ); - expect( testedClass ).to.have.property( 'mixesNested' ).and.to.deep.equal( expectedMixesNested ); - expect( testedClass ).to.have.property( 'augmentsNested' ).and.to.deep.equal( expectedAugmentsNested ); - expect( testedInterface ).to.have.property( 'descendants' ).and.to.deep.equal( expectedDescendants ); - } ); - - it( 'should not put non-classes to inheritance hierarchy', () => { - const newDoclets = buildRelations( testDoclets2 ); - const testedDoclet = newDoclets.find( d => d.longname === 'classB' ); - - expect( testedDoclet ).to.have.property( 'augmentsNested' ).and.to.deep.equal( [ 'classA' ] ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/relation-fixer/relationfixerintegration.js b/packages/jsdoc-plugins/tests/relation-fixer/relationfixerintegration.js deleted file mode 100644 index c2995cd58..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/relationfixerintegration.js +++ /dev/null @@ -1,209 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -const chai = require( 'chai' ); -const expect = chai.expect; -const buildRelations = require( '../../lib/relation-fixer/buildrelations' ); -const addMissingDoclets = require( '../../lib/relation-fixer/addmissingdoclets' ); -const addTypedefProperties = require( '../../lib/relation-fixer/addtypedefproperties' ); - -describe( 'JSDoc relation-fixer plugin', () => { - let testDoclets; - - beforeEach( () => { - testDoclets = [ - { - name: 'interfaceB', - longname: 'interfaceB', - kind: 'interface' - }, - { - name: 'interfaceBProp', - longname: 'interfaceB.prop', - kind: 'member', - scope: 'static', - memberof: 'interfaceB', - description: 'Interface B prop description' - }, - { - name: 'mixinA', - longname: 'mixinA', - kind: 'mixin', - implements: [ - 'interfaceB' - ] - }, - { - name: 'classA', - longname: 'classA', - kind: 'class', - mixes: [ 'mixinA' ] - }, - { - name: 'interfaceBProp', - longname: 'classA.prop', - kind: 'member', - scope: 'static', - memberof: 'classA', - inheritdoc: '' - }, - { - name: 'eventD', - longname: 'classA.eventD', - kind: 'event', - memberof: 'classA' - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA' - ], - augmentsNested: [ - 'classA' - ] - }, - { - name: 'interfaceBProp', - longname: 'classB.prop', - kind: 'member', - scope: 'static', - memberof: 'classB', - inheritdoc: '' - }, - { - name: 'typedefA', - longname: 'typedefA', - kind: 'typedef', - properties: [ - { - type: { - names: [ - 'String' - ] - }, - description: 'Some description', - name: 'modelElement' - }, - { - type: { - names: [ - 'String' - ] - }, - description: 'Another description', - name: 'viewElement' - } - ] - }, - { - name: 'typedefB', - longname: 'typedefB', - kind: 'typedef', - augments: [ - 'typedefA' - ], - properties: [ - { - type: { - names: [ - 'String' - ] - }, - description: 'Some other description', - name: 'modelElement' - } - ] - } - ]; - } ); - - it( 'should add missing doclet through relation chain', () => { - const newDoclets = addMissingDoclets( buildRelations( testDoclets ) ); - const expectedDoclet = { - name: 'interfaceBProp', - longname: 'classB.prop', - kind: 'member', - scope: 'static', - memberof: 'classB', - description: 'Interface B prop description', - mixed: true - }; - - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); - - it( 'should add missing doclet to other links of relation chain', () => { - const newDoclets = addMissingDoclets( buildRelations( testDoclets ) ); - const expectedDoclet = { - name: 'interfaceBProp', - longname: 'classA.prop', - kind: 'member', - scope: 'static', - memberof: 'classA', - description: 'Interface B prop description', - mixed: true - }; - - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); - - it( 'should add missing events', () => { - const newDoclets = addMissingDoclets( buildRelations( testDoclets ) ); - const expectedDoclet = { - name: 'eventD', - longname: 'classB.eventD', - kind: 'event', - memberof: 'classB', - inherited: true - }; - - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); - - it( 'should extend typedefs', () => { - const newDoclets = addMissingDoclets( addTypedefProperties( buildRelations( testDoclets ) ) ); - const expectedDoclet = { - name: 'typedefB', - longname: 'typedefB', - kind: 'typedef', - augments: [ - 'typedefA' - ], - augmentsNested: [ - 'typedefA' - ], - implementsNested: [], - mixesNested: [], - descendants: [], - properties: [ - { - type: { - names: [ - 'String' - ] - }, - description: 'Some other description', - name: 'modelElement' - }, - { - type: { - names: [ - 'String' - ] - }, - description: 'Another description', - name: 'viewElement', - inherited: true - } - ] - }; - - expect( newDoclets ).to.deep.include( expectedDoclet ); - } ); -} ); diff --git a/packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-implicit.js b/packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-implicit.js deleted file mode 100644 index c6a0651b1..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-implicit.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** @type {Array.} */ -module.exports = [ - { - name: 'classA', - longname: 'classA', - kind: 'class' - }, - { - name: 'classAProp', - longname: 'classA.prop', - kind: 'member', - scope: 'static', - memberof: 'classA' - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA' - ], - augmentsNested: [ - 'classA' - ] - } -]; diff --git a/packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-inheritdoc.js b/packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-inheritdoc.js deleted file mode 100644 index 88b8bdd43..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/test-data/inheritance-inheritdoc.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** @type {Array.} */ -module.exports = [ - { - name: 'classA', - longname: 'classA', - kind: 'class' - }, - { - name: 'classAProp', - longname: 'classA.prop', - kind: 'member', - scope: 'static', - memberof: 'classA', - description: 'Class A prop description' - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA' - ], - augmentsNested: [ - 'classA' - ] - }, - { - name: 'classAProp', - longname: 'classB.prop', - kind: 'member', - scope: 'static', - memberof: 'classB', - inheritdoc: '' - } -]; diff --git a/packages/jsdoc-plugins/tests/relation-fixer/test-data/interface.js b/packages/jsdoc-plugins/tests/relation-fixer/test-data/interface.js deleted file mode 100644 index 885b724fe..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/test-data/interface.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** @type {Array.} */ -module.exports = [ - { - name: 'interfaceA', - longname: 'interfaceA', - kind: 'interface', - descendants: [ - 'classA', - 'classB' - ] - }, - { - name: 'intAProperty', - longname: 'interfaceA.intAProperty', - kind: 'member', - memberof: 'interfaceA', - description: 'intAProp description' - }, - { - name: 'classA', - longname: 'classA', - kind: 'class', - implements: [ - 'interfaceA' - ], - implementsNested: [ 'interfaceA' ], - descendants: [ 'classB' ] - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA' - ], - implementsNested: [ 'interfaceA' ], - augmentsNested: [ 'classA' ] - }, - { - name: 'intAProperty', - longname: 'classB.intAProperty', - kind: 'member', - memberof: 'classB', - inheritdoc: '' - } -]; diff --git a/packages/jsdoc-plugins/tests/relation-fixer/test-data/mixins.js b/packages/jsdoc-plugins/tests/relation-fixer/test-data/mixins.js deleted file mode 100644 index 6f43ce018..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/test-data/mixins.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** @type {Array.} */ -module.exports = [ - { - name: 'mixinA', - longname: 'mixinA', - kind: 'mixin' - }, - { - name: 'mixedProp', - longname: 'mixinA.mixedProp', - kind: 'event', - memberof: 'mixinA', - description: 'mixedProp description' - }, - { - name: 'classA', - longname: 'classA', - kind: 'class', - mixesNested: [ 'mixinA' ] - }, - { - name: 'classAProp', - longname: 'classA.prop', - kind: 'member', - scope: 'static', - memberof: 'classA' - }, - { - name: 'mixedProp', - longname: 'classA.mixedProp', - kind: 'event', - memberof: 'classA', - inheritdoc: '' - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA' - ], - augmentsNested: [ - 'classA' - ], - mixesNested: [ 'mixinA' ] - } -]; diff --git a/packages/jsdoc-plugins/tests/relation-fixer/test-data/unwanted-doclets.js b/packages/jsdoc-plugins/tests/relation-fixer/test-data/unwanted-doclets.js deleted file mode 100644 index 7e603eb34..000000000 --- a/packages/jsdoc-plugins/tests/relation-fixer/test-data/unwanted-doclets.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. - * For licensing, see LICENSE.md. - */ - -'use strict'; - -/** @type {Array.} */ -module.exports = [ - { - name: 'classA', - longname: 'classA', - kind: 'class' - }, - { - name: 'classAPropA', - longname: 'classA.propA', - kind: 'member', - scope: 'static', - memberof: 'classA', - inheritdoc: '' - }, - { - name: 'classAPropB', - longname: 'classA.propB', - kind: 'member', - scope: 'static', - memberof: 'classA', - ignore: true - }, - { - name: 'classAPropC', - longname: 'classA.propC', - kind: 'member', - scope: 'static', - memberof: 'classA', - undocumented: true - }, - { - name: 'classB', - longname: 'classB', - kind: 'class', - augments: [ - 'classA' - ], - augmentsNested: [ - 'classA' - ] - } -]; diff --git a/scripts/changelog.js b/scripts/changelog.js index af403d932..b3a175c10 100755 --- a/scripts/changelog.js +++ b/scripts/changelog.js @@ -17,10 +17,6 @@ const changelogOptions = { packages: 'packages', releaseBranch: cliArguments.branch, transformScope: name => { - if ( name === 'jsdoc-plugins' ) { - return 'https://www.npmjs.com/package/@ckeditor/jsdoc-plugins'; - } - if ( name === 'typedoc-plugins' ) { return 'https://www.npmjs.com/package/@ckeditor/typedoc-plugins'; } From 508743a7aea413f94add8558a210573a21aab364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Wed, 18 Sep 2024 11:25:44 +0200 Subject: [PATCH 104/172] Aligned tests. Removed unneeded `type` property from config. --- packages/ckeditor5-dev-docs/lib/build.js | 2 -- .../tests/validators/fires-validator/index.js | 3 +-- .../tests/validators/link-validator/index.js | 4 +--- .../tests/validators/module-validator/index.js | 3 +-- .../tests/validators/overloads-validator/index.js | 3 +-- .../tests/validators/see-validator/index.js | 3 +-- 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index 82a2afbd9..b920fe6e1 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -93,8 +93,6 @@ export default async function build( config ) { /** * @typedef {Object} TypedocConfig * - * @property {'typedoc'} type - * * @property {Object} config * * @property {String} cwd diff --git a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js index ef8614dcf..c80cfd7cc 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/fires-validator/index.js @@ -7,7 +7,7 @@ import { describe, it, expect, vi } from 'vitest'; import { fileURLToPath } from 'url'; import path from 'path'; import testUtils from '../../_utils.js'; -import build from '../../../lib/buildtypedoc.js'; +import build from '../../../lib/build.js'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); @@ -39,7 +39,6 @@ describe( 'dev-docs/validators/fires-validator', function() { it( 'should warn if fired event does not exist', async () => { await build( { - type: 'typedoc', cwd: FIXTURES_PATH, tsconfig: TSCONFIG_PATH, sourceFiles: [ SOURCE_FILES ], diff --git a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js index 1620653e8..3ad28d4cf 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/link-validator/index.js @@ -7,7 +7,7 @@ import { describe, it, expect, vi } from 'vitest'; import { fileURLToPath } from 'url'; import path from 'path'; import testUtils from '../../_utils.js'; -import build from '../../../lib/buildtypedoc.js'; +import build from '../../../lib/build.js'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); @@ -40,7 +40,6 @@ describe( 'dev-docs/validators/link-validator', function() { it( 'should warn if link is not valid', async () => { await build( { - type: 'typedoc', cwd: FIXTURES_PATH, tsconfig: TSCONFIG_PATH, sourceFiles: [ SOURCE_FILES ], @@ -133,7 +132,6 @@ describe( 'dev-docs/validators/link-validator', function() { it( 'should not call error callback for derived class when there are errors in inherited class', async () => { await build( { - type: 'typedoc', cwd: FIXTURES_PATH, tsconfig: TSCONFIG_PATH, sourceFiles: [ DERIVED_FILE ], diff --git a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js index 1d17bfd81..b194c2eb8 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/module-validator/index.js @@ -7,7 +7,7 @@ import { describe, it, expect, vi } from 'vitest'; import { fileURLToPath } from 'url'; import path from 'path'; import testUtils from '../../_utils.js'; -import build from '../../../lib/buildtypedoc.js'; +import build from '../../../lib/build.js'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); @@ -39,7 +39,6 @@ describe( 'dev-docs/validators/module-validator', function() { it( 'should warn if module name is not valid', async () => { await build( { - type: 'typedoc', cwd: FIXTURES_PATH, tsconfig: TSCONFIG_PATH, sourceFiles: [ SOURCE_FILES ], diff --git a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js index 52b846e40..0f11bfc67 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/overloads-validator/index.js @@ -7,7 +7,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { fileURLToPath } from 'url'; import path from 'path'; import testUtils from '../../_utils.js'; -import build from '../../../lib/buildtypedoc.js'; +import build from '../../../lib/build.js'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); @@ -39,7 +39,6 @@ describe( 'dev-docs/validators/overloads-validator', function() { beforeEach( async () => { await build( { - type: 'typedoc', cwd: FIXTURES_PATH, tsconfig: TSCONFIG_PATH, sourceFiles: [ SOURCE_FILES ], diff --git a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js index 4787c959f..78ab261ad 100644 --- a/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/tests/validators/see-validator/index.js @@ -7,7 +7,7 @@ import { describe, it, expect, vi } from 'vitest'; import { fileURLToPath } from 'url'; import path from 'path'; import testUtils from '../../_utils.js'; -import build from '../../../lib/buildtypedoc.js'; +import build from '../../../lib/build.js'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); @@ -39,7 +39,6 @@ describe( 'dev-docs/validators/see-validator', function() { it( 'should warn if link is not valid', async () => { await build( { - type: 'typedoc', cwd: FIXTURES_PATH, tsconfig: TSCONFIG_PATH, sourceFiles: [ SOURCE_FILES ], From 7ee4e4676a179927c10ce835a0976a3f9e2c5f3b Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 18 Sep 2024 13:24:33 +0200 Subject: [PATCH 105/172] Even more tests. --- .../utils/manual-tests/compilehtmlfiles.js | 2 +- .../treatwarningsaserrorswebpackplugin.js | 2 +- .../utils/manual-tests/compilehtmlfiles.js | 463 +++++++++--------- .../utils/manual-tests/compilescripts.js | 330 ++++--------- .../tests/utils/manual-tests/createserver.js | 72 ++- .../utils/manual-tests/getwebpackconfig.js | 155 +++--- .../tests/utils/manual-tests/removedir.js | 96 ++-- 7 files changed, 465 insertions(+), 655 deletions(-) diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js index a8284dc81..ee1eb102b 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js @@ -45,7 +45,7 @@ export default function compileHtmlFiles( options ) { const staticFiles = sourceDirs .flatMap( sourceDir => { - const globPattern = path.join( sourceDir, '**', '*.!(js|html|md)' ).split( path.sep ).join( '/' ); + const globPattern = path.join( sourceDir, '**', '*.!(js|html|md)' ).split( /[\\/]/ ).join( '/' ); return globSync( globPattern ); } ) diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js index 76b876755..9b323263c 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/treatwarningsaserrorswebpackplugin.js @@ -12,7 +12,7 @@ import TreatWarningsAsErrorsWebpackPlugin from '../../../lib/utils/automated-tes const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); -describe( 'TreatWarningsAsErrorsWebpackPlugin()', () => { +describe( 'TreatWarningsAsErrorsWebpackPlugin', () => { it( 'should reassign warnings to errors and not emit the code when errors are present', () => { return new Promise( ( resolve, reject ) => { runCompiler( diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js index 7753418d2..cc2931fb0 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilehtmlfiles.js @@ -3,122 +3,109 @@ * For licensing, see LICENSE.md. */ +import path from 'path'; +import fs from 'fs-extra'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { createRequire } from 'module'; - -const require = createRequire( import.meta.url ); - -const fakeDirname = path.dirname( require.resolve( '../../../lib/utils/manual-tests/compilehtmlfiles' ) ); - -describe( 'compileHtmlFiles', () => { - let sandbox, stubs, files, compileHtmlFiles; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import { globSync } from 'glob'; +import chokidar from 'chokidar'; +import chalk from 'chalk'; +import domCombiner from 'dom-combiner'; +import compileHtmlFiles from '../../../lib/utils/manual-tests/compilehtmlfiles.js'; +import getRelativeFilePath from '../../../lib/utils/getrelativefilepath.js'; + +const stubs = vi.hoisted( () => ( { + commonmark: { + parser: { + parse: vi.fn() + }, + htmlRenderer: { + render: vi.fn() + } + }, + log: { + info: vi.fn() + } +} ) ); + +vi.mock( 'path' ); +vi.mock( 'commonmark', () => ( { + Parser: class Parser { + parse( ...args ) { + return stubs.commonmark.parser.parse( ...args ); + } + }, + + HtmlRenderer: class HtmlRenderer { + render( ...args ) { + return stubs.commonmark.htmlRenderer.render( ...args ); + } + } +} ) ); +vi.mock( 'path' ); +vi.mock( 'glob' ); +vi.mock( 'fs-extra' ); +vi.mock( 'chokidar' ); +vi.mock( 'chalk', () => ( { + default: { + cyan: vi.fn() + } +} ) ); +vi.mock( 'dom-combiner' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( '../../../lib/utils/getrelativefilepath.js' ); + +describe( 'compileHtmlFiles()', () => { + let files; let patternFiles = {}; let separator = '/'; beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - sandbox = sinon.createSandbox(); - - stubs = { - fs: { - readFileSync: sandbox.spy( pathToFile => files[ pathToFile ] ), - ensureDirSync: sandbox.stub(), - outputFileSync: sandbox.stub(), - copySync: sandbox.stub() - }, - - path: { - join: sandbox.stub().callsFake( ( ...chunks ) => chunks.join( separator ) ), - parse: sandbox.stub().callsFake( pathToParse => { - const chunks = pathToParse.split( separator ); - const fileName = chunks.pop(); - - return { - dir: chunks.join( separator ), - name: fileName.split( '.' ).slice( 0, -1 ).join( '.' ) - }; - } ), - dirname: sandbox.stub().callsFake( pathToParse => { - return pathToParse.split( separator ).slice( 0, -1 ).join( separator ); - } ), - sep: separator - }, - - logger: { - info: sandbox.stub(), - warning: sandbox.stub(), - error: sandbox.stub() - }, - - commonmark: { - parse: sandbox.spy(), - render: sandbox.spy( () => '

Markdown header

' ) - }, - - chalk: { - cyan: sandbox.spy( text => text ) - }, - - chokidar: { - watch: sandbox.stub( chokidar, 'watch' ).callsFake( () => ( { - on: () => { - } - } ) ) - }, - - getRelativeFilePath: sandbox.spy( pathToFile => pathToFile ), - glob: { - globSync: sandbox.spy( pattern => patternFiles[ pattern ] ) - }, - domCombiner: sandbox.spy( ( ...args ) => args.join( '\n' ) ) - }; - - mockery.registerMock( 'path', stubs.path ); - mockery.registerMock( 'commonmark', { - Parser: class Parser { - parse( ...args ) { - return stubs.commonmark.parse( ...args ); - } - }, - - HtmlRenderer: class HtmlRenderer { - render( ...args ) { - return stubs.commonmark.render( ...args ); - } - } + stubs.commonmark.htmlRenderer.render.mockReturnValue( '

Markdown header

' ); + vi.mocked( logger ).mockReturnValue( stubs.log ); + vi.mocked( chalk ).cyan.mockImplementation( input => input ); + vi.mocked( fs ).readFileSync.mockImplementation( pathToFile => files[ pathToFile ] ); + vi.mocked( path ).join.mockImplementation( ( ...chunks ) => chunks.join( separator ) ); + vi.mocked( path ).parse.mockImplementation( pathToParse => { + const chunks = pathToParse.split( separator ); + const fileName = chunks.pop(); + + return { + dir: chunks.join( separator ), + name: fileName.split( '.' ).slice( 0, -1 ).join( '.' ) + }; } ); - mockery.registerMock( 'glob', stubs.glob ); - mockery.registerMock( 'fs-extra', stubs.fs ); - mockery.registerMock( 'chokidar', stubs.chokidar ); - mockery.registerMock( 'dom-combiner', stubs.domCombiner ); - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - logger() { - return stubs.logger; - } + vi.mocked( path ).dirname.mockImplementation( pathToParse => { + return pathToParse.split( separator ).slice( 0, -1 ).join( separator ); } ); - mockery.registerMock( '../getrelativefilepath', stubs.getRelativeFilePath ); - } ); - - afterEach( () => { - sandbox.restore(); - mockery.deregisterAll(); - mockery.disable(); + vi.mocked( chokidar ).watch.mockImplementation( () => ( { + on: vi.fn() + } ) ); + vi.mocked( getRelativeFilePath ).mockImplementation( pathToFile => pathToFile ); + vi.mocked( globSync ).mockImplementation( pattern => patternFiles[ pattern ] ); + vi.mocked( domCombiner ).mockImplementation( ( ...args ) => args.join( '\n' ) ); } ); describe( 'Unix environment', () => { beforeEach( () => { separator = '/'; - compileHtmlFiles = require( '../../../lib/utils/manual-tests/compilehtmlfiles' ); + } ); + + it( 'creates a build directory where compiled files are saved', () => { + files = {}; + + compileHtmlFiles( { + buildDir: 'buildDir', + language: 'en', + sourceFiles: [] + } ); + + expect( vi.mocked( fs ).ensureDirSync ).toHaveBeenCalledExactlyOnceWith( 'buildDir' ); } ); it( 'should compile md and html files to the output html file', () => { files = { - [ `${ fakeDirname }/template.html` ]: '
template html content
', + '/template.html': '
template html content
', 'path/to/manual/file.md': '## Markdown header', 'path/to/manual/file.html': '
html file content
' }; @@ -133,50 +120,93 @@ describe( 'compileHtmlFiles', () => { sourceFiles: [ 'path/to/manual/file.js' ] } ); - expect( stubs.commonmark.parse ).to.be.calledWithExactly( '## Markdown header' ); - expect( stubs.fs.ensureDirSync ).to.be.calledWithExactly( 'buildDir' ); + expect( stubs.commonmark.parser.parse ).toHaveBeenCalledExactlyOnceWith( '## Markdown header' ); - /* eslint-disable max-len */ - expect( stubs.fs.outputFileSync ).to.be.calledWithExactly( + expect( vi.mocked( fs ).outputFileSync ).toHaveBeenCalledExactlyOnceWith( 'buildDir/path/to/manual/file.html', [ '
template html content
', '

Markdown header

', '', '' + - '' + + '' + '', '
html file content
', '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + '' ].join( '\n' ) ); - /* eslint-enable max-len */ - expect( stubs.chokidar.watch ).to.be.calledWithExactly( + expect( stubs.log.info ).toHaveBeenCalledTimes( 2 ); + expect( stubs.log.info ).toHaveBeenCalledWith( expect.stringMatching( /^Processing/ ) ); + expect( stubs.log.info ).toHaveBeenCalledWith( expect.stringMatching( /^Finished writing/ ) ); + } ); + + it( 'should listen to changes in source files', () => { + files = { + '/template.html': '
template html content
', + 'path/to/manual/file.md': '## Markdown header', + 'path/to/manual/file.html': '
html file content
' + }; + + patternFiles = { + 'path/to/manual/**/*.!(js|html|md)': [ 'static-file.png' ] + }; + + compileHtmlFiles( { + buildDir: 'buildDir', + language: 'en', + sourceFiles: [ 'path/to/manual/file.js' ] + } ); + + expect( vi.mocked( chokidar ).watch ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( chokidar ).watch ).toHaveBeenCalledWith( 'path/to/manual/file.md', { ignoreInitial: true } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( + expect( vi.mocked( chokidar ).watch ).toHaveBeenCalledWith( 'path/to/manual/file.html', { ignoreInitial: true } ); - expect( stubs.fs.copySync ).to.be.calledWithExactly( - 'static-file.png', 'buildDir/static-file.png' - ); + } ); + + it( 'should copy static resources', () => { + files = { + '/template.html': '
template html content
', + 'path/to/manual/file.md': '## Markdown header', + 'path/to/manual/file.html': '
html file content
' + }; + + patternFiles = { + 'path/to/manual/**/*.!(js|html|md)': [ 'static-file.png' ] + }; + + compileHtmlFiles( { + buildDir: 'buildDir', + language: 'en', + sourceFiles: [ 'path/to/manual/file.js' ] + } ); - expect( stubs.logger.info.callCount ).to.equal( 2 ); - expect( stubs.logger.info.firstCall.args[ 0 ] ).to.match( /^Processing/ ); - expect( stubs.logger.info.secondCall.args[ 0 ] ).to.match( /^Finished writing/ ); + expect( vi.mocked( fs ).copySync ).toHaveBeenCalledExactlyOnceWith( 'static-file.png', 'buildDir/static-file.png' ); } ); it( 'should compile files with options#language specified', () => { + files = { + '/template.html': '
template html content
', + 'path/to/manual/file.md': '## Markdown header', + 'path/to/manual/file.html': '
html file content
' + }; + + patternFiles = { + 'path/to/manual/**/*.!(js|html|md)': [] + }; + compileHtmlFiles( { buildDir: 'buildDir', language: 'en', @@ -184,38 +214,36 @@ describe( 'compileHtmlFiles', () => { sourceFiles: [ 'path/to/manual/file.js' ] } ); - /* eslint-disable max-len */ - expect( stubs.fs.outputFileSync ).to.be.calledWithExactly( + expect( vi.mocked( fs ).outputFileSync ).toHaveBeenCalledExactlyOnceWith( 'buildDir/path/to/manual/file.html', [ '
template html content
', '

Markdown header

', '', '
' + - '' + + '' + '', '
html file content
', '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + '' ].join( '\n' ) ); - /* eslint-enable max-len */ } ); it( 'should work with files containing dots in their names', () => { files = { - [ `${ fakeDirname }/template.html` ]: '
template html content
', + '/template.html': '
template html content
', 'path/to/manual/file.abc.md': '## Markdown header', 'path/to/manual/file.abc.html': '
html file content
' }; @@ -229,35 +257,15 @@ describe( 'compileHtmlFiles', () => { sourceFiles: [ 'path/to/manual/file.abc.js' ] } ); - /* eslint-disable max-len */ - expect( stubs.fs.outputFileSync ).to.be.calledWith( - 'buildDir/path/to/manual/file.abc.html', [ - '
template html content
', - '

Markdown header

', - '', - '
' + - '' + - '', - '
html file content
', - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' - ].join( '\n' ) + expect( vi.mocked( fs ).outputFileSync ).toHaveBeenCalledExactlyOnceWith( + 'buildDir/path/to/manual/file.abc.html', + expect.stringContaining( '' ) ); - /* eslint-enable max-len */ } ); it( 'should work with a few entry points patterns', () => { files = { - [ `${ fakeDirname }/template.html` ]: '
template html content
', + '/template.html': '
template html content
', 'path/to/manual/file.md': '## Markdown header', 'path/to/manual/file.html': '
html file content
', 'path/to/another/manual/file.md': '## Markdown header', @@ -277,15 +285,22 @@ describe( 'compileHtmlFiles', () => { ] } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( 'path/to/manual/file.md', { ignoreInitial: true } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( 'path/to/manual/file.html', { ignoreInitial: true } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( 'path/to/another/manual/file.html', { ignoreInitial: true } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( 'path/to/another/manual/file.html', { ignoreInitial: true } ); + expect( vi.mocked( fs ).outputFileSync ).toHaveBeenCalledTimes( 2 ); + + expect( vi.mocked( fs ).outputFileSync ).toHaveBeenCalledWith( + 'buildDir/path/to/manual/file.html', + expect.stringContaining( '' ) + ); + + expect( vi.mocked( fs ).outputFileSync ).toHaveBeenCalledWith( + 'buildDir/path/to/another/manual/file.html', + expect.stringContaining( '' ) + ); } ); it( 'should not copy md files containing dots in their file names', () => { files = { - [ `${ fakeDirname }/template.html` ]: '
template html content
', + '/template.html': '
template html content
', 'path/to/manual/file.md': '## Markdown header', 'path/to/manual/file.html': '
html file content
' }; @@ -300,42 +315,12 @@ describe( 'compileHtmlFiles', () => { sourceFiles: [ 'path/to/manual/file.js' ] } ); - expect( stubs.commonmark.parse ).to.be.calledWithExactly( '## Markdown header' ); - expect( stubs.fs.ensureDirSync ).to.be.calledWithExactly( 'buildDir' ); - - /* eslint-disable max-len */ - expect( stubs.fs.outputFileSync ).to.be.calledWithExactly( - 'buildDir/path/to/manual/file.html', [ - '
template html content
', - '

Markdown header

', - '', - '
' + - '' + - '', - '
html file content
', - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' - ].join( '\n' ) - ); - /* eslint-enable max-len */ - - expect( stubs.chokidar.watch ).to.be.calledWithExactly( 'path/to/manual/file.md', { ignoreInitial: true } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( 'path/to/manual/file.html', { ignoreInitial: true } ); - expect( stubs.fs.copySync ).not.to.be.calledWith( 'some.file.md', 'buildDir/some.file.md' ); + expect( vi.mocked( fs ).copySync ).not.toHaveBeenCalled(); } ); it( 'should compile the manual test and do not inform about the processed file', () => { files = { - [ `${ fakeDirname }/template.html` ]: '
template html content
', + '/template.html': '
template html content
', 'path/to/manual/file.md': '## Markdown header', 'path/to/manual/file.html': '
html file content
' }; @@ -351,27 +336,22 @@ describe( 'compileHtmlFiles', () => { silent: true } ); - expect( stubs.commonmark.parse ).to.be.calledWithExactly( '## Markdown header' ); - expect( stubs.fs.ensureDirSync ).to.be.calledWithExactly( 'buildDir' ); - - expect( stubs.logger.info.callCount ).to.equal( 0 ); + expect( stubs.log.info ).not.toHaveBeenCalled(); } ); } ); describe( 'Windows environment', () => { beforeEach( () => { separator = '\\'; - compileHtmlFiles = require( '../../../lib/utils/manual-tests/compilehtmlfiles' ); } ); it( 'should work on Windows environments', () => { - // Our wrapper on Glob returns proper paths for Unix and Windows. patternFiles = { - 'path\\to\\manual\\**\\*.!(js|html|md)': [ 'static-file.png' ] + 'path/to/manual/**/*.!(js|html|md)': [ 'static-file.png' ] }; files = { - [ fakeDirname + '\\template.html' ]: '
template html content
', + '\\template.html': '
template html content
', 'path\\to\\manual\\file.md': '## Markdown header', 'path\\to\\manual\\file.html': '
html file content
' }; @@ -381,43 +361,72 @@ describe( 'compileHtmlFiles', () => { sourceFiles: [ 'path\\to\\manual\\file.js' ] } ); - expect( stubs.commonmark.parse ).to.be.calledWithExactly( '## Markdown header' ); - expect( stubs.fs.ensureDirSync ).to.be.calledWithExactly( 'buildDir' ); - - /* eslint-disable max-len */ - expect( stubs.fs.outputFileSync ).to.be.calledWithExactly( + expect( vi.mocked( fs ).outputFileSync ).toHaveBeenCalledExactlyOnceWith( 'buildDir\\path\\to\\manual\\file.html', [ '
template html content
', '

Markdown header

', '', '
' + - '' + + '' + '', '
html file content
', '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + '' ].join( '\n' ) ); - /* eslint-enable max-len */ + } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( + it( 'should listen to changes in source files', () => { + patternFiles = { + 'path/to/manual/**/*.!(js|html|md)': [ 'static-file.png' ] + }; + + files = { + '\\template.html': '
template html content
', + 'path\\to\\manual\\file.md': '## Markdown header', + 'path\\to\\manual\\file.html': '
html file content
' + }; + + compileHtmlFiles( { + buildDir: 'buildDir', + sourceFiles: [ 'path\\to\\manual\\file.js' ] + } ); + + expect( vi.mocked( chokidar ).watch ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( chokidar ).watch ).toHaveBeenCalledWith( 'path\\to\\manual\\file.md', { ignoreInitial: true } ); - expect( stubs.chokidar.watch ).to.be.calledWithExactly( + expect( vi.mocked( chokidar ).watch ).toHaveBeenCalledWith( 'path\\to\\manual\\file.html', { ignoreInitial: true } ); - expect( stubs.fs.copySync ).to.be.calledWithExactly( - 'static-file.png', 'buildDir\\static-file.png' - ); + } ); + + it( 'should copy static resources', () => { + patternFiles = { + 'path/to/manual/**/*.!(js|html|md)': [ 'static-file.png' ] + }; + + files = { + '\\template.html': '
template html content
', + 'path\\to\\manual\\file.md': '## Markdown header', + 'path\\to\\manual\\file.html': '
html file content
' + }; + + compileHtmlFiles( { + buildDir: 'buildDir', + sourceFiles: [ 'path\\to\\manual\\file.js' ] + } ); + + expect( vi.mocked( fs ).copySync ).toHaveBeenCalledExactlyOnceWith( 'static-file.png', 'buildDir\\static-file.png' ); } ); } ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilescripts.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilescripts.js index f3323a1cd..5659c7417 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilescripts.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/compilescripts.js @@ -3,51 +3,40 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import webpack from 'webpack'; +import getRelativeFilePath from '../../../lib/utils/getrelativefilepath.js'; +import requireDll from '../../../lib/utils/requiredll.js'; +import getWebpackConfigForManualTests from '../../../lib/utils/manual-tests/getwebpackconfig.js'; +import compileManualTestScripts from '../../../lib/utils/manual-tests/compilescripts.js'; -const mockery = require( 'mockery' ); -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); +vi.mock( 'webpack' ); +vi.mock( '../../../lib/utils/getrelativefilepath.js' ); +vi.mock( '../../../lib/utils/requiredll.js' ); +vi.mock( '../../../lib/utils/manual-tests/getwebpackconfig.js' ); -describe( 'compileManualTestScripts', () => { - let sandbox, stubs, webpackError, compileManualTestScripts; +describe( 'compileManualTestScripts()', () => { + let webpackError; beforeEach( () => { webpackError = null; - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false + vi.mocked( webpack ).mockImplementation( ( config, callback ) => { + callback( webpackError ); } ); - sandbox = sinon.createSandbox(); - - stubs = { - webpack: sandbox.spy( ( config, callback ) => { - callback( webpackError ); - } ), - getWebpackConfig: sandbox.spy( ( { entries, buildDir } ) => ( { - entries, - buildDir - } ) ), - getRelativeFilePath: sandbox.spy( x => x ), - onTestCompilationStatus: sinon.stub() - }; - - mockery.registerMock( './getwebpackconfig', stubs.getWebpackConfig ); - mockery.registerMock( '../getrelativefilepath', stubs.getRelativeFilePath ); - mockery.registerMock( 'webpack', stubs.webpack ); - - compileManualTestScripts = require( '../../../lib/utils/manual-tests/compilescripts' ); - } ); + vi.mocked( getWebpackConfigForManualTests ).mockImplementation( ( { entries, buildDir } ) => ( { + entries, + buildDir + } ) ); - afterEach( () => { - sandbox.restore(); - mockery.disable(); + vi.mocked( getRelativeFilePath ).mockImplementation( input => input ); } ); it( 'should compile manual test scripts (DLL only)', async () => { + vi.mocked( requireDll ).mockReturnValue( true ); + const onTestCompilationStatus = vi.fn(); + await compileManualTestScripts( { cwd: 'workspace', buildDir: 'buildDir', @@ -57,43 +46,44 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus, additionalLanguages: [ 'pl', 'ar' ], debug: [ 'CK_DEBUG' ], disableWatch: false } ); - expect( stubs.getWebpackConfig.calledOnce ).to.equal( true ); - - sinon.assert.calledWith( stubs.getWebpackConfig.firstCall, { + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { cwd: 'workspace', requireDll: true, buildDir: 'buildDir', themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus, additionalLanguages: [ 'pl', 'ar' ], entries: { 'ckeditor5-foo/manual/file1-dll': 'ckeditor5-foo/manual/file1-dll.js', 'ckeditor5-foo/manual/file2-dll': 'ckeditor5-foo/manual/file2-dll.js' }, debug: [ 'CK_DEBUG' ], - disableWatch: false, - identityFile: undefined, - tsconfig: undefined - } ); - - expect( stubs.webpack.calledOnce ).to.equal( true ); - expect( stubs.webpack.firstCall.args[ 0 ] ).to.deep.equal( { - buildDir: 'buildDir', - entries: { - 'ckeditor5-foo/manual/file1-dll': 'ckeditor5-foo/manual/file1-dll.js', - 'ckeditor5-foo/manual/file2-dll': 'ckeditor5-foo/manual/file2-dll.js' - } - } ); + disableWatch: false + } ) ); + + expect( vi.mocked( webpack ) ).toHaveBeenCalledExactlyOnceWith( + { + buildDir: 'buildDir', + entries: { + 'ckeditor5-foo/manual/file1-dll': 'ckeditor5-foo/manual/file1-dll.js', + 'ckeditor5-foo/manual/file2-dll': 'ckeditor5-foo/manual/file2-dll.js' + } + }, + expect.any( Function ) + ); } ); it( 'should compile manual test scripts (non-DLL only)', async () => { + vi.mocked( requireDll ).mockReturnValue( false ); + const onTestCompilationStatus = vi.fn(); + await compileManualTestScripts( { cwd: 'workspace', buildDir: 'buildDir', @@ -103,43 +93,43 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus, additionalLanguages: [ 'pl', 'ar' ], debug: [ 'CK_DEBUG' ], disableWatch: false } ); - expect( stubs.getWebpackConfig.calledOnce ).to.equal( true ); - - sinon.assert.calledWith( stubs.getWebpackConfig.firstCall, { + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { cwd: 'workspace', requireDll: false, buildDir: 'buildDir', themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus, additionalLanguages: [ 'pl', 'ar' ], entries: { 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js', 'ckeditor5-foo/manual/file2': 'ckeditor5-foo/manual/file2.js' }, debug: [ 'CK_DEBUG' ], - disableWatch: false, - identityFile: undefined, - tsconfig: undefined - } ); - - expect( stubs.webpack.calledOnce ).to.equal( true ); - expect( stubs.webpack.firstCall.args[ 0 ] ).to.deep.equal( { - buildDir: 'buildDir', - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js', - 'ckeditor5-foo/manual/file2': 'ckeditor5-foo/manual/file2.js' - } - } ); + disableWatch: false + } ) ); + + expect( vi.mocked( webpack ) ).toHaveBeenCalledExactlyOnceWith( + { + buildDir: 'buildDir', + entries: { + 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js', + 'ckeditor5-foo/manual/file2': 'ckeditor5-foo/manual/file2.js' + } + }, + expect.any( Function ) + ); } ); it( 'should compile manual test scripts (DLL and non-DLL)', async () => { + vi.mocked( requireDll ).mockImplementation( input => input.includes( 'dll' ) ); + await compileManualTestScripts( { cwd: 'workspace', buildDir: 'buildDir', @@ -149,66 +139,19 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus: vi.fn(), additionalLanguages: [ 'pl', 'ar' ], debug: [ 'CK_DEBUG' ], disableWatch: false } ); - expect( stubs.getWebpackConfig.calledTwice ).to.equal( true ); - - sinon.assert.calledWith( stubs.getWebpackConfig.firstCall, { - cwd: 'workspace', - requireDll: true, - buildDir: 'buildDir', - themePath: 'path/to/theme', - language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, - additionalLanguages: [ 'pl', 'ar' ], - entries: { - 'ckeditor5-foo/manual/file2-dll': 'ckeditor5-foo/manual/file2-dll.js' - }, - debug: [ 'CK_DEBUG' ], - disableWatch: false, - identityFile: undefined, - tsconfig: undefined - } ); - - sinon.assert.calledWith( stubs.getWebpackConfig.secondCall, { - cwd: 'workspace', - requireDll: false, - buildDir: 'buildDir', - themePath: 'path/to/theme', - language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, - additionalLanguages: [ 'pl', 'ar' ], - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js' - }, - debug: [ 'CK_DEBUG' ], - disableWatch: false, - identityFile: undefined, - tsconfig: undefined - } ); - - expect( stubs.webpack.calledTwice ).to.equal( true ); - - expect( stubs.webpack.firstCall.args[ 0 ] ).to.deep.equal( { - buildDir: 'buildDir', - entries: { - 'ckeditor5-foo/manual/file2-dll': 'ckeditor5-foo/manual/file2-dll.js' - } - } ); - - expect( stubs.webpack.secondCall.args[ 0 ] ).to.deep.equal( { - buildDir: 'buildDir', - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js' - } - } ); + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( webpack ) ).toHaveBeenCalledTimes( 2 ); } ); it( 'should compile multiple manual test scripts', async () => { + vi.mocked( requireDll ).mockReturnValue( false ); + await compileManualTestScripts( { cwd: 'workspace', buildDir: 'buildDir', @@ -219,26 +162,25 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: null, - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus: vi.fn(), additionalLanguages: null, tsconfig: undefined } ); - expect( stubs.getWebpackConfig.calledOnce ).to.equal( true ); - - expect( stubs.getRelativeFilePath.calledThrice ).to.equal( true ); - expect( stubs.getRelativeFilePath.firstCall.args[ 0 ] ) - .to.equal( 'ckeditor5-build-classic/tests/manual/ckeditor.js' ); - expect( stubs.getRelativeFilePath.secondCall.args[ 0 ] ) - .to.equal( 'ckeditor5-build-classic/tests/manual/ckeditor.compcat.js' ); - expect( stubs.getRelativeFilePath.thirdCall.args[ 0 ] ) - .to.equal( 'ckeditor5-editor-classic/tests/manual/classic.js' ); + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( getRelativeFilePath ) ).toHaveBeenCalledTimes( 3 ); + expect( vi.mocked( getRelativeFilePath ) ).toHaveBeenCalledWith( 'ckeditor5-build-classic/tests/manual/ckeditor.js' ); + expect( vi.mocked( getRelativeFilePath ) ).toHaveBeenCalledWith( 'ckeditor5-build-classic/tests/manual/ckeditor.compcat.js' ); + expect( vi.mocked( getRelativeFilePath ) ).toHaveBeenCalledWith( 'ckeditor5-editor-classic/tests/manual/classic.js' ); } ); - it( 'rejects if webpack threw an error', () => { - webpackError = new Error( 'Unexpected error.' ); + it( 'rejects if webpack threw an error', async () => { + vi.mocked( webpack ).mockImplementation( () => { + throw new Error( 'Unexpected error' ); + } ); + vi.mocked( requireDll ).mockReturnValue( false ); - return compileManualTestScripts( { + await expect( compileManualTestScripts( { buildDir: 'buildDir', sourceFiles: [ 'ckeditor5-foo/manual/file1.js', @@ -246,16 +188,9 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: null, - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus: vi.fn(), additionalLanguages: null - } ).then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - err => { - expect( err ).to.equal( webpackError ); - } - ); + } ) ).rejects.toThrow( 'Unexpected error' ); } ); it( 'works on Windows environments', async () => { @@ -266,12 +201,11 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: null, - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus: vi.fn(), additionalLanguages: null } ); - expect( stubs.getRelativeFilePath.calledOnce ).to.equal( true ); - expect( stubs.getRelativeFilePath.firstCall.args[ 0 ] ).to.equal( 'ckeditor5-build-classic\\tests\\manual\\ckeditor.js' ); + expect( vi.mocked( getRelativeFilePath ) ).toHaveBeenCalledExactlyOnceWith( 'ckeditor5-build-classic\\tests\\manual\\ckeditor.js' ); } ); it( 'should pass identity file to webpack configuration factory', async () => { @@ -286,41 +220,16 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus: vi.fn(), additionalLanguages: [ 'pl', 'ar' ], debug: [ 'CK_DEBUG' ], identityFile, disableWatch: false } ); - expect( stubs.getWebpackConfig.calledOnce ).to.equal( true ); - - sinon.assert.calledWith( stubs.getWebpackConfig.firstCall, { - cwd: 'workspace', - requireDll: false, - buildDir: 'buildDir', - themePath: 'path/to/theme', - language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, - additionalLanguages: [ 'pl', 'ar' ], - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js', - 'ckeditor5-foo/manual/file2': 'ckeditor5-foo/manual/file2.js' - }, - debug: [ 'CK_DEBUG' ], - identityFile, - disableWatch: false, - tsconfig: undefined - } ); - - expect( stubs.webpack.calledOnce ).to.equal( true ); - expect( stubs.webpack.firstCall.args[ 0 ] ).to.deep.equal( { - buildDir: 'buildDir', - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js', - 'ckeditor5-foo/manual/file2': 'ckeditor5-foo/manual/file2.js' - } - } ); + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + identityFile + } ) ); } ); it( 'should pass the "disableWatch" option to webpack configuration factory', async () => { @@ -332,38 +241,15 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus: vi.fn(), additionalLanguages: [ 'pl', 'ar' ], debug: [ 'CK_DEBUG' ], disableWatch: true } ); - expect( stubs.getWebpackConfig.calledOnce ).to.equal( true ); - - sinon.assert.calledWith( stubs.getWebpackConfig.firstCall, { - cwd: 'workspace', - requireDll: false, - buildDir: 'buildDir', - themePath: 'path/to/theme', - language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, - additionalLanguages: [ 'pl', 'ar' ], - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js' - }, - debug: [ 'CK_DEBUG' ], - identityFile: undefined, - disableWatch: true, - tsconfig: undefined - } ); - - expect( stubs.webpack.calledOnce ).to.equal( true ); - expect( stubs.webpack.firstCall.args[ 0 ] ).to.deep.equal( { - buildDir: 'buildDir', - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js' - } - } ); + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { + disableWatch: true + } ) ); } ); it( 'should pass correct entries object to the webpack for both JS and TS files', async () => { @@ -381,21 +267,18 @@ describe( 'compileManualTestScripts', () => { disableWatch: false } ); - expect( stubs.getWebpackConfig.calledOnce ).to.equal( true ); - expect( stubs.getWebpackConfig.firstCall.args[ 0 ] ).to.deep.include( { + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { entries: { 'ckeditor5-foo\\manual\\file1': 'ckeditor5-foo\\manual\\file1.js', 'ckeditor5-foo\\manual\\file2': 'ckeditor5-foo\\manual\\file2.ts' } - } ); - - expect( stubs.webpack.calledOnce ).to.equal( true ); - expect( stubs.webpack.firstCall.args[ 0 ] ).to.deep.include( { + } ) ); + expect( vi.mocked( webpack ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { entries: { 'ckeditor5-foo\\manual\\file1': 'ckeditor5-foo\\manual\\file1.js', 'ckeditor5-foo\\manual\\file2': 'ckeditor5-foo\\manual\\file2.ts' } - } ); + } ), expect.any( Function ) ); } ); it( 'should pass the "tsconfig" option to webpack configuration factory', async () => { @@ -407,37 +290,14 @@ describe( 'compileManualTestScripts', () => { ], themePath: 'path/to/theme', language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, + onTestCompilationStatus: vi.fn(), additionalLanguages: [ 'pl', 'ar' ], debug: [ 'CK_DEBUG' ], tsconfig: '/absolute/path/to/tsconfig.json' } ); - expect( stubs.getWebpackConfig.calledOnce ).to.equal( true ); - - sinon.assert.calledWith( stubs.getWebpackConfig.firstCall, { - cwd: 'workspace', - requireDll: false, - buildDir: 'buildDir', - themePath: 'path/to/theme', - language: 'en', - onTestCompilationStatus: stubs.onTestCompilationStatus, - additionalLanguages: [ 'pl', 'ar' ], - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js' - }, - debug: [ 'CK_DEBUG' ], - identityFile: undefined, - disableWatch: undefined, + expect( vi.mocked( getWebpackConfigForManualTests ) ).toHaveBeenCalledExactlyOnceWith( expect.objectContaining( { tsconfig: '/absolute/path/to/tsconfig.json' - } ); - - expect( stubs.webpack.calledOnce ).to.equal( true ); - expect( stubs.webpack.firstCall.args[ 0 ] ).to.deep.equal( { - buildDir: 'buildDir', - entries: { - 'ckeditor5-foo/manual/file1': 'ckeditor5-foo/manual/file1.js' - } - } ); + } ) ); } ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js index 44676ef51..5192181ed 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js @@ -3,51 +3,36 @@ * For licensing, see LICENSE.md. */ -'use strict'; +import http from 'http'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import createManualTestServer from '../../../lib/utils/manual-tests/createserver.js'; -const http = require( 'http' ); -const { use, expect } = require( 'chai' ); -const sinon = require( 'sinon' ); -const sinonChai = require( 'sinon-chai' ); -const mockery = require( 'mockery' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); -use( sinonChai ); +describe( 'createManualTestServer()', () => { + let loggerStub, server; -describe( 'createManualTestServer', () => { - let sandbox, httpCreateServerStub, createManualTestServer, server, loggerStub; + beforeEach( async () => { + const { createServer } = http; - beforeEach( () => { - sandbox = sinon.createSandbox(); + loggerStub = vi.fn(); - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false + vi.mocked( logger ).mockReturnValue( { + info: loggerStub } ); - loggerStub = sinon.stub(); + vi.spyOn( http, 'createServer' ).mockImplementation( ( ...theArgs ) => { + server = createServer( ...theArgs ); - httpCreateServerStub = sandbox.stub( http, 'createServer' ).callsFake( function stubbedCreateServer( ...theArgs ) { - server = httpCreateServerStub.wrappedMethod( ...theArgs ); - sandbox.spy( server, 'listen' ); + vi.spyOn( server, 'listen' ); return server; } ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - logger() { - return { - info: loggerStub - }; - } - } ); - - createManualTestServer = require( '../../../lib/utils/manual-tests/createserver' ); } ); afterEach( () => { server.close(); - sandbox.restore(); // To avoid false positives and encourage better testing practices, Mocha will no longer automatically // kill itself via `process.exit()` when it thinks it should be done running. Hence, we must close the stream @@ -55,40 +40,41 @@ describe( 'createManualTestServer', () => { if ( server._readline ) { server._readline.close(); } - - mockery.disable(); } ); it( 'should start http server', () => { createManualTestServer( 'workspace/build/.manual-tests' ); - expect( httpCreateServerStub ).to.be.calledOnce; + expect( vi.mocked( http ).createServer ).toHaveBeenCalledOnce(); } ); it( 'should listen on given port', () => { createManualTestServer( 'workspace/build/.manual-tests', 8888 ); - expect( httpCreateServerStub.returnValues[ 0 ].listen ).to.be.calledOnceWith( 8888 ); + expect( server ).toEqual( expect.objectContaining( { + listen: expect.any( Function ) + } ) ); - expect( loggerStub.calledOnce ).to.equal( true ); - expect( loggerStub.firstCall.firstArg ).to.equal( '[Server] Server running at http://localhost:8888/' ); + expect( server.listen ).toHaveBeenCalledExactlyOnceWith( 8888 ); + expect( loggerStub ).toHaveBeenCalledExactlyOnceWith( '[Server] Server running at http://localhost:8888/' ); } ); it( 'should listen on 8125 port if no specific port was given', () => { createManualTestServer( 'workspace/build/.manual-tests' ); - expect( httpCreateServerStub.returnValues[ 0 ].listen ).to.be.calledOnceWith( 8125 ); + expect( server ).toEqual( expect.objectContaining( { + listen: expect.any( Function ) + } ) ); - expect( loggerStub.calledOnce ).to.equal( true ); - expect( loggerStub.firstCall.firstArg ).to.equal( '[Server] Server running at http://localhost:8125/' ); + expect( server.listen ).toHaveBeenCalledExactlyOnceWith( 8125 ); + expect( loggerStub ).toHaveBeenCalledExactlyOnceWith( '[Server] Server running at http://localhost:8125/' ); } ); - it( 'should call the specificed callback when the server is running (e.g. to allow running web sockets)', () => { - const spy = sinon.spy(); + it( 'should call the specified callback when the server is running (e.g. to allow running web sockets)', () => { + const spy = vi.fn(); createManualTestServer( 'workspace/build/.manual-tests', 1234, spy ); - sinon.assert.calledOnce( spy ); - sinon.assert.calledWithExactly( spy, server ); + expect( spy ).toHaveBeenCalledExactlyOnceWith( server ); } ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/getwebpackconfig.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/getwebpackconfig.js index cf1f40ddb..01ed4c270 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/getwebpackconfig.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/getwebpackconfig.js @@ -3,72 +3,46 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const mockery = require( 'mockery' ); -const sinon = require( 'sinon' ); -const { expect } = require( 'chai' ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { loaders } from '@ckeditor/ckeditor5-dev-utils'; +import getDefinitionsFromFile from '../../../lib/utils/getdefinitionsfromfile.js'; +import getWebpackConfigForManualTests from '../../../lib/utils/manual-tests/getwebpackconfig.js'; + +const stubs = vi.hoisted( () => ( { + translations: { + plugin: { + constructor: vi.fn() + } + } +} ) ); + +vi.mock( 'webpack' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( '@ckeditor/ckeditor5-dev-translations', () => ( { + CKEditorTranslationsPlugin: class CKEditorTranslationsPlugin { + constructor( ...args ) { + stubs.translations.plugin.constructor( ...args ); + } + } +} ) ); +vi.mock( '../../../lib/utils/getdefinitionsfromfile.js' ); describe( 'getWebpackConfigForManualTests()', () => { - let getWebpackConfigForManualTests, stubs; - beforeEach( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - stubs = { - getDefinitionsFromFile: sinon.stub().returns( {} ), - loaders: { - getIconsLoader: sinon.stub().returns( {} ), - getStylesLoader: sinon.stub().returns( {} ), - getTypeScriptLoader: sinon.stub().returns( {} ), - getFormattedTextLoader: sinon.stub().returns( {} ), - getCoverageLoader: sinon.stub().returns( {} ), - getJavaScriptLoader: sinon.stub().returns( {} ) - }, - logger: {}, - webpack: { - DefinePlugin: sinon.stub(), - ProvidePlugin: sinon.stub(), - SourceMapDevToolPlugin: sinon.stub() - }, - devTranslations: { - CKEditorTranslationsPlugin: class { - constructor( args ) { - this.args = args; - } - } - } - }; - - mockery.registerMock( 'webpack', stubs.webpack ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-translations', stubs.devTranslations ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - loaders: stubs.loaders, - logger: () => stubs.logger - } ); - - getWebpackConfigForManualTests = require( '../../../lib/utils/manual-tests/getwebpackconfig' ); - } ); - - afterEach( () => { - sinon.restore(); - mockery.disable(); - mockery.deregisterAll(); + vi.mocked( getDefinitionsFromFile ).mockReturnValue( {} ); + vi.mocked( loaders ).getIconsLoader.mockReturnValue( {} ); + vi.mocked( loaders ).getStylesLoader.mockReturnValue( {} ); + vi.mocked( loaders ).getTypeScriptLoader.mockReturnValue( {} ); + vi.mocked( loaders ).getFormattedTextLoader.mockReturnValue( {} ); + vi.mocked( loaders ).getCoverageLoader.mockReturnValue( {} ); + vi.mocked( loaders ).getJavaScriptLoader.mockReturnValue( {} ); } ); it( 'should return webpack configuration object', () => { const entries = { 'ckeditor5/tests/manual/all-features': '/home/ckeditor/ckeditor5/tests/manual/all-features.js' }; - const buildDir = '/home/ckeditor/ckeditor5/build/.manual-tests'; - const debug = []; const webpackConfig = getWebpackConfigForManualTests( { @@ -79,37 +53,42 @@ describe( 'getWebpackConfigForManualTests()', () => { tsconfig: '/tsconfig/path' } ); - expect( stubs.loaders.getIconsLoader.calledOnce ).to.equal( true ); - expect( stubs.loaders.getIconsLoader.firstCall.args[ 0 ] ).to.have.property( 'matchExtensionOnly', true ); - - expect( stubs.loaders.getStylesLoader.calledOnce ).to.equal( true ); - expect( stubs.loaders.getStylesLoader.firstCall.args[ 0 ] ).to.have.property( 'themePath', '/theme/path' ); - expect( stubs.loaders.getStylesLoader.firstCall.args[ 0 ] ).to.have.property( 'sourceMap', true ); + expect( vi.mocked( loaders ).getIconsLoader ).toHaveBeenCalledExactlyOnceWith( { + matchExtensionOnly: true + } ); + expect( vi.mocked( loaders ).getStylesLoader ).toHaveBeenCalledExactlyOnceWith( { + themePath: '/theme/path', + sourceMap: true + } ); - expect( stubs.loaders.getTypeScriptLoader.calledOnce ).to.equal( true ); + expect( vi.mocked( loaders ).getTypeScriptLoader ).toHaveBeenCalledExactlyOnceWith( { + debugFlags: debug, + configFile: '/tsconfig/path', + includeDebugLoader: true + } ); - expect( stubs.loaders.getTypeScriptLoader.firstCall.args[ 0 ] ).to.have.property( 'debugFlags', debug ); - expect( stubs.loaders.getTypeScriptLoader.firstCall.args[ 0 ] ).to.have.property( 'configFile', '/tsconfig/path' ); - expect( stubs.loaders.getTypeScriptLoader.firstCall.args[ 0 ] ).to.have.property( 'includeDebugLoader', true ); + expect( vi.mocked( loaders ).getFormattedTextLoader ).toHaveBeenCalledOnce(); - expect( stubs.loaders.getFormattedTextLoader.calledOnce ).to.equal( true ); + expect( vi.mocked( loaders ).getJavaScriptLoader ).toHaveBeenCalledExactlyOnceWith( { + debugFlags: debug + } ); - expect( stubs.loaders.getJavaScriptLoader.calledOnce ).to.equal( true ); - expect( stubs.loaders.getJavaScriptLoader.firstCall.args[ 0 ] ).to.have.property( 'debugFlags', debug ); + expect( vi.mocked( loaders ).getCoverageLoader ).not.toHaveBeenCalledOnce(); - expect( stubs.loaders.getCoverageLoader.called ).to.equal( false ); + expect( webpackConfig ).toEqual( expect.objectContaining( { + // To avoid "eval()" in files. + mode: 'none', + entry: entries, + output: { + path: buildDir + }, + plugins: expect.any( Array ), + watch: true, + resolve: expect.any( Object ) + } ) ); - expect( webpackConfig ).to.be.an( 'object' ); expect( webpackConfig.resolve.fallback.timers ).to.equal( false ); - // To avoid "eval()" in files. - expect( webpackConfig ).to.have.property( 'mode', 'none' ); - expect( webpackConfig ).to.have.property( 'entry', entries ); - expect( webpackConfig ).to.have.property( 'output' ); - expect( webpackConfig.output ).to.deep.equal( { path: buildDir } ); - expect( webpackConfig ).to.have.property( 'plugins' ); - expect( webpackConfig ).to.have.property( 'watch', true ); - // The `devtool` property has been replaced by the `SourceMapDevToolPlugin()`. expect( webpackConfig ).to.not.have.property( 'devtool' ); } ); @@ -125,12 +104,15 @@ describe( 'getWebpackConfigForManualTests()', () => { it( 'pattern passed to CKEditorTranslationsPlugin should match paths to ckeditor5 packages', () => { const webpackConfig = getWebpackConfigForManualTests( { disableWatch: true } ); - expect( webpackConfig ).to.have.property( 'plugins' ); - expect( webpackConfig.plugins ).to.be.an( 'Array' ); + expect( stubs.translations.plugin.constructor ).toHaveBeenCalledOnce(); const CKEditorTranslationsPlugin = webpackConfig.plugins.find( plugin => plugin.constructor.name === 'CKEditorTranslationsPlugin' ); - const pattern = CKEditorTranslationsPlugin.args.packageNamesPattern; + expect( CKEditorTranslationsPlugin ).toBeTruthy(); + + const [ firstCall ] = stubs.translations.plugin.constructor.mock.calls; + const [ firstArg ] = firstCall; + const { packageNamesPattern: pattern } = firstArg; expect( 'packages/ckeditor5-foo/bar'.match( pattern )[ 0 ] ).to.equal( 'packages/ckeditor5-foo/' ); } ); @@ -138,12 +120,15 @@ describe( 'getWebpackConfigForManualTests()', () => { it( 'pattern passed to CKEditorTranslationsPlugin should match paths to external repositories named like ckeditor5 package', () => { const webpackConfig = getWebpackConfigForManualTests( { disableWatch: true } ); - expect( webpackConfig ).to.have.property( 'plugins' ); - expect( webpackConfig.plugins ).to.be.an( 'Array' ); + expect( stubs.translations.plugin.constructor ).toHaveBeenCalledOnce(); const CKEditorTranslationsPlugin = webpackConfig.plugins.find( plugin => plugin.constructor.name === 'CKEditorTranslationsPlugin' ); - const pattern = CKEditorTranslationsPlugin.args.packageNamesPattern; + expect( CKEditorTranslationsPlugin ).toBeTruthy(); + + const [ firstCall ] = stubs.translations.plugin.constructor.mock.calls; + const [ firstArg ] = firstCall; + const { packageNamesPattern: pattern } = firstArg; expect( 'external/ckeditor5-foo/packages/ckeditor5-bar/baz'.match( pattern )[ 0 ] ).to.equal( 'packages/ckeditor5-bar/' ); } ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js index 1ea41d208..86758d0d3 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js @@ -3,76 +3,46 @@ * For licensing, see LICENSE.md. */ -'use strict'; - -const mockery = require( 'mockery' ); -const { expect } = require( 'chai' ); -const sinon = require( 'sinon' ); - -describe( 'removeDir', () => { - let sandbox, removeDir; - const logMessages = []; - const deletedPaths = []; - - before( () => { - mockery.enable( { - useCleanCache: true, - warnOnReplace: false, - warnOnUnregistered: false - } ); - - mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', { - logger: () => ( { - info( message ) { - logMessages.push( message ); - } - } ) - } ); - - mockery.registerMock( 'del', path => { - return Promise.resolve().then( () => { - deletedPaths.push( path ); - } ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import del from 'del'; +import { logger } from '@ckeditor/ckeditor5-dev-utils'; +import chalk from 'chalk'; +import removeDir from '../../../lib/utils/manual-tests/removedir.js'; + +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'del' ); +vi.mock( 'chalk', () => ( { + default: { + cyan: vi.fn() + } +} ) ); + +describe( 'removeDir()', () => { + let logInfo; + beforeEach( () => { + logInfo = vi.fn(); + + vi.mocked( del ).mockResolvedValue(); + vi.mocked( chalk ).cyan.mockImplementation( input => input ); + vi.mocked( logger ).mockReturnValue( { + info: logInfo } ); - - mockery.registerMock( 'chalk', { - cyan: message => `\u001b[36m${ message }\u001b[39m` - } ); - - removeDir = require( '../../../lib/utils/manual-tests/removedir' ); - sandbox = sinon.createSandbox(); } ); - after( () => { - mockery.disable(); - mockery.deregisterAll(); - } ); + it( 'should remove directory and log it', async () => { + await removeDir( 'workspace/directory' ); - afterEach( () => { - sandbox.restore(); - logMessages.length = 0; - deletedPaths.length = 0; + expect( vi.mocked( chalk ).cyan ).toHaveBeenCalledOnce(); + expect( vi.mocked( del ) ).toHaveBeenCalledExactlyOnceWith( 'workspace/directory' ); + expect( logInfo ).toHaveBeenCalledExactlyOnceWith( 'Removed directory \'workspace/directory\'' ); } ); - it( 'should remove directory and log it', () => { - return removeDir( 'workspace/directory' ).then( () => { - expect( logMessages ).to.deep.equal( [ - 'Removed directory \'\u001b[36mworkspace/directory\u001b[39m\'' - ] ); + it( 'should remove directory and does not inform about it', async () => { + await removeDir( 'workspace/directory', { silent: true } ); - expect( deletedPaths ).to.deep.equal( [ - 'workspace/directory' - ] ); - } ); - } ); - - it( 'should remove directory and does not inform about it', () => { - return removeDir( 'workspace/directory', { silent: true } ).then( () => { - expect( logMessages ).to.deep.equal( [] ); + expect( vi.mocked( del ) ).toHaveBeenCalledExactlyOnceWith( 'workspace/directory' ); - expect( deletedPaths ).to.deep.equal( [ - 'workspace/directory' - ] ); - } ); + expect( vi.mocked( chalk ).cyan ).not.toHaveBeenCalled(); + expect( logInfo ).not.toHaveBeenCalled(); } ); } ); From edd1a7021feb4055086dbbe764264ceb8286ac6c Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 18 Sep 2024 13:48:44 +0200 Subject: [PATCH 106/172] 100% tests to Vitest. --- .../utils/automated-tests/parsearguments.js | 7 +- .../utils/automated-tests/parsearguments.js | 246 +++++++++--------- 2 files changed, 119 insertions(+), 134 deletions(-) diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js index fe654960b..d201b89ae 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js @@ -3,14 +3,11 @@ * For licensing, see LICENSE.md. */ -import fs from 'fs'; +import fs from 'fs-extra'; import path from 'path'; -import { createRequire } from 'module'; import minimist from 'minimist'; import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; -const require = createRequire( import.meta.url ); - /** * @param {Array.} args * @returns {Object} @@ -173,7 +170,7 @@ export default function parseArguments( args ) { const files = new Set( options.files ); for ( const repositoryName of options.repositories ) { - const cwdPackageJson = require( path.join( cwd, 'package.json' ) ); + const cwdPackageJson = fs.readJsonSync( path.join( cwd, 'package.json' ) ); // Check the main repository. if ( repositoryName === cwdPackageJson.name ) { diff --git a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js index 75c66178a..6c8937632 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js +++ b/packages/ckeditor5-dev-tests/tests/utils/automated-tests/parsearguments.js @@ -3,64 +3,35 @@ * For licensing, see LICENSE.md. */ -import { describe, expect, it, vi } from 'vitest'; - -const originalPosixJoin = path.posix.join; - -vi.mock( 'fs' ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import fs from 'fs-extra'; +import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; +import parseArguments from '../../../lib/utils/automated-tests/parsearguments.js'; + +vi.mock( 'path', () => ( { + default: { + join: vi.fn( ( ...chunks ) => chunks.join( '/' ) ), + dirname: vi.fn() + } +} ) ); +vi.mock( 'fs-extra' ); vi.mock( '@ckeditor/ckeditor5-dev-utils' ); describe( 'parseArguments()', () => { - let parseArguments, sandbox, stubs, packageName; + let logWarningStub; beforeEach( () => { - vi.spyOn( process, 'cwd' ).mockReturnValue( '/' ); - - stubs = { - cwd: sandbox.stub( process, 'cwd' ).callsFake( () => '/' ), - existsSync: sandbox.stub( fs, 'existsSync' ), - tools: { - getDirectories: sandbox.stub() - }, - logger: { - warning: sandbox.stub() - }, - fs: { - statSync: sandbox.stub() - }, - // To force unix paths in tests. - pathJoin: sandbox.stub( path, 'join' ).callsFake( ( ...chunks ) => originalPosixJoin( ...chunks ) ) - }; - - stubs.cwd.returns( '/home/project' ); - - packageName = 'ckeditor5'; - - mockery.registerMock( '/home/project/package.json', { - get name() { - return packageName; - } - } ); + logWarningStub = vi.fn(); - // mockery.registerMock( 'fs', stubs.fs ); - - parseArguments = proxyquire( '../../../lib/utils/automated-tests/parsearguments', { - '@ckeditor/ckeditor5-dev-utils': { - logger() { - return stubs.logger; - }, - tools: stubs.tools - }, - 'fs': stubs.fs + vi.spyOn( process, 'cwd' ).mockReturnValue( '/home/project' ); + vi.mocked( logger ).mockReturnValue( { + warning: logWarningStub } ); } ); - afterEach( () => { - sandbox.restore(); - mockery.disable(); - } ); - it( 'replaces kebab-case strings with camelCase values', () => { + vi.mocked( fs ).readJsonSync.mockReturnValue( {} ); + const options = parseArguments( [ '--source-map', 'true', @@ -87,6 +58,8 @@ describe( 'parseArguments()', () => { } ); it( 'deletes all aliases keys from returned object', () => { + vi.mocked( fs ).readJsonSync.mockReturnValue( {} ); + const options = parseArguments( [ '-b', 'Chrome,Firefox', @@ -180,63 +153,67 @@ describe( 'parseArguments()', () => { expect( options.repositories ).to.deep.equal( [] ); } ); - it( - 'returns an array of packages to tests when `--repositories` is specified ' + - '(root directory check)', - () => { - packageName = 'ckeditor5'; - - stubs.fs.statSync.withArgs( '/home/project/external' ).throws( 'ENOENT: no such file or directory' ); + it( 'returns an array of packages to tests when `--repositories` is specified (root directory check)', () => { + vi.mocked( fs ).readJsonSync.mockReturnValue( { name: 'ckeditor5' } ); + vi.mocked( fs ).statSync.mockImplementation( input => { + if ( input === '/home/project/external' ) { + throw new Error( 'ENOENT: no such file or directory' ); + } + } ); + vi.mocked( tools ).getDirectories.mockImplementation( input => { + if ( input === '/home/project/packages' ) { + return [ + 'ckeditor5-core', + 'ckeditor5-engine' + ]; + } + } ); - stubs.tools.getDirectories.withArgs( '/home/project/packages' ).returns( [ - 'ckeditor5-core', - 'ckeditor5-engine' - ] ); - - const options = parseArguments( [ - '--repositories', - 'ckeditor5' - ] ); - - expect( options.files ).to.deep.equal( [ 'core', 'engine' ] ); + const options = parseArguments( [ + '--repositories', + 'ckeditor5' + ] ); - expect( stubs.logger.warning.callCount ).to.equal( 1 ); - expect( stubs.logger.warning.firstCall.args[ 0 ] ).to.equal( - 'The `external/` directory does not exist. Only the root repository will be checked.' - ); - } - ); + expect( options.files ).to.deep.equal( [ 'core', 'engine' ] ); - it( - 'returns an array of packages to tests when `--repositories` is specified ' + - '(external directory check)', - () => { - packageName = 'foo'; + expect( logWarningStub ).toHaveBeenCalledExactlyOnceWith( + 'The `external/` directory does not exist. Only the root repository will be checked.' + ); + } ); - stubs.fs.statSync.returns( { isDirectory: () => true } ); - stubs.tools.getDirectories.withArgs( '/home/project/external/ckeditor5/packages' ).returns( [ - 'ckeditor5-core', - 'ckeditor5-engine' - ] ); + it( 'returns an array of packages to tests when `--repositories` is specified (external directory check)', () => { + vi.mocked( fs ).readJsonSync.mockReturnValue( { name: 'foo' } ); + vi.mocked( fs ).statSync.mockReturnValue( { isDirectory: () => true } ); + vi.mocked( tools ).getDirectories.mockImplementation( input => { + if ( input === '/home/project/external/ckeditor5/packages' ) { + return [ + 'ckeditor5-core', + 'ckeditor5-engine' + ]; + } + } ); - const options = parseArguments( [ - '--repositories', - 'ckeditor5' - ] ); + const options = parseArguments( [ + '--repositories', + 'ckeditor5' + ] ); - expect( options.files ).to.deep.equal( [ 'core', 'engine' ] ); - expect( stubs.logger.warning.callCount ).to.equal( 0 ); - } - ); + expect( options.files ).to.deep.equal( [ 'core', 'engine' ] ); + expect( logWarningStub ).not.toHaveBeenCalled(); + } ); it( 'returns an array of packages to tests when `--repositories` is specified ' + '(external directory check, specified repository does not exist)', () => { - packageName = 'foo'; + vi.mocked( fs ).readJsonSync.mockReturnValue( { name: 'foo' } ); + vi.mocked( fs ).statSync.mockImplementation( input => { + if ( input === '/home/project/external' ) { + return { isDirectory: () => true }; + } - stubs.fs.statSync.withArgs( '/home/project/external' ).returns( { isDirectory: () => true } ); - stubs.fs.statSync.withArgs( '/home/project/external/ckeditor5' ).throws( 'ENOENT: no such file or directory' ); + throw new Error( 'ENOENT: no such file or directory' ); + } ); const options = parseArguments( [ '--repositories', @@ -244,9 +221,7 @@ describe( 'parseArguments()', () => { ] ); expect( options.files ).to.deep.equal( [] ); - - expect( stubs.logger.warning.callCount ).to.equal( 1 ); - expect( stubs.logger.warning.firstCall.args[ 0 ] ).to.equal( + expect( logWarningStub ).toHaveBeenCalledExactlyOnceWith( 'Did not find the repository "ckeditor5" in the root repository or the "external/" directory.' ); } @@ -256,14 +231,23 @@ describe( 'parseArguments()', () => { 'returns an array of packages (unique list) to tests when `--repositories` is specified ' + '(root directory check + `--files` specified)', () => { - packageName = 'ckeditor5'; - - stubs.fs.statSync.withArgs( '/home/project/external' ).returns( { isDirectory: () => true } ); - stubs.tools.getDirectories.withArgs( '/home/project/packages' ).returns( [ - 'ckeditor5-core', - 'ckeditor5-engine', - 'ckeditor5-utils' - ] ); + vi.mocked( fs ).readJsonSync.mockReturnValue( { name: 'ckeditor5' } ); + vi.mocked( fs ).statSync.mockImplementation( input => { + if ( input === '/home/project/external' ) { + return { isDirectory: () => true }; + } + + throw new Error( 'ENOENT: no such file or directory' ); + } ); + vi.mocked( tools ).getDirectories.mockImplementation( input => { + if ( input === '/home/project/packages' ) { + return [ + 'ckeditor5-core', + 'ckeditor5-engine', + 'ckeditor5-utils' + ]; + } + } ); const options = parseArguments( [ '--repositories', @@ -280,23 +264,28 @@ describe( 'parseArguments()', () => { 'returns an array of packages to tests when `--repositories` is specified ' + '(root and external directories check)', () => { - packageName = 'ckeditor5'; - - stubs.fs.statSync.withArgs( '/home/project/external' ).returns( { isDirectory: () => true } ); - stubs.fs.statSync.withArgs( '/home/project/external/foo' ).returns( { isDirectory: () => true } ); - stubs.fs.statSync.withArgs( '/home/project/external/bar' ).returns( { isDirectory: () => true } ); - stubs.tools.getDirectories.withArgs( '/home/project/packages' ).returns( [ - 'ckeditor5-core', - 'ckeditor5-engine' - ] ); - stubs.tools.getDirectories.withArgs( '/home/project/external/foo/packages' ).returns( [ - 'ckeditor5-foo-1', - 'ckeditor5-foo-2' - ] ); - stubs.tools.getDirectories.withArgs( '/home/project/external/bar/packages' ).returns( [ - 'ckeditor5-bar-1', - 'ckeditor5-bar-2' - ] ); + vi.mocked( fs ).readJsonSync.mockReturnValue( { name: 'ckeditor5' } ); + vi.mocked( fs ).statSync.mockReturnValue( { isDirectory: () => true } ); + vi.mocked( tools ).getDirectories.mockImplementation( input => { + if ( input === '/home/project/packages' ) { + return [ + 'ckeditor5-core', + 'ckeditor5-engine' + ]; + } + if ( input === '/home/project/external/foo/packages' ) { + return [ + 'ckeditor5-foo-1', + 'ckeditor5-foo-2' + ]; + } + if ( input === '/home/project/external/bar/packages' ) { + return [ + 'ckeditor5-bar-1', + 'ckeditor5-bar-2' + ]; + } + } ); const options = parseArguments( [ '--repositories', @@ -330,7 +319,7 @@ describe( 'parseArguments()', () => { describe( 'tsconfig', () => { it( 'should be null by default, if `tsconfig.test.json` does not exist', () => { - stubs.existsSync.returns( false ); + vi.mocked( fs ).existsSync.mockReturnValue( false ); const options = parseArguments( [] ); @@ -338,8 +327,7 @@ describe( 'parseArguments()', () => { } ); it( 'should use `tsconfig.test.json` from `cwd` if it is available by default', () => { - stubs.cwd.returns( '/home/project' ); - stubs.existsSync.returns( true ); + vi.mocked( fs ).existsSync.mockReturnValue( true ); const options = parseArguments( [] ); @@ -347,16 +335,16 @@ describe( 'parseArguments()', () => { } ); it( 'should parse `--tsconfig` to absolute path if it is set and it exists', () => { - stubs.cwd.returns( '/home/project' ); - stubs.existsSync.returns( true ); - const options = parseArguments( [ '--tsconfig', './configs/tsconfig.json' ] ); + vi.mocked( fs ).existsSync.mockReturnValue( true ); + + const options = parseArguments( [ '--tsconfig', 'configs/tsconfig.json' ] ); expect( options.tsconfig ).to.be.equal( '/home/project/configs/tsconfig.json' ); } ); it( 'should be null if `--tsconfig` points to non-existing file', () => { - stubs.cwd.returns( '/home/project' ); - stubs.existsSync.returns( false ); + vi.mocked( fs ).existsSync.mockReturnValue( false ); + const options = parseArguments( [ '--tsconfig', './configs/tsconfig.json' ] ); expect( options.tsconfig ).to.equal( null ); From f7137388d9a07d69c11a0947370b0c9752320e48 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Wed, 18 Sep 2024 13:54:17 +0200 Subject: [PATCH 107/172] ESM for a script that updates licenses. --- scripts/bump-year.js | 45 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/scripts/bump-year.js b/scripts/bump-year.js index 993315aaa..6465720b5 100644 --- a/scripts/bump-year.js +++ b/scripts/bump-year.js @@ -17,26 +17,27 @@ git commit -am "Internal: Bumped the year." && git push */ -require( '@ckeditor/ckeditor5-dev-bump-year' ) - .bumpYear( { - cwd: process.cwd(), - globPatterns: [ - { // LICENSE.md, .eslintrc.js, etc. - pattern: '*', - options: { - dot: true - } - }, - { - pattern: '.husky/*' - }, - { - pattern: '!(coverage|.nyc_output)/**', - options: { - ignore: [ - '**/typedoc-plugins/tests/module-fixer/fixtures/emptyfile.ts' - ] - } +import { bumpYear } from '@ckeditor/ckeditor5-dev-bump-year'; + +bumpYear( { + cwd: process.cwd(), + globPatterns: [ + { // LICENSE.md, .eslintrc.js, etc. + pattern: '*', + options: { + dot: true + } + }, + { + pattern: '.husky/*' + }, + { + pattern: '!(coverage|.nyc_output)/**', + options: { + ignore: [ + '**/typedoc-plugins/tests/module-fixer/fixtures/emptyfile.ts' + ] } - ] - } ); + } + ] +} ); From 0aa7ab0cd83c14303e4987176b1de06c18e9672b Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Thu, 19 Sep 2024 08:50:00 +0200 Subject: [PATCH 108/172] Replaced require with import (readline) for Windows. --- .../lib/utils/manual-tests/createserver.js | 12 ++++------ .../tests/utils/manual-tests/createserver.js | 23 +++++++++++++------ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js index bafe7dfc4..1dcd59108 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js @@ -4,12 +4,13 @@ */ import http from 'http'; +import fs from 'fs'; import path from 'path'; +import readline from 'readline'; +import { fileURLToPath } from 'url'; import { globSync } from 'glob'; -import fs from 'fs'; import combine from 'dom-combiner'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; -import { fileURLToPath } from 'url'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); @@ -30,15 +31,12 @@ export default function createManualTestServer( sourcePath, port = 8125, onCreat // SIGINT isn't caught on Windows in process. However, `CTRL+C` can be caught // by `readline` module. After that we can emit SIGINT to the process manually. if ( process.platform === 'win32' ) { - const readline = require( 'readline' ).createInterface( { + const readlineInterface = readline.createInterface( { input: process.stdin, output: process.stdout } ); - // Save the reference of the stream to be able to close it in tests. - server._readline = readline; - - readline.on( 'SIGINT', () => { + readlineInterface.on( 'SIGINT', () => { process.emit( 'SIGINT' ); } ); } diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js index 5192181ed..4abfaf93c 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js @@ -4,11 +4,13 @@ */ import http from 'http'; +import readline from 'readline'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; import createManualTestServer from '../../../lib/utils/manual-tests/createserver.js'; vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'readline' ); describe( 'createManualTestServer()', () => { let loggerStub, server; @@ -33,13 +35,6 @@ describe( 'createManualTestServer()', () => { afterEach( () => { server.close(); - - // To avoid false positives and encourage better testing practices, Mocha will no longer automatically - // kill itself via `process.exit()` when it thinks it should be done running. Hence, we must close the stream - // before leaving the test. See: https://stackoverflow.com/a/52143003. - if ( server._readline ) { - server._readline.close(); - } } ); it( 'should start http server', () => { @@ -77,4 +72,18 @@ describe( 'createManualTestServer()', () => { expect( spy ).toHaveBeenCalledExactlyOnceWith( server ); } ); + + it( 'should use "readline" to listen to the SIGINT event on Windows', () => { + const readlineInterface = { + on: vi.fn() + }; + + vi.mocked( readline ).createInterface.mockReturnValue( readlineInterface ); + vi.spyOn( process, 'platform', 'get' ).mockReturnValue( 'win32' ); + + createManualTestServer( 'workspace/build/.manual-tests' ); + + expect( vi.mocked( readline ).createInterface ).toHaveBeenCalledOnce(); + expect( readlineInterface.on ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', expect.any( Function ) ); + } ); } ); From ab5491dddea4f31e5c966d2c9d6e31d8a1f53ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Thu, 19 Sep 2024 13:36:31 +0200 Subject: [PATCH 109/172] Aligned to `ckeditor5-dev` ESM changes. --- .eslintrc.cjs | 16 ++++++++++------ package.json | 2 ++ scripts/ci/is-project-ready-to-release.js | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 515231078..40af67664 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -10,15 +10,19 @@ module.exports = { env: { node: true }, + parser: '@babel/eslint-parser', + parserOptions: { + requireConfigFile: false, + babelOptions: { + parserOpts: { + plugins: [ 'importAttributes' ] + } + } + }, ignorePatterns: [ '**/dist/*', '**/coverage/**', - '**/node_modules/**', - - // ESLint does not understand `import ... with { ... }`. - // See: https://github.com/eslint/eslint/discussions/15305. - 'packages/ckeditor5-dev-ci/lib/data/index.js', - 'packages/ckeditor5-dev-transifex/lib/data/index.js' + '**/node_modules/**' ], rules: { 'no-console': 'off', diff --git a/package.json b/package.json index dceda2c5c..bb1476bbc 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ }, "type": "module", "devDependencies": { + "@babel/core": "^7.25.1", + "@babel/eslint-parser": "^7.25.1", "@ckeditor/ckeditor5-dev-ci": "^43.0.0", "@ckeditor/ckeditor5-dev-release-tools": "^43.0.0", "@ckeditor/ckeditor5-dev-bump-year": "^43.0.0", diff --git a/scripts/ci/is-project-ready-to-release.js b/scripts/ci/is-project-ready-to-release.js index 3d9cf254d..d2ffd7e56 100644 --- a/scripts/ci/is-project-ready-to-release.js +++ b/scripts/ci/is-project-ready-to-release.js @@ -7,7 +7,7 @@ /* eslint-env node */ -import releaseTools from '@ckeditor/ckeditor5-dev-release-tools'; +import * as releaseTools from '@ckeditor/ckeditor5-dev-release-tools'; const changelogVersion = releaseTools.getLastFromChangelog(); const npmTag = releaseTools.getNpmTagFromVersion( changelogVersion ); From 5facca7f1d50cc8b70b3dabfadaa05c03b3062d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Thu, 19 Sep 2024 14:27:36 +0200 Subject: [PATCH 110/172] Reverted changes with `'@babel/eslint-parser'` as it does not work with TypeScript. --- .eslintrc.cjs | 16 ++++++---------- package.json | 2 -- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 40af67664..515231078 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -10,19 +10,15 @@ module.exports = { env: { node: true }, - parser: '@babel/eslint-parser', - parserOptions: { - requireConfigFile: false, - babelOptions: { - parserOpts: { - plugins: [ 'importAttributes' ] - } - } - }, ignorePatterns: [ '**/dist/*', '**/coverage/**', - '**/node_modules/**' + '**/node_modules/**', + + // ESLint does not understand `import ... with { ... }`. + // See: https://github.com/eslint/eslint/discussions/15305. + 'packages/ckeditor5-dev-ci/lib/data/index.js', + 'packages/ckeditor5-dev-transifex/lib/data/index.js' ], rules: { 'no-console': 'off', diff --git a/package.json b/package.json index bb1476bbc..dceda2c5c 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,6 @@ }, "type": "module", "devDependencies": { - "@babel/core": "^7.25.1", - "@babel/eslint-parser": "^7.25.1", "@ckeditor/ckeditor5-dev-ci": "^43.0.0", "@ckeditor/ckeditor5-dev-release-tools": "^43.0.0", "@ckeditor/ckeditor5-dev-bump-year": "^43.0.0", From 57ae04cbd99cfaaf0e1dfd407b540092d9fa94f0 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 20 Sep 2024 07:56:11 +0200 Subject: [PATCH 111/172] Fixed re-exports in ckeditor5-dev-web-crawler. --- .../lib/constants.js | 30 ++++++------------- .../ckeditor5-dev-web-crawler/lib/spinner.js | 9 ++---- .../ckeditor5-dev-web-crawler/lib/utils.js | 12 ++------ 3 files changed, 14 insertions(+), 37 deletions(-) diff --git a/packages/ckeditor5-dev-web-crawler/lib/constants.js b/packages/ckeditor5-dev-web-crawler/lib/constants.js index 9d1f3ac95..6d143ea20 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/constants.js +++ b/packages/ckeditor5-dev-web-crawler/lib/constants.js @@ -9,15 +9,15 @@ import { cpus } from 'os'; -const DEFAULT_CONCURRENCY = cpus().length / 2; +export const DEFAULT_CONCURRENCY = cpus().length / 2; -const DEFAULT_TIMEOUT = 15 * 1000; +export const DEFAULT_TIMEOUT = 15 * 1000; -const DEFAULT_RESPONSIVENESS_CHECK_TIMEOUT = 1000; +export const DEFAULT_RESPONSIVENESS_CHECK_TIMEOUT = 1000; -const DEFAULT_REMAINING_ATTEMPTS = 3; +export const DEFAULT_REMAINING_ATTEMPTS = 3; -const ERROR_TYPES = { +export const ERROR_TYPES = { PAGE_CRASH: { event: 'error', description: 'Page crash' @@ -45,7 +45,7 @@ const ERROR_TYPES = { } }; -const PATTERN_TYPE_TO_ERROR_TYPE_MAP = { +export const PATTERN_TYPE_TO_ERROR_TYPE_MAP = { 'page-crash': ERROR_TYPES.PAGE_CRASH, 'uncaught-exception': ERROR_TYPES.UNCAUGHT_EXCEPTION, 'request-failure': ERROR_TYPES.REQUEST_FAILURE, @@ -54,20 +54,8 @@ const PATTERN_TYPE_TO_ERROR_TYPE_MAP = { 'navigation-error': ERROR_TYPES.NAVIGATION_ERROR }; -const IGNORE_ALL_ERRORS_WILDCARD = '*'; +export const IGNORE_ALL_ERRORS_WILDCARD = '*'; -const META_TAG_NAME = 'x-cke-crawler-ignore-patterns'; +export const META_TAG_NAME = 'x-cke-crawler-ignore-patterns'; -const DATA_ATTRIBUTE_NAME = 'data-cke-crawler-skip'; - -export default { - DEFAULT_CONCURRENCY, - DEFAULT_TIMEOUT, - DEFAULT_RESPONSIVENESS_CHECK_TIMEOUT, - DEFAULT_REMAINING_ATTEMPTS, - ERROR_TYPES, - PATTERN_TYPE_TO_ERROR_TYPE_MAP, - IGNORE_ALL_ERRORS_WILDCARD, - META_TAG_NAME, - DATA_ATTRIBUTE_NAME -}; +export const DATA_ATTRIBUTE_NAME = 'data-cke-crawler-skip'; diff --git a/packages/ckeditor5-dev-web-crawler/lib/spinner.js b/packages/ckeditor5-dev-web-crawler/lib/spinner.js index 0ca74bd0b..b10e232c9 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/spinner.js +++ b/packages/ckeditor5-dev-web-crawler/lib/spinner.js @@ -15,7 +15,7 @@ import ora from 'ora'; * @param {Object} options * @param {Boolean} [options.noSpinner=false] Whether to display the spinner with progress or a message with current progress. */ -function createSpinner( { noSpinner } ) { +export function createSpinner( { noSpinner } ) { return ora( { spinner: { frames: [ 'âŖž', 'âŖˇ', 'âŖ¯', 'âŖŸ', 'âĄŋ', 'âĸŋ', 'âŖģ', 'âŖŊ' ] @@ -34,7 +34,7 @@ function createSpinner( { noSpinner } ) { * @param {Boolean} [options.verbose] Whether to display raw log instead of modifying the spinner instance. * @returns {Function} Progress handler. */ -function getProgressHandler( spinner, { verbose } ) { +export function getProgressHandler( spinner, { verbose } ) { let current = 0; return ( { total } ) => { @@ -50,8 +50,3 @@ function getProgressHandler( spinner, { verbose } ) { } }; } - -export default { - createSpinner, - getProgressHandler -}; diff --git a/packages/ckeditor5-dev-web-crawler/lib/utils.js b/packages/ckeditor5-dev-web-crawler/lib/utils.js index 8b78f1720..fad2d161f 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/utils.js +++ b/packages/ckeditor5-dev-web-crawler/lib/utils.js @@ -14,7 +14,7 @@ * @param {String} url Page URL. * @returns {String} Base URL from page URL. */ -function getBaseUrl( url ) { +export function getBaseUrl( url ) { const { origin, pathname } = new URL( url ); return `${ origin }${ pathname }`; @@ -26,7 +26,7 @@ function getBaseUrl( url ) { * @param {String} url The URL to validate. * @returns {Boolean} */ -function isUrlValid( url ) { +export function isUrlValid( url ) { try { return [ 'http:', 'https:' ].includes( new URL( url ).protocol ); } catch ( error ) { @@ -40,12 +40,6 @@ function isUrlValid( url ) { * @param {*} data The value to transform to an array. * @returns {Array.<*>} An array created from data. */ -function toArray( data ) { +export function toArray( data ) { return Array.isArray( data ) ? data : [ data ]; } - -export default { - getBaseUrl, - isUrlValid, - toArray -}; From 020d138b838f9cb8f97f5800db6f31eb30bed39a Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 20 Sep 2024 08:35:46 +0200 Subject: [PATCH 112/172] Stale-bot: Fixed importing a configuration file. --- packages/ckeditor5-dev-stale-bot/bin/stale-bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js index 759f420e1..418001bf9 100755 --- a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js +++ b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js @@ -32,7 +32,7 @@ async function main() { throw new Error( 'Missing or invalid CLI argument: --config-path' ); } - const config = await import( configPath ); + const { default: config } = await import( configPath ); validateConfig( config ); From ac408059b8c4b9673e304f88ec288cfd87a29760 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 20 Sep 2024 08:45:41 +0200 Subject: [PATCH 113/172] Dependency checker: align to ESM changes (run-time check). --- .../lib/checkdependencies.js | 7 ++++--- packages/ckeditor5-dev-dependency-checker/package.json | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js b/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js index 84a136776..6dc5a4c2a 100644 --- a/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js +++ b/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import fs from 'fs'; +import fs from 'fs-extra'; import upath from 'upath'; import { globSync } from 'glob'; import depCheck from 'depcheck'; @@ -52,7 +52,7 @@ async function checkDependenciesInPackage( packagePath, options ) { return true; } - const packageJson = require( packageJsonPath ); + const packageJson = await fs.readJson( packageJsonPath ); const missingCSSFiles = []; const onMissingCSSFile = file => missingCSSFiles.push( file ); @@ -62,6 +62,7 @@ async function checkDependenciesInPackage( packagePath, options ) { parsers: { '**/*.css': filePath => parsePostCSS( filePath, onMissingCSSFile ), '**/*.cjs': depCheck.parser.es6, + '**/*.mjs': depCheck.parser.es6, '**/*.js': depCheck.parser.es6, '**/*.jsx': depCheck.parser.jsx, '**/*.ts': depCheck.parser.typescript, @@ -163,7 +164,7 @@ async function checkDependenciesInPackage( packagePath, options ) { * @returns {Array.} */ function getInvalidItselfImports( repositoryPath ) { - const packageJson = require( upath.join( repositoryPath, 'package.json' ) ); + const packageJson = fs.readJsonSync( upath.join( repositoryPath, 'package.json' ) ); const globPattern = upath.join( repositoryPath, '@(src|tests)/**/*.js' ); const invalidImportsItself = new Set(); diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index 00e9b1f70..969989fb0 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -28,6 +28,7 @@ "@ckeditor/ckeditor5-dev-utils": "^43.0.0", "chalk": "^4.1.0", "depcheck": "^1.3.1", + "fs-extra": "^11.2.0", "glob": "^10.2.5", "minimist": "^1.2.8", "upath": "^2.0.1" From 6c6bffc40baebe8ecdbd4a9c30424997658b6436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Fri, 20 Sep 2024 11:53:26 +0200 Subject: [PATCH 114/172] Temporary changes to enable alpha release from the `epic/3757-esm` branch. --- .circleci/template.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.circleci/template.yml b/.circleci/template.yml index aa1e6488d..3d9356d8c 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -56,7 +56,8 @@ commands: CKE5_GITHUB_ORGANIZATION="ckeditor" CKE5_GITHUB_REPOSITORY="ckeditor5-dev" CKE5_CIRCLE_APPROVAL_JOB_NAME="release_approval" - CKE5_GITHUB_RELEASE_BRANCH="master" + # CKE5_GITHUB_RELEASE_BRANCH="master" + CKE5_GITHUB_RELEASE_BRANCH="epic/3757-esm" echo export CKE5_CIRCLE_APPROVAL_JOB_NAME=$CKE5_CIRCLE_APPROVAL_JOB_NAME >> $BASH_ENV echo export CKE5_GITHUB_RELEASE_BRANCH=$CKE5_GITHUB_RELEASE_BRANCH >> $BASH_ENV @@ -130,7 +131,8 @@ jobs: - bootstrap_repository_command - run: name: Check if packages are ready to be released - command: yarn run release:prepare-packages --verbose --compile-only + # command: yarn run release:prepare-packages --verbose --compile-only + command: yarn run release:prepare-packages --verbose --compile-only --branch=epic/3757-esm trigger_release_process: machine: true @@ -168,11 +170,13 @@ jobs: command: | #!/bin/bash - CKE5_LATEST_COMMIT_HASH=$( git log -n 1 --pretty=format:%H origin/master ) + # CKE5_LATEST_COMMIT_HASH=$( git log -n 1 --pretty=format:%H origin/master ) + CKE5_LATEST_COMMIT_HASH=$( git log -n 1 --pretty=format:%H origin/epic/3757-esm ) CKE5_TRIGGER_COMMIT_HASH=<< pipeline.parameters.triggerCommitHash >> if [[ "${CKE5_LATEST_COMMIT_HASH}" != "${CKE5_TRIGGER_COMMIT_HASH}" ]]; then - echo "There is a newer commit in the repository on the \`#master\` branch. Use its build to start the release." + # echo "There is a newer commit in the repository on the \`#master\` branch. Use its build to start the release." + echo "There is a newer commit in the repository on the \`#epic/3757-esm\` branch. Use its build to start the release." circleci-agent step halt fi - npm_login_command @@ -198,10 +202,12 @@ jobs: command: yarn ckeditor5-dev-ci-circle-disable-auto-cancel-builds - run: name: Prepare the new version to release - command: npm run release:prepare-packages -- --verbose + # command: npm run release:prepare-packages -- --verbose + command: npm run release:prepare-packages -- --verbose --branch=epic/3757-esm - run: name: Publish the packages - command: npm run release:publish-packages -- --verbose + # command: npm run release:publish-packages -- --verbose + command: npm run release:publish-packages -- --verbose --branch=epic/3757-esm - run: name: Enable the redundant workflows option command: yarn ckeditor5-dev-ci-circle-enable-auto-cancel-builds @@ -232,7 +238,8 @@ workflows: filters: branches: only: - - master + # - master + - epic/3757-esm - notify_ci_failure: filters: branches: From 8871512b069f958e1ebc6104b3f415b227ea83d9 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 20 Sep 2024 12:17:04 +0200 Subject: [PATCH 115/172] Docs: Changelog. [skip ci] --- CHANGELOG.md | 82 ++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a4cbe49..6bcc7361f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,38 @@ Changelog ========= +## [44.0.0-alpha.0](https://github.com/ckeditor/ckeditor5-dev/compare/v43.0.0...v44.0.0-alpha.0) (2024-09-20) + +### Other changes + +* **[release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools)**: Convert ckeditor5-dev-release-tools to ESM. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/22d7d9d98de7983aa43f749a5b3697862c2c43c9)) +* **[transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex)**: Converted ckeditor5-dev-transifex to ESM. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/a37d6430b973ead4627fd51b0f4d0a8c24335455)) + +### Released packages + +Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. + +
+Released packages (summary) + +Other releases: + +* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 +
+ + ## [43.0.0](https://github.com/ckeditor/ckeditor5-dev/compare/v42.1.0...v43.0.0) (2024-09-09) ### MAJOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) @@ -179,56 +211,6 @@ Other releases: * [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/42.0.1): v42.0.0 => v42.0.1 - -## [42.0.0](https://github.com/ckeditor/ckeditor5-dev/compare/v41.0.0...v42.0.0) (2024-07-29) - -We are excited to announce a new major release of the `@ckeditor/ckeditor5-dev-*` packages. - -### Release highlights - -This release brings the updated configuration for the build tools. As it might produce output incompatible with the previous settings, this release is marked as a major bump. - -The [`@ckeditor/ckeditor5-dev-build-tools`](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools) package now supports a new `globals` option, which allows passing pairs of external package names and associated global variables used in the `umd` build. - -Additionally, the global names for the `ckeditor5` and `ckeditor5-premium-features` packages in the UMD builds have been changed to `CKEDITOR` and `CKEDITOR_PREMIUM_FEATURES` respectively. - -### MAJOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) - -* **[build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools)**: The global names for the `ckeditor5` and `ckeditor5-premium-features` packages in the UMD builds have been changed to `CKEDITOR` and `CKEDITOR_PREMIUM_FEATURES` respectively. - -### MINOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) - -* **[build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools)**: Ability to pass `globals` parameter if necessary for external imports in `umd` bundles. - -### Bug fixes - -* **[build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools)**: Ability to pass `globals` parameter if necessary for external imports in `umd` bundles. See https://github.com/ckeditor/ckeditor5/issues/16798. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/74f4571f186a2cbb30a8d3fcb62475c89f59c641)) - -### Released packages - -Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. - -
-Released packages (summary) - -Other releases: - -* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/jsdoc-plugins](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins/v/42.0.0): v41.0.0 => v42.0.0 -* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/42.0.0): v41.0.0 => v42.0.0 -
- --- To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases). From e7181e72a16dee9dcb3906ef7ae539fb4bf97e00 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 20 Sep 2024 12:21:48 +0200 Subject: [PATCH 116/172] Changelog for v44.0.0-alpha. --- CHANGELOG.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bcc7361f..921e70796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,32 @@ Changelog ## [44.0.0-alpha.0](https://github.com/ckeditor/ckeditor5-dev/compare/v43.0.0...v44.0.0-alpha.0) (2024-09-20) +### MAJOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) + +* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The `builds.getDllPluginWebpackConfig()` function is now asynchronous now. +* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The following functions from the `bundler` object are no longer available: `createEntryFile()`, `getEditorConfig()`, `getPlugins()`. +* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The following functions from the `stream` object are no longer available: `isTestFile()`, `isSourceFile()`, `isJSFile()`. +* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The `styles.themeLogger()` function is no longer exposed publicly. +* **[tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests)**: The IntelliJ Karma configuration path needs to be updated. Now, it ends with the `cjs` suffix instead of `js`. +* **[docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs)**: The package uses only TypeDoc to build the documentation. The `build()` function no longer supports `type` property in the configuration. +* The following packages have been converted to ESM. To load them, use an `import` statement instead of `require()`. + * [`@ckeditor/ckeditor5-dev-build-tools`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-build-tools) + * [`@ckeditor/ckeditor5-dev-bump-year`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-bump-year) + * [`@ckeditor/ckeditor5-dev-ci`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-ci) + * [`@ckeditor/ckeditor5-dev-dependency-checker`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-dependency-checker) + * [`@ckeditor/ckeditor5-dev-docs`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-docs) + * [`@ckeditor/ckeditor5-dev-release-tools`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-release-tools) + * [`@ckeditor/ckeditor5-dev-stale-bot`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-stale-bot) + * [`@ckeditor/ckeditor5-dev-tests`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-tests) + * [`@ckeditor/ckeditor5-dev-transifex`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-transifex) + * [`@ckeditor/ckeditor5-dev-translations`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-translations) + * [`@ckeditor/ckeditor5-dev-utils`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-utils) + * [`@ckeditor/ckeditor5-dev-web-crawler`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-web-crawler) + ### Other changes -* **[release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools)**: Convert ckeditor5-dev-release-tools to ESM. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/22d7d9d98de7983aa43f749a5b3697862c2c43c9)) -* **[transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex)**: Converted ckeditor5-dev-transifex to ESM. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/a37d6430b973ead4627fd51b0f4d0a8c24335455)) +* The CKEditor 5 Dev packages are now ESM. +* The `@ckeditor/jsdoc-plugins` package is no longer available as CKEditor 5 documentation uses TypeScript sources to prepare API docs. The package is no longer use by us anywhere. Hence, we decided to remove a dead code. ### Released packages From f89ab93c15cce45d055883dc3addff9ef47f9347 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 20 Sep 2024 12:35:26 +0200 Subject: [PATCH 117/172] Added missing method parameter to fetch calls. --- packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js | 7 ++++++- .../lib/circle-update-auto-cancel-builds.js | 2 +- packages/ckeditor5-dev-ci/lib/trigger-circle-build.js | 2 +- packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js | 2 +- .../tests/circle-update-auto-cancel-builds.js | 2 +- packages/ckeditor5-dev-ci/tests/trigger-circle-build.js | 8 ++++---- packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js | 4 ++-- packages/ckeditor5-dev-transifex/lib/transifexservice.js | 1 + .../ckeditor5-dev-transifex/tests/transifexservice.js | 3 +++ 9 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js index 9454dcf0d..fc73e8e39 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js +++ b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js @@ -104,7 +104,12 @@ async function waitForOtherJobsAndSendNotification() { */ async function getOtherJobsData() { const url = `https://circleci.com/api/v2/workflow/${ CIRCLE_WORKFLOW_ID }/job`; - const options = { headers: { 'Circle-Token': CKE5_CIRCLE_TOKEN } }; + const options = { + method: 'GET', + headers: { + 'Circle-Token': CKE5_CIRCLE_TOKEN + } + }; const response = await fetch( url, options ); const data = await response.json(); diff --git a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js index 6528ebf4e..cde1d1501 100644 --- a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js @@ -20,7 +20,7 @@ export default async function circleUpdateAutoCancelBuilds( options ) { } = options; const circleRequestOptions = { - method: 'patch', + method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Circle-Token': circleToken diff --git a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js index 8e2810864..76c368799 100644 --- a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js @@ -40,7 +40,7 @@ export default async function triggerCircleBuild( options ) { } const requestOptions = { - method: 'post', + method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', diff --git a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js index 3eb0fa713..daed11455 100644 --- a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js @@ -13,7 +13,7 @@ */ export default async function getJobApprover( circleCiToken, workflowId, jobName ) { const circleRequestOptions = { - method: 'get', + method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', diff --git a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js index 0850440ee..05d988ca0 100644 --- a/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/tests/circle-update-auto-cancel-builds.js @@ -28,7 +28,7 @@ describe( 'lib/circleUpdateAutoCancelBuilds', () => { expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-foo/settings', { - method: 'patch', + method: 'PATCH', headers: { 'Circle-Token': 'circle-token', 'Content-Type': 'application/json' diff --git a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js index 4cb85bc3f..4f973c5ab 100644 --- a/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/tests/trigger-circle-build.js @@ -32,7 +32,7 @@ describe( 'lib/triggerCircleBuild', () => { expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { - method: 'post', + method: 'POST', headers: { Accept: 'application/json', 'Circle-Token': 'circle-token', @@ -67,7 +67,7 @@ describe( 'lib/triggerCircleBuild', () => { expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { - method: 'post', + method: 'POST', headers: { Accept: 'application/json', 'Circle-Token': 'circle-token', @@ -103,7 +103,7 @@ describe( 'lib/triggerCircleBuild', () => { expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { - method: 'post', + method: 'POST', headers: { Accept: 'application/json', 'Circle-Token': 'circle-token', @@ -139,7 +139,7 @@ describe( 'lib/triggerCircleBuild', () => { expect( vi.mocked( fetchMock ) ).toHaveBeenCalledWith( 'https://circleci.com/api/v2/project/github/ckeditor/ckeditor5-dev/pipeline', { - method: 'post', + method: 'POST', headers: { Accept: 'application/json', 'Circle-Token': 'circle-token', diff --git a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js index 4267e0968..bc7c1fbbf 100644 --- a/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/tests/utils/get-job-approver.js @@ -34,7 +34,7 @@ describe( 'lib/utils/getJobApprover', () => { expect( vi.mocked( fetchMock ) ).toHaveBeenNthCalledWith( 1, 'https://circleci.com/api/v2/workflow/abc-123-abc-456/job', { - 'method': 'get', + 'method': 'GET', 'headers': expect.objectContaining( { 'Circle-Token': 'circle-token' } ) @@ -43,7 +43,7 @@ describe( 'lib/utils/getJobApprover', () => { expect( vi.mocked( fetchMock ) ).toHaveBeenNthCalledWith( 2, 'https://circleci.com/api/v2/user/foo-unique-id', { - 'method': 'get', + 'method': 'GET', 'headers': expect.objectContaining( { 'Circle-Token': 'circle-token' } ) diff --git a/packages/ckeditor5-dev-transifex/lib/transifexservice.js b/packages/ckeditor5-dev-transifex/lib/transifexservice.js index 0450c3a8e..d16e67ebb 100644 --- a/packages/ckeditor5-dev-transifex/lib/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/lib/transifexservice.js @@ -338,6 +338,7 @@ async function downloadFile( downloadRequest, numberOfAttempts = 1 ) { const { url, resourceName, languageCode } = downloadRequest; const response = await fetch( url, { + method: 'GET', headers: { ...transifexApi.auth() } diff --git a/packages/ckeditor5-dev-transifex/tests/transifexservice.js b/packages/ckeditor5-dev-transifex/tests/transifexservice.js index 5436c6182..552ddcfad 100644 --- a/packages/ckeditor5-dev-transifex/tests/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/tests/transifexservice.js @@ -288,18 +288,21 @@ describe( 'dev-transifex/transifex-service', () => { expect( fetchMock ).toHaveBeenCalledTimes( 3 ); expect( fetchMock ).toHaveBeenNthCalledWith( 1, 'https://example.com/ckeditor5-core/en', { + method: 'GET', headers: { Authorization: 'Bearer secretToken' } } ); expect( fetchMock ).toHaveBeenNthCalledWith( 2, 'https://example.com/ckeditor5-core/pl', { + method: 'GET', headers: { Authorization: 'Bearer secretToken' } } ); expect( fetchMock ).toHaveBeenNthCalledWith( 3, 'https://example.com/ckeditor5-core/de', { + method: 'GET', headers: { Authorization: 'Bearer secretToken' } From 34396ba8e3e6b3f1c31efa757ffa0862fbeaaaee Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Fri, 20 Sep 2024 12:46:19 +0200 Subject: [PATCH 118/172] Release tools: fixed npm publish callback. --- .../lib/utils/publishpackageonnpmcallback.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js index d4eb4fe6f..2bbb13cfe 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js @@ -3,10 +3,6 @@ * For licensing, see LICENSE.md. */ -import { tools } from '@ckeditor/ckeditor5-dev-utils'; -import upath from 'upath'; -import fs from 'fs-extra'; - /** * Calls the npm command to publish the package. When a package is successfully published, it is removed from the filesystem. * @@ -16,13 +12,19 @@ import fs from 'fs-extra'; * @returns {Promise} */ export default async function publishPackageOnNpmCallback( packagePath, taskOptions ) { - const result = await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, { + const { tools } = await import( '@ckeditor/ckeditor5-dev-utils' ); + const { default: fs } = await import( 'fs-extra' ); + const { default: path } = await import( 'upath' ); + + const options = { cwd: packagePath, async: true, verbosity: 'error' - } ) + }; + + const result = await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, options ) .catch( e => { - const packageName = upath.basename( packagePath ); + const packageName = path.basename( packagePath ); if ( e.toString().includes( 'code E409' ) ) { return { shouldKeepDirectory: true }; From 84a3759da9098ecfbd7c8ea03192acae8bf30354 Mon Sep 17 00:00:00 2001 From: CKEditorBot Date: Fri, 20 Sep 2024 10:52:37 +0000 Subject: [PATCH 119/172] Release: v'44.0.0-alpha.0'. --- package.json | 8 ++++---- packages/ckeditor5-dev-build-tools/package.json | 2 +- packages/ckeditor5-dev-bump-year/package.json | 2 +- packages/ckeditor5-dev-ci/package.json | 2 +- packages/ckeditor5-dev-dependency-checker/package.json | 4 ++-- packages/ckeditor5-dev-docs/package.json | 6 +++--- packages/ckeditor5-dev-release-tools/package.json | 4 ++-- packages/ckeditor5-dev-stale-bot/package.json | 4 ++-- packages/ckeditor5-dev-tests/package.json | 6 +++--- packages/ckeditor5-dev-transifex/package.json | 6 +++--- packages/ckeditor5-dev-translations/package.json | 2 +- packages/ckeditor5-dev-utils/package.json | 4 ++-- packages/ckeditor5-dev-web-crawler/package.json | 2 +- packages/typedoc-plugins/package.json | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index dceda2c5c..d4e028b5f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ckeditor5-dev", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "private": true, "author": "CKSource (http://cksource.com/)", "license": "GPL-2.0-or-later", @@ -15,9 +15,9 @@ }, "type": "module", "devDependencies": { - "@ckeditor/ckeditor5-dev-ci": "^43.0.0", - "@ckeditor/ckeditor5-dev-release-tools": "^43.0.0", - "@ckeditor/ckeditor5-dev-bump-year": "^43.0.0", + "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.0", "coveralls": "^3.1.1", "eslint": "^7.0.0", "eslint-config-ckeditor5": "^6.0.0", diff --git a/packages/ckeditor5-dev-build-tools/package.json b/packages/ckeditor5-dev-build-tools/package.json index 27e3cdf49..e56d508a5 100644 --- a/packages/ckeditor5-dev-build-tools/package.json +++ b/packages/ckeditor5-dev-build-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-build-tools", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Rollup-based tools used to build CKEditor 5 packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index f795a5dd3..d267b5bc3 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-bump-year", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Used to bump year in the licence text specified at the top of the file.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 1d03e4f25..8253a9607 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-ci", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Utils used on various Continuous Integration services.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index 969989fb0..c659926ee 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-dependency-checker", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Contains tools for validating dependencies specified in package.json.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-dependency-checker": "bin/dependencychecker.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^43.0.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", "chalk": "^4.1.0", "depcheck": "^1.3.1", "fs-extra": "^11.2.0", diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index bd214341d..236172be2 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-docs", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Tasks used to build and verify the documentation for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,8 +22,8 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^43.0.0", - "@ckeditor/typedoc-plugins": "^43.0.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", + "@ckeditor/typedoc-plugins": "^44.0.0-alpha.0", "fast-glob": "^3.2.4", "fs-extra": "^11.2.0", "tmp": "^0.2.1", diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index cce4e611c..0df96d8bb 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-release-tools", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Tools used for releasing CKEditor 5 and related packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^43.0.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", "@octokit/rest": "^19.0.0", "chalk": "^4.0.0", "cli-columns": "^4.0.0", diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index 0a5e70fa8..c339f3305 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-stale-bot", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "A stale bot is used to mark issues and pull requests that have not recently been updated.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-stale-bot": "bin/stale-bot.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^43.0.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", "chalk": "^4.1.0", "date-fns": "^2.30.0", "fs-extra": "^11.2.0", diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 9bc232159..ef1121dc8 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-tests", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Testing environment for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -28,8 +28,8 @@ }, "dependencies": { "@babel/core": "^7.10.5", - "@ckeditor/ckeditor5-dev-translations": "^43.0.0", - "@ckeditor/ckeditor5-dev-utils": "^43.0.0", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", "@ckeditor/ckeditor5-inspector": "^4.0.0", "@types/chai": "^4.3.5", "@types/karma-sinon-chai": "^2.0.2", diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index 859139347..a4c39e763 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-transifex", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Used to download and upload translations using the Transifex service.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -24,8 +24,8 @@ "dependencies": { "fs-extra": "^11.2.0", "del": "^5.1.0", - "@ckeditor/ckeditor5-dev-utils": "^43.0.0", - "@ckeditor/ckeditor5-dev-translations": "^43.0.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", "chalk": "^4.0.0", "inquirer": "^7.1.0", "@transifex/api": "^4.2.1", diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index c4cc85f0f..8803e9306 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-translations", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "CKEditor 5 translations plugin for webpack.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index bf602182b..57e07f697 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-utils", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Utils for CKEditor 5 development tools packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-translations": "^43.0.0", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", "chalk": "^3.0.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.6.1", diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index a6f395c8b..04e201fe1 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-web-crawler", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Used to run a web crawler that checks for errors on specified pages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index 1056890f0..763885174 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/typedoc-plugins", - "version": "43.0.0", + "version": "44.0.0-alpha.0", "description": "Various TypeDoc plugins developed by the CKEditor 5 team.", "keywords": [], "author": "CKSource (http://cksource.com/)", From 980c0ed18f24662df676c5716373cabf068a1d8a Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 08:23:55 +0200 Subject: [PATCH 120/172] ESLint@8. --- .eslintrc.cjs | 4 ++++ package.json | 4 ++-- packages/ckeditor5-dev-build-tools/src/build.ts | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 515231078..7d1bb09a5 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -7,6 +7,10 @@ module.exports = { extends: 'ckeditor5', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module' + }, env: { node: true }, diff --git a/package.json b/package.json index d4e028b5f..5e01b0379 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.0", "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.0", "coveralls": "^3.1.1", - "eslint": "^7.0.0", - "eslint-config-ckeditor5": "^6.0.0", + "eslint": "^8.21.0", + "eslint-config-ckeditor5": "^7.0.0", "fs-extra": "^11.2.0", "glob": "^10.2.5", "husky": "^8.0.2", diff --git a/packages/ckeditor5-dev-build-tools/src/build.ts b/packages/ckeditor5-dev-build-tools/src/build.ts index 2814c84e0..e9303e406 100644 --- a/packages/ckeditor5-dev-build-tools/src/build.ts +++ b/packages/ckeditor5-dev-build-tools/src/build.ts @@ -104,6 +104,7 @@ async function generateUmdBuild( args: BuildOptions, bundle: RollupOutput ): Pro args.input = args.output; const { dir, name } = path.parse( args.output ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { plugins, ...config } = await getRollupConfig( args ); /** From 543af023582433d49e5039d19df0b216d1fde52d Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 09:46:52 +0200 Subject: [PATCH 121/172] ckeditor5-dev-release-tools: use the latest compatible version of dependencies. --- .../lib/utils/getpackagespaths.js | 2 +- .../lib/utils/transformcommitfactory.js | 4 +-- .../ckeditor5-dev-release-tools/package.json | 28 +++++++++---------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js index 2530f55df..3f41ed1a8 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js @@ -4,7 +4,7 @@ */ import path from 'path'; -import minimatch from 'minimatch'; +import { minimatch } from 'minimatch'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; import getPackageJson from './getpackagejson.js'; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js index 075503ebb..f8b91ce96 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js @@ -3,12 +3,10 @@ * For licensing, see LICENSE.md. */ -import lodash from 'lodash'; +import { cloneDeepWith } from 'lodash-es'; import * as utils from './transformcommitutils.js'; import getChangedFilesForCommit from './getchangedfilesforcommit.js'; -const { cloneDeepWith } = lodash; - // Squash commit follows the pattern: "A pull request title (#{number})". const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 0df96d8bb..9dd9cac26 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -23,22 +23,22 @@ ], "dependencies": { "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", - "@octokit/rest": "^19.0.0", - "chalk": "^4.0.0", + "@octokit/rest": "^21.0.0", + "chalk": "^5.0.0", "cli-columns": "^4.0.0", "compare-func": "^2.0.0", "concat-stream": "^2.0.0", - "conventional-changelog-writer": "^6.0.0", - "conventional-commits-filter": "^3.0.0", - "conventional-commits-parser": "^4.0.0", - "date-fns": "^2.30.0", - "fs-extra": "^11.2.0", - "git-raw-commits": "^3.0.0", - "glob": "^10.2.5", - "inquirer": "^7.1.0", - "lodash": "^4.17.15", - "minimatch": "^3.0.4", - "semver": "^7.5.3", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "date-fns": "^4.0.0", + "fs-extra": "^11.0.0", + "git-raw-commits": "^5.0.0", + "glob": "^10.0.0", + "inquirer": "^11.0.0", + "lodash-es": "^4.17.21", + "minimatch": "^9.0.0", + "semver": "^7.6.3", "shell-escape": "^0.2.0", "upath": "^2.0.1" }, @@ -46,7 +46,7 @@ "jest-extended": "^4.0.2", "vitest": "^2.0.5", "handlebars": "^4.7.6", - "mock-fs": "^5.1.2" + "mock-fs": "^5.2.0" }, "scripts": { "test": "vitest run --config vitest.config.js", From fb1d106dff24caf084055b6f1c0375913cb16dec Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 09:53:52 +0200 Subject: [PATCH 122/172] ckeditor5-dev-build-tools: unified dependencies versioning across the repository. --- packages/ckeditor5-dev-build-tools/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ckeditor5-dev-build-tools/package.json b/packages/ckeditor5-dev-build-tools/package.json index e56d508a5..87bbc1219 100644 --- a/packages/ckeditor5-dev-build-tools/package.json +++ b/packages/ckeditor5-dev-build-tools/package.json @@ -35,17 +35,17 @@ "@rollup/plugin-typescript": "^11.1.6", "@rollup/pluginutils": "^5.1.0", "@swc/core": "^1.4.8", - "chalk": "^5.3.0", - "cssnano": "^7.0.4", + "chalk": "^5.0.0", + "cssnano": "^7.0.0", "cssnano-preset-lite": "^4.0.1", "estree-walker": "^3.0.3", - "glob": "^10.3.10", + "glob": "^10.0.0", "lodash-es": "^4.17.21", "magic-string": "^0.30.6", "pofile": "^1.1.4", "postcss": "^8.0.0", - "postcss-mixins": "^9.0.4", - "postcss-nesting": "^12.0.2", + "postcss-mixins": "^11.0.0", + "postcss-nesting": "^13.0.0", "purgecss": "^6.0.0", "rollup": "^4.9.5", "rollup-plugin-styles": "^4.0.0", From 457eed5c60aff331650cb1ce2dd3de02d0389845 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 09:54:24 +0200 Subject: [PATCH 123/172] ckeditor5-dev-bump-year: unified dependencies versioning across the repository. --- packages/ckeditor5-dev-bump-year/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index d267b5bc3..b5ad92091 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "chalk": "^4.1.0", - "glob": "^10.2.5" + "chalk": "^5.0.0", + "glob": "^10.0.0" } } From 0b365110fd38ead421c97bfc13a779c39108ed7c Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 09:55:42 +0200 Subject: [PATCH 124/172] ckeditor5-dev-ci: unified dependencies versioning across the repository. --- packages/ckeditor5-dev-ci/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 8253a9607..992ae3cc3 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -34,7 +34,7 @@ "ckeditor5-dev-ci-circle-enable-auto-cancel-builds": "bin/circle-enable-auto-cancel-builds.js" }, "dependencies": { - "@octokit/rest": "^19.0.0", + "@octokit/rest": "^21.0.0", "minimist": "^1.2.8", "slack-notify": "^2.0.6" }, From bac140258b4cf8dcb72d73fbc9cca2df267f945e Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 09:56:13 +0200 Subject: [PATCH 125/172] ckeditor5-dev-dependency-checker: unified dependencies versioning across the repository. --- packages/ckeditor5-dev-dependency-checker/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index c659926ee..175bc3e84 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -26,10 +26,10 @@ }, "dependencies": { "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", - "chalk": "^4.1.0", + "chalk": "^5.0.0", "depcheck": "^1.3.1", - "fs-extra": "^11.2.0", - "glob": "^10.2.5", + "fs-extra": "^11.0.0", + "glob": "^10.0.0", "minimist": "^1.2.8", "upath": "^2.0.1" } From 986473baa12100dfcb9fee31789154f1f389dec0 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 09:58:22 +0200 Subject: [PATCH 126/172] ckeditor5-dev-docs: use the latest compatible version of dependencies. --- packages/ckeditor5-dev-docs/lib/build.js | 2 +- packages/ckeditor5-dev-docs/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index b920fe6e1..0254464f9 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import glob from 'fast-glob'; +import { glob } from 'glob'; import TypeDoc from 'typedoc'; import typedocPlugins from '@ckeditor/typedoc-plugins'; diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index 236172be2..fc8d79faa 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -24,8 +24,8 @@ "dependencies": { "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", "@ckeditor/typedoc-plugins": "^44.0.0-alpha.0", - "fast-glob": "^3.2.4", - "fs-extra": "^11.2.0", + "glob": "^10.0.0", + "fs-extra": "^11.0.0", "tmp": "^0.2.1", "typedoc": "^0.23.15", "typedoc-plugin-rename-defaults": "0.6.6" From 2bb12d3068032bee54c58852e1f3a0ec783d0939 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:02:08 +0200 Subject: [PATCH 127/172] ckeditor5-dev-stale-bot: use the latest compatible version of dependencies. --- packages/ckeditor5-dev-stale-bot/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index c339f3305..f56ed340b 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -26,13 +26,13 @@ }, "dependencies": { "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", - "chalk": "^4.1.0", - "date-fns": "^2.30.0", - "fs-extra": "^11.2.0", + "chalk": "^5.0.0", + "date-fns": "^4.0.0", + "fs-extra": "^11.0.0", "graphql": "^16.8.1", - "graphql-request": "^6.1.0", + "graphql-request": "^7.0.0", "minimist": "^1.2.8", - "ora": "^5.2.0", + "ora": "^8.0.0", "upath": "^2.0.1" }, "devDependencies": { From 8bb85424407ef8e09156d1c8d15ace6404b2f2b2 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:29:08 +0200 Subject: [PATCH 128/172] ckeditor5-dev-tests: use the latest compatible version of dependencies. --- .../lib/tasks/runautomatedtests.js | 4 +- .../utils/manual-tests/compilehtmlfiles.js | 6 +-- .../lib/utils/manual-tests/removedir.js | 4 +- packages/ckeditor5-dev-tests/package.json | 42 +++++++++---------- .../tests/tasks/runautomatedtests.js | 2 +- .../tests/utils/manual-tests/removedir.js | 8 ++-- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js b/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js index a60467a7e..a51534de3 100644 --- a/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js +++ b/packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js @@ -10,8 +10,8 @@ import { logger } from '@ckeditor/ckeditor5-dev-utils'; import getKarmaConfig from '../utils/automated-tests/getkarmaconfig.js'; import chalk from 'chalk'; import { globSync } from 'glob'; -import minimatch from 'minimatch'; -import mkdirp from 'mkdirp'; +import { minimatch } from 'minimatch'; +import { mkdirp } from 'mkdirp'; import karmaLogger from 'karma/lib/logger.js'; import karma from 'karma'; import transformFileOptionToTestGlob from '../utils/transformfileoptiontotestglob.js'; diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js index ee1eb102b..cad036e20 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js @@ -6,7 +6,7 @@ import path from 'path'; import fs from 'fs-extra'; import { globSync } from 'glob'; -import _ from 'lodash'; +import { uniq, debounce } from 'lodash-es'; import chalk from 'chalk'; import * as commonmark from 'commonmark'; import combine from 'dom-combiner'; @@ -40,7 +40,7 @@ export default function compileHtmlFiles( options ) { const sourceMDFiles = options.sourceFiles.map( jsFile => setExtension( jsFile, 'md' ) ); const sourceHtmlFiles = sourceMDFiles.map( mdFile => setExtension( mdFile, 'html' ) ); - const sourceDirs = _.uniq( sourceMDFiles.map( file => path.dirname( file ) ) ); + const sourceDirs = uniq( sourceMDFiles.map( file => path.dirname( file ) ) ); const sourceFilePathBases = sourceMDFiles.map( mdFile => getFilePathWithoutExtension( mdFile ) ); const staticFiles = sourceDirs @@ -183,7 +183,7 @@ function getFilePathWithoutExtension( file ) { function watchFiles( filePaths, onChange, onTestCompilationStatus ) { for ( const filePath of filePaths ) { - const debouncedOnChange = _.debounce( () => { + const debouncedOnChange = debounce( () => { onChange( filePath ); onTestCompilationStatus( 'finished' ); }, 500 ); diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js index 8189e524c..8bce9e107 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import del from 'del'; +import { deleteAsync } from 'del'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; import chalk from 'chalk'; @@ -18,7 +18,7 @@ import chalk from 'chalk'; * @returns {Promise} */ export default function removeDir( dir, options = {} ) { - return del( dir ).then( () => { + return deleteAsync( dir ).then( () => { if ( !options.silent ) { logger().info( `Removed directory '${ chalk.cyan( dir ) }'` ); } diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index ef1121dc8..c1f52ae94 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -35,43 +35,43 @@ "@types/karma-sinon-chai": "^2.0.2", "@types/mocha": "^10.0.1", "@types/sinon": "^10.0.15", - "assertion-error": "^1.1.0", - "babel-plugin-istanbul": "^6.1.0", + "assertion-error": "^2.0.0", + "babel-plugin-istanbul": "^7.0.0", "buffer": "^6.0.3", - "chai": "^4.2.0", - "chalk": "^4.0.0", - "chokidar": "^3.4.0", + "chai": "^5.0.0", + "chalk": "^5.0.0", + "chokidar": "^4.0.0", "commonmark": "^0.29.1", - "del": "^5.1.0", + "del": "^7.0.0", "dom-combiner": "^0.1.3", - "fs-extra": "^11.2.0", - "glob": "^10.2.5", - "inquirer": "^7.1.0", - "is-interactive": "^1.0.0", - "is-wsl": "^2.2.0", + "fs-extra": "^11.0.0", + "glob": "^10.0.0", + "inquirer": "^11.0.0", + "is-interactive": "^2.0.0", + "is-wsl": "^3.0.0", "js-beautify": "^1.11.0", - "karma": "^6.3.17", + "karma": "^6.4.4", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^3.1.0", "karma-coverage": "^2.0.3", - "karma-firefox-launcher": "^1.3.0", + "karma-firefox-launcher": "^2.0.0", "karma-mocha": "^2.0.1", "karma-mocha-reporter": "^2.2.5", "karma-sinon": "^1.0.5", "karma-sinon-chai": "^2.0.2", - "karma-sourcemap-loader": "^0.3.8", + "karma-sourcemap-loader": "^0.4.0", "karma-webpack": "^5.0.0", - "lodash": "^4.17.15", - "minimatch": "^3.0.4", + "lodash-es": "^4.17.21", + "minimatch": "^9.0.0", "minimist": "^1.2.8", - "mkdirp": "^1.0.4", - "mocha": "^7.1.2", + "mkdirp": "^3.0.0", + "mocha": "^10.0.0", "node-notifier": "^10.0.1", "process": "^0.11.10", - "sinon": "^9.2.4", - "sinon-chai": "^3.5.0", + "sinon": "^19.0.0", + "sinon-chai": "^4.0.0", "socket.io": "^4.0.0", - "typescript": "^4.6.4", + "typescript": "5.0.4", "webpack": "^5.94.0" }, "devDependencies": { diff --git a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js index 7cc0c9288..86e39f4b8 100644 --- a/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js +++ b/packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js @@ -7,7 +7,7 @@ import path from 'path'; import fs from 'fs'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { globSync } from 'glob'; -import mkdirp from 'mkdirp'; +import { mkdirp } from 'mkdirp'; import chalk from 'chalk'; import karma from 'karma'; import karmaLogger from 'karma/lib/logger.js'; diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js index 86758d0d3..dd07059f8 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/removedir.js @@ -4,7 +4,7 @@ */ import { beforeEach, describe, expect, it, vi } from 'vitest'; -import del from 'del'; +import { deleteAsync } from 'del'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; import chalk from 'chalk'; import removeDir from '../../../lib/utils/manual-tests/removedir.js'; @@ -22,7 +22,7 @@ describe( 'removeDir()', () => { beforeEach( () => { logInfo = vi.fn(); - vi.mocked( del ).mockResolvedValue(); + vi.mocked( deleteAsync ).mockResolvedValue(); vi.mocked( chalk ).cyan.mockImplementation( input => input ); vi.mocked( logger ).mockReturnValue( { info: logInfo @@ -33,14 +33,14 @@ describe( 'removeDir()', () => { await removeDir( 'workspace/directory' ); expect( vi.mocked( chalk ).cyan ).toHaveBeenCalledOnce(); - expect( vi.mocked( del ) ).toHaveBeenCalledExactlyOnceWith( 'workspace/directory' ); + expect( vi.mocked( deleteAsync ) ).toHaveBeenCalledExactlyOnceWith( 'workspace/directory' ); expect( logInfo ).toHaveBeenCalledExactlyOnceWith( 'Removed directory \'workspace/directory\'' ); } ); it( 'should remove directory and does not inform about it', async () => { await removeDir( 'workspace/directory', { silent: true } ); - expect( vi.mocked( del ) ).toHaveBeenCalledExactlyOnceWith( 'workspace/directory' ); + expect( vi.mocked( deleteAsync ) ).toHaveBeenCalledExactlyOnceWith( 'workspace/directory' ); expect( vi.mocked( chalk ).cyan ).not.toHaveBeenCalled(); expect( logInfo ).not.toHaveBeenCalled(); From d2e6d4e799ec53142a254fe7e55d8d4145e76cd0 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:32:13 +0200 Subject: [PATCH 129/172] ckeditor5-dev-transifex: use the latest compatible version of dependencies. --- packages/ckeditor5-dev-transifex/lib/createpotfiles.js | 4 ++-- packages/ckeditor5-dev-transifex/package.json | 10 +++++----- .../ckeditor5-dev-transifex/tests/createpotfiles.js | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js index fdd009e61..16fd8a9eb 100644 --- a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js @@ -5,7 +5,7 @@ import path from 'path'; import fs from 'fs-extra'; -import del from 'del'; +import { deleteAsync } from 'del'; import { logger as utilsLogger } from '@ckeditor/ckeditor5-dev-utils'; import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; import { verifyProperties } from './utils.js'; @@ -229,7 +229,7 @@ function assertNoRepeatedContext( { packageContexts } ) { } function removeExistingPotFiles( translationsDirectory ) { - del.sync( translationsDirectory ); + deleteAsync.sync( translationsDirectory ); } /** diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index a4c39e763..540b7d3f2 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -22,13 +22,13 @@ "lib" ], "dependencies": { - "fs-extra": "^11.2.0", - "del": "^5.1.0", + "fs-extra": "^11.0.0", + "del": "^7.0.0", "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", - "chalk": "^4.0.0", - "inquirer": "^7.1.0", - "@transifex/api": "^4.2.1", + "chalk": "^5.0.0", + "inquirer": "^11.0.0", + "@transifex/api": "^7.0.0", "cli-table": "^0.3.1" }, "devDependencies": { diff --git a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js index 9441de44c..87ca1df8f 100644 --- a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js @@ -8,7 +8,7 @@ import createPotFiles from '../lib/createpotfiles.js'; import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; import { verifyProperties } from '../lib/utils.js'; -import del from 'del'; +import { deleteAsync } from 'del'; import fs from 'fs-extra'; import path from 'path'; @@ -53,8 +53,8 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( del.sync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( del.sync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex' ); + expect( vi.mocked( deleteAsync.sync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( deleteAsync.sync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex' ); } ); it( 'should create a POT file entry for one message with a corresponding context', () => { From 1f15d812e312c22ca480272c2d4205d8ab1aa90a Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:34:57 +0200 Subject: [PATCH 130/172] ckeditor5-dev-translations: use the latest compatible version of dependencies. --- .../ckeditor5-dev-translations/lib/servetranslations.js | 2 +- packages/ckeditor5-dev-translations/package.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ckeditor5-dev-translations/lib/servetranslations.js b/packages/ckeditor5-dev-translations/lib/servetranslations.js index 1e058140e..57aef6946 100644 --- a/packages/ckeditor5-dev-translations/lib/servetranslations.js +++ b/packages/ckeditor5-dev-translations/lib/servetranslations.js @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import path from 'path'; import { fileURLToPath } from 'url'; import chalk from 'chalk'; -import rimraf from 'rimraf'; +import { rimraf } from 'rimraf'; import webpackSources from 'webpack-sources'; const __filename = fileURLToPath( import.meta.url ); diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index 8803e9306..ef751ead3 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -24,10 +24,10 @@ "dependencies": { "@babel/parser": "^7.18.9", "@babel/traverse": "^7.18.9", - "chalk": "^4.0.0", - "fs-extra": "^11.2.0", - "rimraf": "^3.0.2", - "webpack-sources": "^2.0.1", + "chalk": "^5.0.0", + "fs-extra": "^11.0.0", + "rimraf": "^5.0.0", + "webpack-sources": "^3.0.0", "pofile": "^1.0.9" }, "devDependencies": { From 04582a6b6b20a561d22e4c423b11e08bc50b2b5a Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:50:57 +0200 Subject: [PATCH 131/172] ckeditor5-dev-utils: use the latest compatible version of dependencies. --- packages/ckeditor5-dev-utils/package.json | 37 +++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index 57e07f697..9daf9d7d8 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -23,33 +23,30 @@ ], "dependencies": { "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.6.1", - "css-loader": "^5.2.7", - "cssnano": "^6.0.3", - "del": "^5.0.0", - "esbuild-loader": "~3.0.1", - "fs-extra": "^11.2.0", - "is-interactive": "^1.0.0", - "javascript-stringify": "^1.6.0", + "chalk": "^5.0.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^3.0.0", + "css-loader": "^7.0.0", + "cssnano": "^7.0.0", + "esbuild-loader": "^4.0.0", + "fs-extra": "^11.0.0", + "is-interactive": "^2.0.0", "mini-css-extract-plugin": "^2.4.2", - "mocha": "^7.1.2", + "mocha": "^10.0.0", "postcss": "^8.4.12", - "postcss-import": "^14.1.0", - "postcss-loader": "^4.3.0", - "postcss-mixins": "^9.0.2", - "postcss-nesting": "^10.1.4", + "postcss-import": "^16.0.0", + "postcss-loader": "^8.0.0", + "postcss-mixins": "^11.0.0", + "postcss-nesting": "^13.0.0", "raw-loader": "^4.0.1", "shelljs": "^0.8.1", - "style-loader": "^2.0.0", - "terser-webpack-plugin": "^4.2.3", - "through2": "^3.0.1" + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.0.0", + "through2": "^4.0.0" }, "devDependencies": { "jest-extended": "^4.0.2", - "handlebars": "^4.7.6", - "vinyl": "^2.1.0", + "vinyl": "^3.0.0", "vitest": "^2.0.5" }, "scripts": { From 6bf2e250d6c6ffcf40852eb021312ed1b9288d7b Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:55:41 +0200 Subject: [PATCH 132/172] ckeditor5-dev-web-crawler: use the latest compatible version of dependencies. --- packages/ckeditor5-dev-web-crawler/lib/runcrawler.js | 10 +++++++--- packages/ckeditor5-dev-web-crawler/package.json | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js index a51b0c8d0..bb293c9a1 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js +++ b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js @@ -129,10 +129,14 @@ async function createBrowser( options ) { const browser = await puppeteer.launch( browserOptions ); - const [ defaultBlankPage ] = await browser.pages(); + // For unknown reasons, in order to be able to visit pages in Puppeteer on CI, we must close the default page that is opened when the + // browser starts. + if ( process.env.CI ) { + const [ defaultBlankPage ] = await browser.pages(); - if ( defaultBlankPage ) { - await defaultBlankPage.close(); + if ( defaultBlankPage ) { + await defaultBlankPage.close(); + } } return browser; diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index 04e201fe1..6170af640 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -22,9 +22,9 @@ "lib" ], "dependencies": { - "chalk": "^4.1.0", - "ora": "^5.2.0", - "puppeteer": "^19.7.5", - "strip-ansi": "^6.0.0" + "chalk": "^5.0.0", + "ora": "^8.0.0", + "puppeteer": "^23.4.0", + "strip-ansi": "^7.0.0" } } From 85062469ec3316c052b2504c8af8ab816effcb2d Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:56:02 +0200 Subject: [PATCH 133/172] typedoc-plugins: use the latest compatible version of dependencies. --- packages/typedoc-plugins/package.json | 4 ++-- .../typedoc-plugins/tests/event-inheritance-fixer/index.js | 2 +- packages/typedoc-plugins/tests/event-param-fixer/index.js | 2 +- .../tests/interface-augmentation-fixer/index.js | 2 +- packages/typedoc-plugins/tests/module-fixer/index.js | 2 +- .../typedoc-plugins/tests/purge-private-api-docs/index.js | 2 +- packages/typedoc-plugins/tests/symbol-fixer/index.js | 2 +- packages/typedoc-plugins/tests/tag-error/index.js | 2 +- packages/typedoc-plugins/tests/tag-event/index.js | 2 +- packages/typedoc-plugins/tests/tag-observable/index.js | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index 763885174..9fa29b772 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -22,8 +22,8 @@ ], "devDependencies": { "chai": "^4.2.0", - "fast-glob": "^3.2.4", - "mocha": "^7.1.2", + "glob": "^10.0.0", + "mocha": "^10.0.0", "sinon": "^9.2.4", "typedoc": "^0.23.15" }, diff --git a/packages/typedoc-plugins/tests/event-inheritance-fixer/index.js b/packages/typedoc-plugins/tests/event-inheritance-fixer/index.js index 1500d18d6..de90e31e9 100644 --- a/packages/typedoc-plugins/tests/event-inheritance-fixer/index.js +++ b/packages/typedoc-plugins/tests/event-inheritance-fixer/index.js @@ -4,7 +4,7 @@ */ const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); diff --git a/packages/typedoc-plugins/tests/event-param-fixer/index.js b/packages/typedoc-plugins/tests/event-param-fixer/index.js index 62ab39896..7f2ba9e02 100644 --- a/packages/typedoc-plugins/tests/event-param-fixer/index.js +++ b/packages/typedoc-plugins/tests/event-param-fixer/index.js @@ -5,7 +5,7 @@ const sinon = require( 'sinon' ); const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); diff --git a/packages/typedoc-plugins/tests/interface-augmentation-fixer/index.js b/packages/typedoc-plugins/tests/interface-augmentation-fixer/index.js index bde89ffd1..50779555f 100644 --- a/packages/typedoc-plugins/tests/interface-augmentation-fixer/index.js +++ b/packages/typedoc-plugins/tests/interface-augmentation-fixer/index.js @@ -4,7 +4,7 @@ */ const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); diff --git a/packages/typedoc-plugins/tests/module-fixer/index.js b/packages/typedoc-plugins/tests/module-fixer/index.js index a15a321d3..117505d78 100644 --- a/packages/typedoc-plugins/tests/module-fixer/index.js +++ b/packages/typedoc-plugins/tests/module-fixer/index.js @@ -4,7 +4,7 @@ */ const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); diff --git a/packages/typedoc-plugins/tests/purge-private-api-docs/index.js b/packages/typedoc-plugins/tests/purge-private-api-docs/index.js index 68fd7a68d..01da83b4a 100644 --- a/packages/typedoc-plugins/tests/purge-private-api-docs/index.js +++ b/packages/typedoc-plugins/tests/purge-private-api-docs/index.js @@ -4,7 +4,7 @@ */ const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); diff --git a/packages/typedoc-plugins/tests/symbol-fixer/index.js b/packages/typedoc-plugins/tests/symbol-fixer/index.js index 3fe5936ee..eb047c742 100644 --- a/packages/typedoc-plugins/tests/symbol-fixer/index.js +++ b/packages/typedoc-plugins/tests/symbol-fixer/index.js @@ -5,7 +5,7 @@ const { expect } = require( 'chai' ); const TypeDoc = require( 'typedoc' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const utils = require( '../utils' ); const { plugins } = require( '../../lib' ); diff --git a/packages/typedoc-plugins/tests/tag-error/index.js b/packages/typedoc-plugins/tests/tag-error/index.js index 530530b7d..71abd8335 100644 --- a/packages/typedoc-plugins/tests/tag-error/index.js +++ b/packages/typedoc-plugins/tests/tag-error/index.js @@ -4,7 +4,7 @@ */ const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); diff --git a/packages/typedoc-plugins/tests/tag-event/index.js b/packages/typedoc-plugins/tests/tag-event/index.js index c77fee64c..fa7f431d9 100644 --- a/packages/typedoc-plugins/tests/tag-event/index.js +++ b/packages/typedoc-plugins/tests/tag-event/index.js @@ -5,7 +5,7 @@ const sinon = require( 'sinon' ); const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); diff --git a/packages/typedoc-plugins/tests/tag-observable/index.js b/packages/typedoc-plugins/tests/tag-observable/index.js index 2e326f505..458b3a656 100644 --- a/packages/typedoc-plugins/tests/tag-observable/index.js +++ b/packages/typedoc-plugins/tests/tag-observable/index.js @@ -4,7 +4,7 @@ */ const { expect } = require( 'chai' ); -const glob = require( 'fast-glob' ); +const { glob } = require( 'glob' ); const TypeDoc = require( 'typedoc' ); const utils = require( '../utils' ); From d36e6e95021c11c23c9e65e56d133a7785f59130 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 10:59:57 +0200 Subject: [PATCH 134/172] ckeditor5-dev: dependencies upgrade. --- package.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5e01b0379..e1ba03c20 100644 --- a/package.json +++ b/package.json @@ -21,15 +21,14 @@ "coveralls": "^3.1.1", "eslint": "^8.21.0", "eslint-config-ckeditor5": "^7.0.0", - "fs-extra": "^11.2.0", - "glob": "^10.2.5", + "fs-extra": "^11.0.0", + "glob": "^10.0.0", "husky": "^8.0.2", "js-yaml": "^4.1.0", - "lint-staged": "^10.2.4", - "listr2": "^6.5.0", + "lint-staged": "^15.0.0", + "listr2": "^8.0.0", "minimist": "^1.2.8", - "nyc": "^15.1.0", - "semver": "^7.5.3", + "semver": "^7.6.3", "upath": "^2.0.1" }, "scripts": { From 8a7fe1a6d51815472f6f19aea97a341ff50eff40 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 11:40:22 +0200 Subject: [PATCH 135/172] Aligned code base after bumping deps in ckeditor5-dev-release-tools. --- .../generatechangelogformonorepository.js | 4 +- .../generatechangelogforsinglepackage.js | 4 +- .../lib/utils/generatechangelog.js | 4 +- .../lib/utils/getcommits.js | 14 ++-- .../lib/utils/getwriteroptions.js | 8 +- .../tests/utils/generatechangelog.js | 79 +++++++------------ .../tests/utils/getcommits.js | 10 +-- 7 files changed, 53 insertions(+), 70 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js index 34c34777c..aba420bba 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js @@ -415,9 +415,9 @@ export default async function generateChangelogForMonoRepository( options ) { date: options.formatDate ? options.formatDate( new Date() ) : getFormattedDate() }; - const writerOptions = getWriterOptions( { + const writerOptions = getWriterOptions( commit => { // We do not allow modifying the commit hash value by the generator itself. - hash: hash => hash + return commit; } ); writerOptions.commitsSort = sortFunctionFactory( 'rawScope' ); diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js index 3a2604fae..ab18ddba5 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js @@ -110,9 +110,9 @@ export default async function generateChangelogForSinglePackage( options = {} ) date: options.formatDate ? options.formatDate( new Date() ) : getFormattedDate() }; - const writerOptions = getWriterOptions( { + const writerOptions = getWriterOptions( commit => { // We do not allow modifying the commit hash value by the generator itself. - hash: hash => hash + return commit; } ); const publicCommits = [ ...allCommits ] diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js index ce38601c3..a0906993c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js @@ -5,7 +5,7 @@ import { Readable } from 'stream'; import { stream } from '@ckeditor/ckeditor5-dev-utils'; -import conventionalChangelogWriter from 'conventional-changelog-writer'; +import { writeChangelogStream } from 'conventional-changelog-writer'; const UPDATED_TRANSLATION_COMMIT = '* Updated translations.'; @@ -49,7 +49,7 @@ export default function generateChangelog( commits, context, options ) { return new Promise( ( resolve, reject ) => { commitStream - .pipe( conventionalChangelogWriter( context, options ) ) + .pipe( writeChangelogStream( context, options ) ) .pipe( stream.noop( changes => { changes = mergeUpdateTranslationsCommits( changes.toString(), { skipCommitsLink: context.skipCommitsLink diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js index b6fedbd83..e1135ce4e 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js @@ -3,9 +3,9 @@ * For licensing, see LICENSE.md. */ -import conventionalCommitsParser from 'conventional-commits-parser'; -import conventionalCommitsFilter from 'conventional-commits-filter'; -import gitRawCommits from 'git-raw-commits'; +import { parseCommitsStream } from 'conventional-commits-parser'; +import { filterRevertedCommitsSync } from 'conventional-commits-filter'; +import { getRawCommitsStream } from 'git-raw-commits'; import concat from 'concat-stream'; import parserOptions from './parseroptions.js'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; @@ -45,7 +45,7 @@ export default function getCommits( transformCommit, options = {} ) { // 1. Commits from the last release and to the point where the release branch was created (the merge-base commit). findCommits( { from: options.from, to: baseCommit } ), // 2. Commits from the merge-base commit to HEAD. - findCommits( { from: baseCommit } ) + findCommits( { from: baseCommit, to: 'HEAD' } ) ]; return Promise.all( commitPromises ) @@ -60,7 +60,7 @@ export default function getCommits( transformCommit, options = {} ) { } ); return new Promise( ( resolve, reject ) => { - const stream = gitRawCommits( gitRawCommitsOpts ) + const stream = getRawCommitsStream( gitRawCommitsOpts ) .on( 'error', err => { /* istanbul ignore else */ if ( err.message.match( /'HEAD': unknown/ ) ) { @@ -74,9 +74,9 @@ export default function getCommits( transformCommit, options = {} ) { } } ); - stream.pipe( conventionalCommitsParser( parserOptions ) ) + stream.pipe( parseCommitsStream( parserOptions ) ) .pipe( concat( data => { - const commits = conventionalCommitsFilter( data ) + const commits = [ ...filterRevertedCommitsSync( data ) ] .map( commit => transformCommit( commit ) ) .reduce( ( allCommits, commit ) => { if ( Array.isArray( commit ) ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js index be6cb971b..f60b58664 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js @@ -14,7 +14,7 @@ const __dirname = path.dirname( __filename ); const templatePath = path.join( __dirname, '..', 'templates' ); /** - * @param {Function|Object} transform + * @param {WriterOptionsTransformCallback} transform * @returns {Object} */ export default function getWriterOptions( transform ) { @@ -34,3 +34,9 @@ export default function getWriterOptions( transform ) { function sortFunction( a, b ) { return getTypeOrder( a.title ) - getTypeOrder( b.title ); } + +/** + * @callback WriterOptionsTransformCallback + * @param {Commit} + * @returns {Commit} + */ diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js index 02ac72518..8d85feb05 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js @@ -41,9 +41,7 @@ describe( 'generateChangelog()', () => { commit: 'commit' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -93,9 +91,7 @@ describe( 'generateChangelog()', () => { commit: 'commit' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -145,9 +141,7 @@ describe( 'generateChangelog()', () => { commit: 'commit' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -204,9 +198,7 @@ describe( 'generateChangelog()', () => { commit: 'commit' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -265,9 +257,7 @@ describe( 'generateChangelog()', () => { skipCommitsLink: true }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -301,9 +291,7 @@ describe( 'generateChangelog()', () => { skipCompareLink: true }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( [], context, options ) .then( changes => { @@ -360,9 +348,7 @@ describe( 'generateChangelog()', () => { commit: 'commit' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -428,9 +414,7 @@ describe( 'generateChangelog()', () => { commit: 'c' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); + const options = getWriterOptions( transformCommitCallback( 2 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -493,9 +477,7 @@ describe( 'generateChangelog()', () => { skipCommitsLink: true }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); + const options = getWriterOptions( transformCommitCallback( 2 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -576,9 +558,7 @@ describe( 'generateChangelog()', () => { commit: 'c' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); + const options = getWriterOptions( transformCommitCallback( 2 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -661,9 +641,7 @@ describe( 'generateChangelog()', () => { commit: 'c' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); + const options = getWriterOptions( transformCommitCallback( 2 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -734,9 +712,7 @@ describe( 'generateChangelog()', () => { commit: 'c' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); + const options = getWriterOptions( transformCommitCallback( 2 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -794,9 +770,7 @@ describe( 'generateChangelog()', () => { commit: 'commit' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -846,9 +820,7 @@ describe( 'generateChangelog()', () => { isInternalRelease: true }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( commits, context, options ) .then( changes => { @@ -876,9 +848,7 @@ describe( 'generateChangelog()', () => { commit: 'commit' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( [], context, options ) .then( changes => { @@ -969,9 +939,7 @@ describe( 'generateChangelog()', () => { commit: 'c' }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 2 ) - } ); + const options = getWriterOptions( transformCommitCallback( 2 ) ); const sortFunction = compareFunc( item => { if ( Array.isArray( item.scope ) ) { @@ -1047,9 +1015,7 @@ describe( 'generateChangelog()', () => { skipCompareLink: true }; - const options = getWriterOptions( { - hash: hash => hash.slice( 0, 7 ) - } ); + const options = getWriterOptions( transformCommitCallback( 7 ) ); return generateChangelog( [], context, options ) .then( changes => { @@ -1072,3 +1038,14 @@ describe( 'generateChangelog()', () => { function replaceDates( changelog ) { return changelog.replace( /\d{4}-\d{2}-\d{2}/g, '0000-00-00' ); } + +/** + * @param {Number} length + * @returns {WriterOptionsTransformCallback} + */ +function transformCommitCallback( length ) { + return commit => ( { + ...commit, + hash: commit.hash.slice( 0, length ) + } ); +} diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 641057e37..0dcee0018 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -7,7 +7,7 @@ import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach } import { fileURLToPath } from 'url'; import fs from 'fs'; import path from 'path'; -import gitRawCommits from 'git-raw-commits'; +import { getRawCommitsStream } from 'git-raw-commits'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; const __filename = fileURLToPath( import.meta.url ); @@ -36,13 +36,13 @@ describe( 'getCommits()', () => { } vi.doMock( 'git-raw-commits', () => ( { - default: vi.fn( gitRawCommits ) + getRawCommitsStream: vi.fn( getRawCommitsStream ) } ) ); vi.doMock( '@ckeditor/ckeditor5-dev-utils' ); stubs = { - gitRawCommits: ( await import( 'git-raw-commits' ) ).default, + getRawCommitsStream: ( await import( 'git-raw-commits' ) ).getRawCommitsStream, devTools: ( await import( '@ckeditor/ckeditor5-dev-utils' ) ).tools }; @@ -269,7 +269,7 @@ describe( 'getCommits()', () => { .then( commits => { expect( commits.length ).toEqual( 8 ); - expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 1, { + expect( stubs.getRawCommitsStream ).toHaveBeenNthCalledWith( 1, { from: 'v1.0.0', to: baseCommit, format: '%B%n-hash-%n%H', @@ -277,7 +277,7 @@ describe( 'getCommits()', () => { firstParent: true } ); - expect( stubs.gitRawCommits ).toHaveBeenNthCalledWith( 2, { + expect( stubs.getRawCommitsStream ).toHaveBeenNthCalledWith( 2, { to: 'HEAD', from: baseCommit, format: '%B%n-hash-%n%H', From 7f0efc4c621e8b8aa8497b1bf085d509cb19b5b1 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 11:52:12 +0200 Subject: [PATCH 136/172] Aligned code base after bumping deps in ckeditor5-dev-tests. --- .../lib/utils/automated-tests/assertions/equal-markup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js index 9dd6b42a0..73f1d7d1c 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js @@ -3,8 +3,8 @@ * For licensing, see LICENSE.md. */ -import AssertionError from 'assertion-error'; -import { html_beautify as beautify } from 'js-beautify/js/lib/beautify-html.js'; +import { AssertionError } from 'assertion-error'; +import { html_beautify as beautify } from 'js-beautify'; /** * Factory function that registers the `equalMarkup` assertion. From 762b33ec43b7fabe1432aed5fcb2cb9a7de177e8 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 11:53:38 +0200 Subject: [PATCH 137/172] Aligned code base after bumping deps in ckeditor5-dev-transifex. --- packages/ckeditor5-dev-transifex/lib/createpotfiles.js | 4 ++-- packages/ckeditor5-dev-transifex/tests/createpotfiles.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js index 16fd8a9eb..00165f622 100644 --- a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js @@ -5,7 +5,7 @@ import path from 'path'; import fs from 'fs-extra'; -import { deleteAsync } from 'del'; +import { deleteSync } from 'del'; import { logger as utilsLogger } from '@ckeditor/ckeditor5-dev-utils'; import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; import { verifyProperties } from './utils.js'; @@ -229,7 +229,7 @@ function assertNoRepeatedContext( { packageContexts } ) { } function removeExistingPotFiles( translationsDirectory ) { - deleteAsync.sync( translationsDirectory ); + deleteSync( translationsDirectory ); } /** diff --git a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js index 87ca1df8f..dec22caae 100644 --- a/packages/ckeditor5-dev-transifex/tests/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/tests/createpotfiles.js @@ -8,7 +8,7 @@ import createPotFiles from '../lib/createpotfiles.js'; import { findMessages } from '@ckeditor/ckeditor5-dev-translations'; import { verifyProperties } from '../lib/utils.js'; -import { deleteAsync } from 'del'; +import { deleteSync } from 'del'; import fs from 'fs-extra'; import path from 'path'; @@ -53,8 +53,8 @@ describe( 'dev-transifex/createPotFiles()', () => { logger: loggerMocks } ); - expect( vi.mocked( deleteAsync.sync ) ).toHaveBeenCalledTimes( 1 ); - expect( vi.mocked( deleteAsync.sync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex' ); + expect( vi.mocked( deleteSync ) ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( deleteSync ) ).toHaveBeenCalledWith( '/cwd/build/.transifex' ); } ); it( 'should create a POT file entry for one message with a corresponding context', () => { From ff1df0b7819841b881cb13863c0c4eb7555328c1 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 11:56:32 +0200 Subject: [PATCH 138/172] Aligned code base after bumping deps in ckeditor5-dev-utils. --- packages/ckeditor5-dev-utils/lib/tools/createspinner.js | 6 +++--- packages/ckeditor5-dev-utils/tests/tools/createspinner.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js index b69118c9c..b025260e9 100644 --- a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js +++ b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js @@ -6,7 +6,7 @@ import readline from 'readline'; import isInteractive from 'is-interactive'; import cliSpinners from 'cli-spinners'; -import { show as cliCursorShow, hide as cliCursorHide } from 'cli-cursor'; +import cliCursor from 'cli-cursor'; // A size of default indent for a log. const INDENT_SIZE = 3; @@ -64,7 +64,7 @@ export default function createSpinner( title, options = {} ) { let index = 0; let shouldClearLastLine = false; - cliCursorHide(); + cliCursor.hide(); timerId = setInterval( () => { if ( index === frames.length ) { @@ -102,7 +102,7 @@ export default function createSpinner( title, options = {} ) { clearLastLine(); } - cliCursorShow(); + cliCursor.show(); console.log( `${ indent }${ finishEmoji } ${ title }` ); } }; diff --git a/packages/ckeditor5-dev-utils/tests/tools/createspinner.js b/packages/ckeditor5-dev-utils/tests/tools/createspinner.js index fd4e848e8..fb519392c 100644 --- a/packages/ckeditor5-dev-utils/tests/tools/createspinner.js +++ b/packages/ckeditor5-dev-utils/tests/tools/createspinner.js @@ -4,7 +4,7 @@ */ import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; -import { show as cliCursorShow, hide as cliCursorHide } from 'cli-cursor'; +import cliCursor from 'cli-cursor'; import isInteractive from 'is-interactive'; import createSpinner from '../../lib/tools/createspinner.js'; import readline from 'readline'; @@ -128,7 +128,7 @@ describe( 'createSpinner()', () => { spinner.start(); - expect( vi.mocked( cliCursorHide ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( cliCursor ).hide ).toHaveBeenCalledOnce(); } ); it( 'allows indenting messages by specifying the "options.indentLevel" option', () => { @@ -212,7 +212,7 @@ describe( 'createSpinner()', () => { spinner.start(); spinner.finish(); - expect( vi.mocked( cliCursorShow ) ).toHaveBeenCalledOnce(); + expect( vi.mocked( cliCursor ).show ).toHaveBeenCalledOnce(); } ); it( 'allows indenting messages by specifying the "options.indentLevel" option', () => { From 689d99d3a25ef6bca4220f9a1e7a7307eb607325 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 12:04:57 +0200 Subject: [PATCH 139/172] Restored missing nyc package in typedoc-plugins (tests). --- packages/typedoc-plugins/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index 9fa29b772..c60ec4678 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -24,6 +24,7 @@ "chai": "^4.2.0", "glob": "^10.0.0", "mocha": "^10.0.0", + "nyc": "^17.1.0", "sinon": "^9.2.4", "typedoc": "^0.23.15" }, From c584a84391987721193b9af92d98c5479156f550 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 12:22:47 +0200 Subject: [PATCH 140/172] Moved the "coveralls" package to install on CI as it is not needed locally. --- .circleci/template.yml | 3 +++ package.json | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/template.yml b/.circleci/template.yml index 3d9356d8c..2baccf359 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -119,6 +119,9 @@ jobs: then circleci-agent step halt fi + - run: + name: Install the "coveralls" package + command: yarn add --ignore-workspace-root-check coveralls - run: name: Upload code coverage command: cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js diff --git a/package.json b/package.json index e1ba03c20..bd13fd982 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.0", "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.0", "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.0", - "coveralls": "^3.1.1", "eslint": "^8.21.0", "eslint-config-ckeditor5": "^7.0.0", "fs-extra": "^11.0.0", From 8b5078e67ebbbe9e8a5a952fa18646dfca6a2563 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 12:47:06 +0200 Subject: [PATCH 141/172] Feature (release-tools): Allow defining a main branch when generating the changelog entries. --- .../lib/tasks/generatechangelogformonorepository.js | 6 +++++- .../lib/tasks/generatechangelogforsinglepackage.js | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js index aba420bba..2d7606b54 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js @@ -53,6 +53,8 @@ const noteInfo = `[ℹī¸](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-c * * @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. * + * @param {String} [options.mainBranch='master'] A name of the main branch in the repository. + * * @param {Array.} [options.externalRepositories=[]] An array of object with additional repositories * that the function takes into consideration while gathering commits. It assumes that those directories are also mono repositories. * @@ -85,6 +87,7 @@ export default async function generateChangelogForMonoRepository( options ) { cwd: options.cwd, from: options.from ? options.from : 'v' + rootPkgJson.version, releaseBranch: options.releaseBranch || 'master', + mainBranch: options.mainBranch || 'master', externalRepositories: options.externalRepositories || [] }; @@ -225,7 +228,8 @@ export default async function generateChangelogForMonoRepository( options ) { const commitOptions = { from: options.from, - releaseBranch: options.releaseBranch + releaseBranch: options.releaseBranch, + mainBranch: options.mainBranch }; let promise = getCommits( transformCommit, commitOptions ) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js index ab18ddba5..8d4dfc86a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js @@ -36,6 +36,8 @@ const SKIP_GENERATE_CHANGELOG = 'Typed "skip" as a new version. Aborting.'; * * @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. * + * @param {String} [options.mainBranch='master'] A name of the main branch in the repository. + * * @param {FormatDateCallback} [options.formatDate] A callback allowing defining a custom format of the date inserted into the changelog. * If not specified, the default date matches the `YYYY-MM-DD` pattern. * @@ -53,7 +55,8 @@ export default async function generateChangelogForSinglePackage( options = {} ) const commitOptions = { from: options.from ? options.from : 'v' + pkgJson.version, - releaseBranch: options.releaseBranch + releaseBranch: options.releaseBranch || 'master', + mainBranch: options.mainBranch || 'master' }; // Initial release. From 3598a5ba420a8df5eba94c3b6bf1c70ec86383f8 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 12:47:32 +0200 Subject: [PATCH 142/172] Define a main branch to generate the changelog entries correctly. --- scripts/changelog.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/changelog.js b/scripts/changelog.js index 6051ec3f8..278462e38 100755 --- a/scripts/changelog.js +++ b/scripts/changelog.js @@ -13,6 +13,7 @@ const cliArguments = parseArguments( process.argv.slice( 2 ) ); const changelogOptions = { cwd: process.cwd(), packages: 'packages', + mainBranch: 'epic/3757-esm', releaseBranch: cliArguments.branch, transformScope: name => { if ( name === 'typedoc-plugins' ) { From 9ea42c9628655e720333fea1246c0015e2d38475 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 12:47:58 +0200 Subject: [PATCH 143/172] Docs: Changelog. [skip ci] --- CHANGELOG.md | 70 +++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 921e70796..7d9d11686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,40 @@ Changelog ========= +## [44.0.0-alpha.1](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.0...v44.0.0-alpha.1) (2024-09-23) + +### Features + +* **[release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools)**: Allow defining a main branch when generating the changelog entries. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/8b5078e67ebbbe9e8a5a952fa18646dfca6a2563)) + +### Released packages + +Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. + +
+Released packages (summary) + +Releases containing new features: + +* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 + +Other releases: + +* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 +
+ + ## [44.0.0-alpha.0](https://github.com/ckeditor/ckeditor5-dev/compare/v43.0.0...v44.0.0-alpha.0) (2024-09-20) ### MAJOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) @@ -197,42 +231,6 @@ Other releases: * [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/42.1.0): v42.0.1 => v42.1.0 - -## [42.0.1](https://github.com/ckeditor/ckeditor5-dev/compare/v42.0.0...v42.0.1) (2024-08-13) - -### Bug fixes - -* **[tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests)**: Added a Chrome flag to prevent displaying the search engine choice screen that disrupts automated tests in windowed mode. Closes [ckeditor/ckeditor5#16825](https://github.com/ckeditor/ckeditor5/issues/16825). ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/4f7291f1f8114ed0184f11a51c74752c6d8ecaa9)) - -### Other changes - -* **[stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot)**: Aligned stale bot to recent changes in the GitHub GraphQL API in the `repository.labels` connection. GitHub recently started returning a lot of mismatched labels for the query and now stale bot ensures that only the required ones are used. Closes [ckeditor/ckeditor5#16872](https://github.com/ckeditor/ckeditor5/issues/16872). ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/666daf6cfe52b5ce63e7937168022eb86fcb4f9c)) - -### Released packages - -Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. - -
-Released packages (summary) - -Other releases: - -* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/jsdoc-plugins](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins/v/42.0.1): v42.0.0 => v42.0.1 -* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/42.0.1): v42.0.0 => v42.0.1 -
- --- To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases). From 033430b2a712c7bd575e6d5472b3efe61a7a1158 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 12:49:29 +0200 Subject: [PATCH 144/172] Changelog for v44.0.0-alpha.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d9d11686..468d3b76f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ Changelog * **[release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools)**: Allow defining a main branch when generating the changelog entries. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/8b5078e67ebbbe9e8a5a952fa18646dfca6a2563)) +### Other changes + +* Almost all dependencies of `ckeditor5-dev-*` packages have been bumped to their latest versions. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/2358a19113eb80f6204f39a1d0e0411810283ef2)) + ### Released packages Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. From c4ab299197a5ff1824e480533181ea2fad39553d Mon Sep 17 00:00:00 2001 From: CKEditorBot Date: Mon, 23 Sep 2024 10:55:57 +0000 Subject: [PATCH 145/172] Release: v'44.0.0-alpha.1'. --- package.json | 8 ++++---- packages/ckeditor5-dev-build-tools/package.json | 2 +- packages/ckeditor5-dev-bump-year/package.json | 2 +- packages/ckeditor5-dev-ci/package.json | 2 +- packages/ckeditor5-dev-dependency-checker/package.json | 4 ++-- packages/ckeditor5-dev-docs/package.json | 6 +++--- packages/ckeditor5-dev-release-tools/package.json | 4 ++-- packages/ckeditor5-dev-stale-bot/package.json | 4 ++-- packages/ckeditor5-dev-tests/package.json | 6 +++--- packages/ckeditor5-dev-transifex/package.json | 6 +++--- packages/ckeditor5-dev-translations/package.json | 2 +- packages/ckeditor5-dev-utils/package.json | 4 ++-- packages/ckeditor5-dev-web-crawler/package.json | 2 +- packages/typedoc-plugins/package.json | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index bd13fd982..754ca8da8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ckeditor5-dev", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "private": true, "author": "CKSource (http://cksource.com/)", "license": "GPL-2.0-or-later", @@ -15,9 +15,9 @@ }, "type": "module", "devDependencies": { - "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.0", - "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.0", - "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.1", "eslint": "^8.21.0", "eslint-config-ckeditor5": "^7.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-build-tools/package.json b/packages/ckeditor5-dev-build-tools/package.json index 87bbc1219..840442be3 100644 --- a/packages/ckeditor5-dev-build-tools/package.json +++ b/packages/ckeditor5-dev-build-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-build-tools", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Rollup-based tools used to build CKEditor 5 packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index b5ad92091..bfa76ba28 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-bump-year", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Used to bump year in the licence text specified at the top of the file.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 992ae3cc3..ffd00498e 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-ci", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Utils used on various Continuous Integration services.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index 175bc3e84..5cc9fa475 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-dependency-checker", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Contains tools for validating dependencies specified in package.json.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-dependency-checker": "bin/dependencychecker.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", "chalk": "^5.0.0", "depcheck": "^1.3.1", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index fc8d79faa..ea3f16e38 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-docs", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Tasks used to build and verify the documentation for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,8 +22,8 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", - "@ckeditor/typedoc-plugins": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", + "@ckeditor/typedoc-plugins": "^44.0.0-alpha.1", "glob": "^10.0.0", "fs-extra": "^11.0.0", "tmp": "^0.2.1", diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 9dd9cac26..668d5a49b 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-release-tools", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Tools used for releasing CKEditor 5 and related packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", "@octokit/rest": "^21.0.0", "chalk": "^5.0.0", "cli-columns": "^4.0.0", diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index f56ed340b..926657736 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-stale-bot", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "A stale bot is used to mark issues and pull requests that have not recently been updated.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-stale-bot": "bin/stale-bot.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", "chalk": "^5.0.0", "date-fns": "^4.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index c1f52ae94..3ee6b6f34 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-tests", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Testing environment for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -28,8 +28,8 @@ }, "dependencies": { "@babel/core": "^7.10.5", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", "@ckeditor/ckeditor5-inspector": "^4.0.0", "@types/chai": "^4.3.5", "@types/karma-sinon-chai": "^2.0.2", diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index 540b7d3f2..bdbe2f0ea 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-transifex", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Used to download and upload translations using the Transifex service.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -24,8 +24,8 @@ "dependencies": { "fs-extra": "^11.0.0", "del": "^7.0.0", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.0", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.1", "chalk": "^5.0.0", "inquirer": "^11.0.0", "@transifex/api": "^7.0.0", diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index ef751ead3..0234ffae5 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-translations", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "CKEditor 5 translations plugin for webpack.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index 9daf9d7d8..dbdfb4a4b 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-utils", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Utils for CKEditor 5 development tools packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.0", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.1", "chalk": "^5.0.0", "cli-cursor": "^5.0.0", "cli-spinners": "^3.0.0", diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index 6170af640..337eb8bac 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-web-crawler", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Used to run a web crawler that checks for errors on specified pages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index c60ec4678..1b9272528 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/typedoc-plugins", - "version": "44.0.0-alpha.0", + "version": "44.0.0-alpha.1", "description": "Various TypeDoc plugins developed by the CKEditor 5 team.", "keywords": [], "author": "CKSource (http://cksource.com/)", From 868a8057894c114b850d25d02de4827c8d2ec1a5 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 13:07:42 +0200 Subject: [PATCH 146/172] Addressed an issue with invalid commit names after wrapping an input argument in shell-escape helper. --- .../lib/tasks/commitandtag.js | 10 +++++++--- .../tests/tasks/commitandtag.js | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js index e3a67a5ca..b03b60f79 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js @@ -38,7 +38,11 @@ export default async function commitAndTag( { version, files, cwd = process.cwd( await tools.shExec( `git add ${ shellEscape( [ filePath ] ) }`, shExecOptions ); } - const escapedVersion = shellEscape( [ version ] ); - await tools.shExec( `git commit --message "Release: v${ escapedVersion }." --no-verify`, shExecOptions ); - await tools.shExec( `git tag v${ escapedVersion }`, shExecOptions ); + const escapedVersion = { + commit: shellEscape( [ `Release: v${ version }.` ] ), + tag: shellEscape( [ `v${ version }` ] ) + }; + + await tools.shExec( `git commit --message ${ escapedVersion.commit } --no-verify`, shExecOptions ); + await tools.shExec( `git tag ${ escapedVersion.tag }`, shExecOptions ); } diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js b/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js index 1fb6f0e69..5b8dfd929 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/commitandtag.js @@ -16,7 +16,7 @@ vi.mock( '@ckeditor/ckeditor5-dev-utils' ); describe( 'commitAndTag()', () => { beforeEach( () => { vi.mocked( glob ).mockResolvedValue( [] ); - vi.mocked( shellEscape ).mockImplementation( v => v[ 0 ] ); + vi.mocked( shellEscape ).mockImplementation( v => `'${ v[ 0 ] }'` ); } ); it( 'should not create a commit and tag if there are no files modified', async () => { @@ -49,10 +49,10 @@ describe( 'commitAndTag()', () => { } ); expect( vi.mocked( tools.shExec ) ).toHaveBeenCalledTimes( 6 ); - expect( vi.mocked( tools.shExec ).mock.calls[ 0 ][ 0 ] ).to.equal( 'git add package.json' ); - expect( vi.mocked( tools.shExec ).mock.calls[ 1 ][ 0 ] ).to.equal( 'git add README.md' ); - expect( vi.mocked( tools.shExec ).mock.calls[ 2 ][ 0 ] ).to.equal( 'git add packages/custom-package/package.json' ); - expect( vi.mocked( tools.shExec ).mock.calls[ 3 ][ 0 ] ).to.equal( 'git add packages/custom-package/README.md' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 0 ][ 0 ] ).to.equal( 'git add \'package.json\'' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 1 ][ 0 ] ).to.equal( 'git add \'README.md\'' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 2 ][ 0 ] ).to.equal( 'git add \'packages/custom-package/package.json\'' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 3 ][ 0 ] ).to.equal( 'git add \'packages/custom-package/README.md\'' ); } ); it( 'should set correct commit message', async () => { @@ -60,7 +60,7 @@ describe( 'commitAndTag()', () => { await commitAndTag( { version: '1.0.0', packagesDirectory: 'packages' } ); - expect( vi.mocked( tools.shExec ).mock.calls[ 1 ][ 0 ] ).to.equal( 'git commit --message "Release: v1.0.0." --no-verify' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 1 ][ 0 ] ).to.equal( 'git commit --message \'Release: v1.0.0.\' --no-verify' ); } ); it( 'should set correct tag', async () => { @@ -68,7 +68,7 @@ describe( 'commitAndTag()', () => { await commitAndTag( { version: '1.0.0', packagesDirectory: 'packages' } ); - expect( vi.mocked( tools.shExec ).mock.calls[ 2 ][ 0 ] ).to.equal( 'git tag v1.0.0' ); + expect( vi.mocked( tools.shExec ).mock.calls[ 2 ][ 0 ] ).to.equal( 'git tag \'v1.0.0\'' ); } ); it( 'should escape arguments passed to a shell command', async () => { @@ -84,11 +84,12 @@ describe( 'commitAndTag()', () => { files: [ 'package.json', 'README.md', 'packages/*/package.json', 'packages/*/README.md' ] } ); - expect( vi.mocked( shellEscape ) ).toHaveBeenCalledTimes( 5 ); + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledTimes( 6 ); expect( vi.mocked( shellEscape ).mock.calls[ 0 ][ 0 ] ).to.deep.equal( [ 'package.json' ] ); expect( vi.mocked( shellEscape ).mock.calls[ 1 ][ 0 ] ).to.deep.equal( [ 'README.md' ] ); expect( vi.mocked( shellEscape ).mock.calls[ 2 ][ 0 ] ).to.deep.equal( [ 'packages/custom-package/package.json' ] ); expect( vi.mocked( shellEscape ).mock.calls[ 3 ][ 0 ] ).to.deep.equal( [ 'packages/custom-package/README.md' ] ); - expect( vi.mocked( shellEscape ).mock.calls[ 4 ][ 0 ] ).to.deep.equal( [ '1.0.0' ] ); + expect( vi.mocked( shellEscape ).mock.calls[ 4 ][ 0 ] ).to.deep.equal( [ 'Release: v1.0.0.' ] ); + expect( vi.mocked( shellEscape ).mock.calls[ 5 ][ 0 ] ).to.deep.equal( [ 'v1.0.0' ] ); } ); } ); From 833ac929e4193b6791c79cdc5df05e67539c0c7f Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 13:46:18 +0200 Subject: [PATCH 147/172] Other (tests): Restored the previous version of Chai and sinon-chai packages due to issues with processing ESM in Karma. --- packages/ckeditor5-dev-tests/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 3ee6b6f34..3b553d28e 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -38,7 +38,7 @@ "assertion-error": "^2.0.0", "babel-plugin-istanbul": "^7.0.0", "buffer": "^6.0.3", - "chai": "^5.0.0", + "chai": "^4.5.0", "chalk": "^5.0.0", "chokidar": "^4.0.0", "commonmark": "^0.29.1", @@ -69,7 +69,7 @@ "node-notifier": "^10.0.1", "process": "^0.11.10", "sinon": "^19.0.0", - "sinon-chai": "^4.0.0", + "sinon-chai": "^3.5.0", "socket.io": "^4.0.0", "typescript": "5.0.4", "webpack": "^5.94.0" From 3c858289a5826c9fceddfb380a4e35d48b44a099 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 14:09:33 +0200 Subject: [PATCH 148/172] Other (docs): Support for passing an array of files to ignore when preparing API. --- packages/ckeditor5-dev-docs/lib/build.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index 0254464f9..22312aa54 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -20,9 +20,12 @@ export default async function build( config ) { const sourceFilePatterns = config.sourceFiles.filter( Boolean ); const strictMode = config.strict || false; const extraPlugins = config.extraPlugins || []; + const ignoreFiles = config.ignoreFiles || []; const validatorOptions = config.validatorOptions || {}; - const files = await glob( sourceFilePatterns ); + const files = await glob( sourceFilePatterns, { + ignore: ignoreFiles + } ); const typeDoc = new TypeDoc.Application(); typeDoc.options.addReader( new TypeDoc.TSConfigReader() ); @@ -101,6 +104,8 @@ export default async function build( config ) { * * @property {Array.} sourceFiles Glob pattern with source files. * + * @property {Array.} [ignoreFiles=[]] Glob pattern with files to ignore. + * * @property {Boolean} [strict=false] If `true`, errors found during the validation will finish the process * and exit code will be changed to `1`. * @property {String} [outputPath] A path to the place where extracted doclets will be saved. Is an optional value due to tests. From ce3902d5855f1b3ba886dea1db195a5b27e22026 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 14:23:44 +0200 Subject: [PATCH 149/172] Other: Use the "2021" edition as a default preset for CKEditor 5 files (`postcss-nesting`). --- packages/ckeditor5-dev-build-tools/src/config.ts | 3 ++- packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js | 3 ++- packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/ckeditor5-dev-build-tools/src/config.ts b/packages/ckeditor5-dev-build-tools/src/config.ts index 586d4a46e..cd20ccec8 100644 --- a/packages/ckeditor5-dev-build-tools/src/config.ts +++ b/packages/ckeditor5-dev-build-tools/src/config.ts @@ -152,7 +152,8 @@ export async function getRollupConfig( options: BuildOptions ) { plugins: [ postcssMixins, postcssNesting( { - noIsPseudoSelector: true + noIsPseudoSelector: true, + edition: '2021' } ) ], minimize: minify, diff --git a/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js b/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js index 1a5bfddd3..231e9617e 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js +++ b/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js @@ -30,7 +30,8 @@ export default function getPostCssConfig( options = {} ) { postCssMixins(), postCssNesting( { // https://github.com/ckeditor/ckeditor5/issues/11730 - noIsPseudoSelector: true + noIsPseudoSelector: true, + edition: '2021' } ), themeLogger() ] diff --git a/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js b/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js index 737c36dbd..7a8d06c0d 100644 --- a/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js +++ b/packages/ckeditor5-dev-utils/tests/styles/getpostcssconfig.js @@ -58,7 +58,8 @@ describe( 'getPostCssConfig()', () => { getPostCssConfig(); expect( vi.mocked( postCssNesting ) ).toHaveBeenCalledExactlyOnceWith( { - noIsPseudoSelector: true + noIsPseudoSelector: true, + edition: '2021' } ); } ); From 54053c4b10646e234787bcde9f6029f4dab38644 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 14:29:15 +0200 Subject: [PATCH 150/172] Changelog for v44.0.0-alpha.2. --- CHANGELOG.md | 72 ++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 468d3b76f..f2a809f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,39 @@ Changelog ========= +## [44.0.0-alpha.2](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.1...v44.0.0-alpha.2) (2024-09-23) + +### Other changes + +* **[docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs)**: Support for passing an array of files to ignore when preparing API. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/3c858289a5826c9fceddfb380a4e35d48b44a099)) +* **[tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests)**: Restored the previous version of Chai and sinon-chai packages due to issues with processing ESM in Karma. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/833ac929e4193b6791c79cdc5df05e67539c0c7f)) +* Use the "2021" edition as a default preset for CKEditor 5 files (`postcss-nesting`). ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/ce3902d5855f1b3ba886dea1db195a5b27e22026)) + +### Released packages + +Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. + +
+Released packages (summary) + +Other releases: + +* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.2): v44.0.0-alpha.1 => v44.0.0-alpha.2 +
+ + ## [44.0.0-alpha.1](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.0...v44.0.0-alpha.1) (2024-09-23) ### Features @@ -196,45 +229,6 @@ Other releases: * [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 - -## [42.1.0](https://github.com/ckeditor/ckeditor5-dev/compare/v42.0.1...v42.1.0) (2024-08-29) - -### Features - -* **[build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools)**: Introduced a new `loadSourcemaps` plugin for loading source maps of external dependencies. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/defb966ca3e090d062d173e5098a2325696491ec)) - -### Bug fixes - -* **[build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools)**: Fixed source maps generation for the UMD build. Fixes [ckeditor/ckeditor5#16984](https://github.com/ckeditor/ckeditor5/issues/16984). ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/defb966ca3e090d062d173e5098a2325696491ec)) - -### Released packages - -Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. - -
-Released packages (summary) - -Releases containing new features: - -* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/42.1.0): v42.0.1 => v42.1.0 - -Other releases: - -* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/jsdoc-plugins](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins/v/42.1.0): v42.0.1 => v42.1.0 -* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/42.1.0): v42.0.1 => v42.1.0 -
- --- To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases). From e2714646515d711f4629a173a606ef69a03c66a9 Mon Sep 17 00:00:00 2001 From: CKEditorBot Date: Mon, 23 Sep 2024 12:38:02 +0000 Subject: [PATCH 151/172] Release: v44.0.0-alpha.2. --- package.json | 8 ++++---- packages/ckeditor5-dev-build-tools/package.json | 2 +- packages/ckeditor5-dev-bump-year/package.json | 2 +- packages/ckeditor5-dev-ci/package.json | 2 +- packages/ckeditor5-dev-dependency-checker/package.json | 4 ++-- packages/ckeditor5-dev-docs/package.json | 6 +++--- packages/ckeditor5-dev-release-tools/package.json | 4 ++-- packages/ckeditor5-dev-stale-bot/package.json | 4 ++-- packages/ckeditor5-dev-tests/package.json | 6 +++--- packages/ckeditor5-dev-transifex/package.json | 6 +++--- packages/ckeditor5-dev-translations/package.json | 2 +- packages/ckeditor5-dev-utils/package.json | 4 ++-- packages/ckeditor5-dev-web-crawler/package.json | 2 +- packages/typedoc-plugins/package.json | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 754ca8da8..ffe7c5b27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ckeditor5-dev", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "private": true, "author": "CKSource (http://cksource.com/)", "license": "GPL-2.0-or-later", @@ -15,9 +15,9 @@ }, "type": "module", "devDependencies": { - "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.1", - "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.1", - "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.2", "eslint": "^8.21.0", "eslint-config-ckeditor5": "^7.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-build-tools/package.json b/packages/ckeditor5-dev-build-tools/package.json index 840442be3..f42584e26 100644 --- a/packages/ckeditor5-dev-build-tools/package.json +++ b/packages/ckeditor5-dev-build-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-build-tools", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Rollup-based tools used to build CKEditor 5 packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index bfa76ba28..8780ccbdc 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-bump-year", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Used to bump year in the licence text specified at the top of the file.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index ffd00498e..5d6848d3b 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-ci", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Utils used on various Continuous Integration services.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index 5cc9fa475..dd9beb778 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-dependency-checker", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Contains tools for validating dependencies specified in package.json.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-dependency-checker": "bin/dependencychecker.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", "chalk": "^5.0.0", "depcheck": "^1.3.1", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index ea3f16e38..b2a1a37d0 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-docs", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Tasks used to build and verify the documentation for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,8 +22,8 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", - "@ckeditor/typedoc-plugins": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", + "@ckeditor/typedoc-plugins": "^44.0.0-alpha.2", "glob": "^10.0.0", "fs-extra": "^11.0.0", "tmp": "^0.2.1", diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 668d5a49b..319d211fa 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-release-tools", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Tools used for releasing CKEditor 5 and related packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", "@octokit/rest": "^21.0.0", "chalk": "^5.0.0", "cli-columns": "^4.0.0", diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index 926657736..60002825c 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-stale-bot", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "A stale bot is used to mark issues and pull requests that have not recently been updated.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-stale-bot": "bin/stale-bot.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", "chalk": "^5.0.0", "date-fns": "^4.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 3b553d28e..6634b45cf 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-tests", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Testing environment for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -28,8 +28,8 @@ }, "dependencies": { "@babel/core": "^7.10.5", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.1", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", "@ckeditor/ckeditor5-inspector": "^4.0.0", "@types/chai": "^4.3.5", "@types/karma-sinon-chai": "^2.0.2", diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index bdbe2f0ea..98314a6a6 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-transifex", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Used to download and upload translations using the Transifex service.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -24,8 +24,8 @@ "dependencies": { "fs-extra": "^11.0.0", "del": "^7.0.0", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.1", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.2", "chalk": "^5.0.0", "inquirer": "^11.0.0", "@transifex/api": "^7.0.0", diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index 0234ffae5..afc756e14 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-translations", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "CKEditor 5 translations plugin for webpack.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index dbdfb4a4b..00be96e7d 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-utils", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Utils for CKEditor 5 development tools packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.1", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.2", "chalk": "^5.0.0", "cli-cursor": "^5.0.0", "cli-spinners": "^3.0.0", diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index 337eb8bac..b960bfd17 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-web-crawler", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Used to run a web crawler that checks for errors on specified pages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index 1b9272528..c18373963 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/typedoc-plugins", - "version": "44.0.0-alpha.1", + "version": "44.0.0-alpha.2", "description": "Various TypeDoc plugins developed by the CKEditor 5 team.", "keywords": [], "author": "CKSource (http://cksource.com/)", From e94009abd804176cc381b9bac3de42b1da0db3da Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 20:05:45 +0200 Subject: [PATCH 152/172] Other (tests): Downgrade the "sinon" package as it is not compatible with current CKEditor 5 tests. --- packages/ckeditor5-dev-tests/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 6634b45cf..560673b1f 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -68,7 +68,7 @@ "mocha": "^10.0.0", "node-notifier": "^10.0.1", "process": "^0.11.10", - "sinon": "^19.0.0", + "sinon": "^9.2.4", "sinon-chai": "^3.5.0", "socket.io": "^4.0.0", "typescript": "5.0.4", From d70d99ffeed4609de954bae936e6f01875ded5a8 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 20:20:38 +0200 Subject: [PATCH 153/172] Other (web-crawler): Aligned internals to the latest Puppeteer API. --- packages/ckeditor5-dev-web-crawler/lib/runcrawler.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js index bb293c9a1..ea27f4f13 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js +++ b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js @@ -577,9 +577,9 @@ function registerErrorHandlers( page, { link, onError } ) { return argument; }; - const serializeArguments = argument => argument - .executionContext() - .evaluate( serializeArgumentInPageContext, argument ); + const serializeArguments = argument => { + return argument.evaluate( serializeArgumentInPageContext, argument ); + }; const serializedArguments = await Promise.all( message.args().map( serializeArguments ) ); From 9ddc9b7fd7f82b202b453a1b9b305e392aba1106 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 23 Sep 2024 20:21:23 +0200 Subject: [PATCH 154/172] Changelog for 44.0.0-alpha.3. --- CHANGELOG.md | 84 ++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2a809f71..52b431072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,38 @@ Changelog ========= +## [44.0.0-alpha.3](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.2...v44.0.0-alpha.3) (2024-09-23) + +### Other changes + +* **[tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests)**: Downgrade the "sinon" package as it is not compatible with current CKEditor 5 tests. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/e94009abd804176cc381b9bac3de42b1da0db3da)) +* **[web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler)**: Aligned internals to the latest Puppeteer API. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/d70d99ffeed4609de954bae936e6f01875ded5a8)) + +### Released packages + +Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. + +
+Released packages (summary) + +Other releases: + +* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.3): v44.0.0-alpha.2 => v44.0.0-alpha.3 +
+ + ## [44.0.0-alpha.2](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.1...v44.0.0-alpha.2) (2024-09-23) ### Other changes @@ -177,58 +209,6 @@ Other releases: * [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/43.0.0): v42.1.0 => v43.0.0 - -## [43.0.0-alpha.0](https://github.com/ckeditor/ckeditor5-dev/compare/v42.1.0...v43.0.0-alpha.0) (2024-09-02) - -### MAJOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) - -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The `git` and `workspace` objects are no longer exported from the package. Also, the following functions are no longer available in the `tools` object: - -* `isDirectory()` -* `isFile()` -* `isSymlink()` -* `sortObject()` -* `readPackageName()` -* `npmInstall()` -* `npmUninstall()` -* `npmUpdate()` -* `copyTemplateFile()` -* `copyFile()` -* `getGitUrlFromNpm()` -* `removeSymlink()` -* `clean()` - -### Other changes - -* **[dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker)**: The dependency checker analyzes dependencies by including the `lib/` and `bin/` directories as production code. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/e84c7019a61fa31c233e961afed014c1c9303989)) -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: Removed several utilities functions non-used in the CKEditor 5 environment. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/e84c7019a61fa31c233e961afed014c1c9303989)) -* Added several missing `dependencies` and `devDependencies` in packages. Also, removed non-used ones. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/e84c7019a61fa31c233e961afed014c1c9303989)) - -### Released packages - -Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. - -
-Released packages (summary) - -Other releases: - -* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/jsdoc-plugins](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/43.0.0-alpha.0): v42.1.0 => v43.0.0-alpha.0 -
- --- To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases). From 3b69ca68a1d6cdfc6175c6f366b9ddab82dd1d30 Mon Sep 17 00:00:00 2001 From: CKEditorBot Date: Mon, 23 Sep 2024 18:29:40 +0000 Subject: [PATCH 155/172] Release: v44.0.0-alpha.3. --- package.json | 8 ++++---- packages/ckeditor5-dev-build-tools/package.json | 2 +- packages/ckeditor5-dev-bump-year/package.json | 2 +- packages/ckeditor5-dev-ci/package.json | 2 +- packages/ckeditor5-dev-dependency-checker/package.json | 4 ++-- packages/ckeditor5-dev-docs/package.json | 6 +++--- packages/ckeditor5-dev-release-tools/package.json | 4 ++-- packages/ckeditor5-dev-stale-bot/package.json | 4 ++-- packages/ckeditor5-dev-tests/package.json | 6 +++--- packages/ckeditor5-dev-transifex/package.json | 6 +++--- packages/ckeditor5-dev-translations/package.json | 2 +- packages/ckeditor5-dev-utils/package.json | 4 ++-- packages/ckeditor5-dev-web-crawler/package.json | 2 +- packages/typedoc-plugins/package.json | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index ffe7c5b27..60517014b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ckeditor5-dev", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "private": true, "author": "CKSource (http://cksource.com/)", "license": "GPL-2.0-or-later", @@ -15,9 +15,9 @@ }, "type": "module", "devDependencies": { - "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.2", - "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.2", - "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.3", "eslint": "^8.21.0", "eslint-config-ckeditor5": "^7.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-build-tools/package.json b/packages/ckeditor5-dev-build-tools/package.json index f42584e26..237080c02 100644 --- a/packages/ckeditor5-dev-build-tools/package.json +++ b/packages/ckeditor5-dev-build-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-build-tools", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Rollup-based tools used to build CKEditor 5 packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index 8780ccbdc..4fa689651 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-bump-year", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Used to bump year in the licence text specified at the top of the file.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index 5d6848d3b..b80364974 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-ci", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Utils used on various Continuous Integration services.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index dd9beb778..7d9f9b68d 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-dependency-checker", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Contains tools for validating dependencies specified in package.json.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-dependency-checker": "bin/dependencychecker.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", "chalk": "^5.0.0", "depcheck": "^1.3.1", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index b2a1a37d0..909981ca5 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-docs", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Tasks used to build and verify the documentation for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,8 +22,8 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", - "@ckeditor/typedoc-plugins": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", + "@ckeditor/typedoc-plugins": "^44.0.0-alpha.3", "glob": "^10.0.0", "fs-extra": "^11.0.0", "tmp": "^0.2.1", diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 319d211fa..62a5630c0 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-release-tools", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Tools used for releasing CKEditor 5 and related packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", "@octokit/rest": "^21.0.0", "chalk": "^5.0.0", "cli-columns": "^4.0.0", diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index 60002825c..8ec5e3b38 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-stale-bot", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "A stale bot is used to mark issues and pull requests that have not recently been updated.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-stale-bot": "bin/stale-bot.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", "chalk": "^5.0.0", "date-fns": "^4.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 560673b1f..3e015b7ab 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-tests", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Testing environment for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -28,8 +28,8 @@ }, "dependencies": { "@babel/core": "^7.10.5", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.2", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", "@ckeditor/ckeditor5-inspector": "^4.0.0", "@types/chai": "^4.3.5", "@types/karma-sinon-chai": "^2.0.2", diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index 98314a6a6..9652de37b 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-transifex", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Used to download and upload translations using the Transifex service.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -24,8 +24,8 @@ "dependencies": { "fs-extra": "^11.0.0", "del": "^7.0.0", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.2", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.3", "chalk": "^5.0.0", "inquirer": "^11.0.0", "@transifex/api": "^7.0.0", diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index afc756e14..3c918518e 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-translations", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "CKEditor 5 translations plugin for webpack.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index 00be96e7d..15d898d18 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-utils", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Utils for CKEditor 5 development tools packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.2", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.3", "chalk": "^5.0.0", "cli-cursor": "^5.0.0", "cli-spinners": "^3.0.0", diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index b960bfd17..eba898a51 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-web-crawler", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Used to run a web crawler that checks for errors on specified pages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index c18373963..8f948d795 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/typedoc-plugins", - "version": "44.0.0-alpha.2", + "version": "44.0.0-alpha.3", "description": "Various TypeDoc plugins developed by the CKEditor 5 team.", "keywords": [], "author": "CKSource (http://cksource.com/)", From 15a8f79e15a69ad4cec8365cb5d86cd731ba1953 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 08:32:59 +0200 Subject: [PATCH 156/172] Fix (tests): Prevent crashing the manual test server when reading a non-existing file due to an "ERR_HTTP_HEADERS_SENT" Node.js error. --- .../lib/utils/manual-tests/createserver.js | 15 +- .../tests/utils/manual-tests/createserver.js | 283 +++++++++++++++++- 2 files changed, 293 insertions(+), 5 deletions(-) diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js index 1dcd59108..4baa9a4c3 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js @@ -59,17 +59,19 @@ export default function createManualTestServer( sourcePath, port = 8125, onCreat } function onRequest( sourcePath, request, response ) { - response.writeHead( 200, { - 'Content-Type': getContentType( request.url.endsWith( '/' ) ? '.html' : path.extname( request.url ) ) - } ); + const contentType = getContentType( request.url.endsWith( '/' ) ? '.html' : path.extname( request.url ) ); // Ignore a 'favicon' request. if ( request.url === '/favicon.ico' ) { + response.writeHead( 200, { 'Content-Type': contentType } ); + return response.end( null, 'utf-8' ); } // Generate index.html with list of the tests. if ( request.url === '/' ) { + response.writeHead( 200, { 'Content-Type': contentType } ); + return response.end( generateIndex( sourcePath ), 'utf-8' ); } @@ -79,7 +81,9 @@ function onRequest( sourcePath, request, response ) { const url = request.url.replace( /\?.+$/, '' ); const content = fs.readFileSync( path.join( sourcePath, url ) ); - response.end( content, 'utf-8' ); + response.writeHead( 200, { 'Content-Type': contentType } ); + + return response.end( content, 'utf-8' ); } catch ( error ) { logger().error( `[Server] Cannot find file '${ request.url }'.` ); @@ -101,6 +105,7 @@ function getContentType( fileExtension ) { return 'text/css'; case '.json': + case '.map': return 'application/json'; case '.png': @@ -152,6 +157,8 @@ function generateIndex( sourcePath ) { testList += ''; } + testList += ''; + const headerHtml = '

CKEditor 5 manual tests

'; return combine( viewTemplate, headerHtml, testList ); diff --git a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js index 4abfaf93c..3bf525743 100644 --- a/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/tests/utils/manual-tests/createserver.js @@ -5,12 +5,18 @@ import http from 'http'; import readline from 'readline'; +import fs from 'fs'; +import { globSync } from 'glob'; +import combine from 'dom-combiner'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { logger } from '@ckeditor/ckeditor5-dev-utils'; import createManualTestServer from '../../../lib/utils/manual-tests/createserver.js'; -vi.mock( '@ckeditor/ckeditor5-dev-utils' ); vi.mock( 'readline' ); +vi.mock( 'fs' ); +vi.mock( '@ckeditor/ckeditor5-dev-utils' ); +vi.mock( 'glob' ); +vi.mock( 'dom-combiner' ); describe( 'createManualTestServer()', () => { let loggerStub, server; @@ -86,4 +92,279 @@ describe( 'createManualTestServer()', () => { expect( vi.mocked( readline ).createInterface ).toHaveBeenCalledOnce(); expect( readlineInterface.on ).toHaveBeenCalledExactlyOnceWith( 'SIGINT', expect.any( Function ) ); } ); + + describe( 'request handler', () => { + beforeEach( () => { + createManualTestServer( 'workspace/build/.manual-tests' ); + } ); + + it( 'should handle a request for a favicon (`/favicon.ico`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/favicon.ico' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'image/x-icon' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( null, 'utf-8' ); + } ); + + it( 'should handle a root request (`/`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( '' ); + vi.mocked( globSync ).mockReturnValue( [ + '/build/.manual-tests/ckeditor5-foo/tests/manual/test-1.html', + '/build/.manual-tests/ckeditor5-foo/tests/manual/test-2.html', + '/build/.manual-tests/ckeditor5-bar/tests/manual/test-3.html', + '/build/.manual-tests/ckeditor5-bar/tests/manual/test-4.html' + ] ); + vi.mocked( combine ).mockReturnValue( 'Generated index.' ); + + serverCallback( request, response ); + + const expectedTestList = + '
'; + + expect( vi.mocked( combine ) ).toHaveBeenCalledExactlyOnceWith( + '', + expect.stringContaining( 'CKEditor 5 manual tests' ), + expectedTestList + ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'text/html' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'Generated index.', 'utf-8' ); + } ); + + it( 'should handle a request for a static resource (`*.html`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.html' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( 'An example content.' ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'text/html' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'An example content.', 'utf-8' ); + } ); + + it( 'should handle a request for a static resource (`*.js`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.js' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( 'An example content.' ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'text/javascript' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'An example content.', 'utf-8' ); + } ); + + it( 'should handle a request for a static resource (`*.json`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.json' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( 'An example content.' ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'application/json' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'An example content.', 'utf-8' ); + } ); + + it( 'should handle a request for a static resource (`*.js.map`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.js.map' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( 'An example content.' ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'application/json' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'An example content.', 'utf-8' ); + } ); + + it( 'should handle a request for a static resource (`*.css`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.css' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( 'An example content.' ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'text/css' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'An example content.', 'utf-8' ); + } ); + + it( 'should handle a request for a static resource (`*.png`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.png' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( 'An example content.' ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'image/png' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'An example content.', 'utf-8' ); + } ); + + it( 'should handle a request for a static resource (`*.jpg`)', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.jpg' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( fs ).readFileSync.mockReturnValue( 'An example content.' ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 200, expect.objectContaining( { + 'Content-Type': 'image/jpg' + } ) ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( 'An example content.', 'utf-8' ); + } ); + + it( 'should handle a request for a non-existing resource', () => { + const [ firstCall ] = vi.mocked( http ).createServer.mock.calls; + const [ serverCallback ] = firstCall; + + const request = { + url: '/file.jpg' + }; + const response = { + writeHead: vi.fn(), + end: vi.fn() + }; + + vi.mocked( logger ).mockReturnValue( { + error: vi.fn() + } ); + + vi.mocked( fs ).readFileSync.mockImplementation( () => { + const error = new Error( 'A resource does not exist' ); + error.code = 'ENOENT'; + + throw error; + } ); + + serverCallback( request, response ); + + expect( response.writeHead ).toHaveBeenCalledExactlyOnceWith( 404 ); + + expect( response.end ).toHaveBeenCalledExactlyOnceWith( + expect.stringContaining( 'Sorry, check with the site admin for error: ENOENT' ), + 'utf-8' + ); + } ); + } ); } ); From c89444e9598bffbd7b3d1070df607ac05d54c2d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Smyrek?= Date: Tue, 24 Sep 2024 08:54:06 +0200 Subject: [PATCH 157/172] Fix (web-crawler): Use `jsonValue()` method to get the serialized arguments instead of calling `evaluate()` method, which may cause unhandled rejection due to destroyed context. --- .../lib/runcrawler.js | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js index ea27f4f13..97c75863a 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js +++ b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js @@ -561,25 +561,7 @@ function registerErrorHandlers( page, { link, onError } ) { return; } - const serializeArgumentInPageContext = argument => { - // Since errors are not serializable, return the message with the call stack as the output text. - if ( argument instanceof Error ) { - return argument.stack; - } - - // Cast non-string iterable argument to an array. - if ( typeof argument !== 'string' && argument[ Symbol.iterator ] ) { - return [ ...argument ]; - } - - // Return argument right away. Since we use `executionContext().evaluate()`, it'll return JSON value of the - // argument if possible, or `undefined` if it fails to stringify it. - return argument; - }; - - const serializeArguments = argument => { - return argument.evaluate( serializeArgumentInPageContext, argument ); - }; + const serializeArguments = argument => argument.jsonValue(); const serializedArguments = await Promise.all( message.args().map( serializeArguments ) ); From 707f70b17af37dee90327acc865e612e2021144d Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 08:56:25 +0200 Subject: [PATCH 158/172] Changelog for 44.0.0-alpha.4. --- CHANGELOG.md | 84 ++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52b431072..320b26d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,38 @@ Changelog ========= +## [44.0.0-alpha.4](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.3...v44.0.0-alpha.4) (2024-09-24) + +### Bug fixes + +* **[tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests)**: Prevent crashing the manual test server when reading a non-existing file due to an "ERR_HTTP_HEADERS_SENT" Node.js error. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/15a8f79e15a69ad4cec8365cb5d86cd731ba1953)) +* **[web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler)**: Use `jsonValue()` method to get the serialized arguments instead of calling `evaluate()` method, which may cause unhandled rejection due to destroyed context. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/c89444e9598bffbd7b3d1070df607ac05d54c2d9)) + +### Released packages + +Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. + +
+Released packages (summary) + +Other releases: + +* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.4): v44.0.0-alpha.3 => v44.0.0-alpha.4 +
+ + ## [44.0.0-alpha.3](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.2...v44.0.0-alpha.3) (2024-09-23) ### Other changes @@ -157,58 +189,6 @@ Other releases: * [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 - -## [43.0.0](https://github.com/ckeditor/ckeditor5-dev/compare/v42.1.0...v43.0.0) (2024-09-09) - -### MAJOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) - -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The `git` and `workspace` objects are no longer exported from the package. Also, the following functions are no longer available in the `tools` object: - -* `isDirectory()` -* `isFile()` -* `isSymlink()` -* `sortObject()` -* `readPackageName()` -* `npmInstall()` -* `npmUninstall()` -* `npmUpdate()` -* `copyTemplateFile()` -* `copyFile()` -* `getGitUrlFromNpm()` -* `removeSymlink()` -* `clean()` - -### Other changes - -* **[dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker)**: The dependency checker analyzes dependencies by including the `lib/` and `bin/` directories as production code. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/e84c7019a61fa31c233e961afed014c1c9303989)) -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: Removed several utilities functions non-used in the CKEditor 5 environment. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/e84c7019a61fa31c233e961afed014c1c9303989)) -* Added several missing `dependencies` and `devDependencies` in packages. Also, removed non-used ones. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/e84c7019a61fa31c233e961afed014c1c9303989)) - -### Released packages - -Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. - -
-Released packages (summary) - -Other releases: - -* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/jsdoc-plugins](https://www.npmjs.com/package/@ckeditor/jsdoc-plugins/v/43.0.0): v42.1.0 => v43.0.0 -* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/43.0.0): v42.1.0 => v43.0.0 -
- --- To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases). From fafeb30c39456c312242ea6dbe6abd1e943655dd Mon Sep 17 00:00:00 2001 From: CKEditorBot Date: Tue, 24 Sep 2024 07:04:24 +0000 Subject: [PATCH 159/172] Release: v44.0.0-alpha.4. --- package.json | 8 ++++---- packages/ckeditor5-dev-build-tools/package.json | 2 +- packages/ckeditor5-dev-bump-year/package.json | 2 +- packages/ckeditor5-dev-ci/package.json | 2 +- packages/ckeditor5-dev-dependency-checker/package.json | 4 ++-- packages/ckeditor5-dev-docs/package.json | 6 +++--- packages/ckeditor5-dev-release-tools/package.json | 4 ++-- packages/ckeditor5-dev-stale-bot/package.json | 4 ++-- packages/ckeditor5-dev-tests/package.json | 6 +++--- packages/ckeditor5-dev-transifex/package.json | 6 +++--- packages/ckeditor5-dev-translations/package.json | 2 +- packages/ckeditor5-dev-utils/package.json | 4 ++-- packages/ckeditor5-dev-web-crawler/package.json | 2 +- packages/typedoc-plugins/package.json | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 60517014b..cd2dea637 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ckeditor5-dev", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "private": true, "author": "CKSource (http://cksource.com/)", "license": "GPL-2.0-or-later", @@ -15,9 +15,9 @@ }, "type": "module", "devDependencies": { - "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.3", - "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.3", - "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.4", "eslint": "^8.21.0", "eslint-config-ckeditor5": "^7.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-build-tools/package.json b/packages/ckeditor5-dev-build-tools/package.json index 237080c02..1cb08b722 100644 --- a/packages/ckeditor5-dev-build-tools/package.json +++ b/packages/ckeditor5-dev-build-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-build-tools", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Rollup-based tools used to build CKEditor 5 packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index 4fa689651..8691ae7b4 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-bump-year", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Used to bump year in the licence text specified at the top of the file.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index b80364974..cc08afe22 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-ci", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Utils used on various Continuous Integration services.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index 7d9f9b68d..dafd4420b 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-dependency-checker", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Contains tools for validating dependencies specified in package.json.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-dependency-checker": "bin/dependencychecker.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", "chalk": "^5.0.0", "depcheck": "^1.3.1", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index 909981ca5..2f1678598 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-docs", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Tasks used to build and verify the documentation for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,8 +22,8 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", - "@ckeditor/typedoc-plugins": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", + "@ckeditor/typedoc-plugins": "^44.0.0-alpha.4", "glob": "^10.0.0", "fs-extra": "^11.0.0", "tmp": "^0.2.1", diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index 62a5630c0..edac53434 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-release-tools", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Tools used for releasing CKEditor 5 and related packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", "@octokit/rest": "^21.0.0", "chalk": "^5.0.0", "cli-columns": "^4.0.0", diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index 8ec5e3b38..aeb6dc2c6 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-stale-bot", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "A stale bot is used to mark issues and pull requests that have not recently been updated.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-stale-bot": "bin/stale-bot.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", "chalk": "^5.0.0", "date-fns": "^4.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 3e015b7ab..7f090ea38 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-tests", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Testing environment for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -28,8 +28,8 @@ }, "dependencies": { "@babel/core": "^7.10.5", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.3", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", "@ckeditor/ckeditor5-inspector": "^4.0.0", "@types/chai": "^4.3.5", "@types/karma-sinon-chai": "^2.0.2", diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index 9652de37b..4a255d886 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-transifex", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Used to download and upload translations using the Transifex service.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -24,8 +24,8 @@ "dependencies": { "fs-extra": "^11.0.0", "del": "^7.0.0", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.3", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.4", "chalk": "^5.0.0", "inquirer": "^11.0.0", "@transifex/api": "^7.0.0", diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index 3c918518e..631b2bf30 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-translations", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "CKEditor 5 translations plugin for webpack.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index 15d898d18..a1af2ccd7 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-utils", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Utils for CKEditor 5 development tools packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.3", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.4", "chalk": "^5.0.0", "cli-cursor": "^5.0.0", "cli-spinners": "^3.0.0", diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index eba898a51..188026838 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-web-crawler", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Used to run a web crawler that checks for errors on specified pages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index 8f948d795..719464603 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/typedoc-plugins", - "version": "44.0.0-alpha.3", + "version": "44.0.0-alpha.4", "description": "Various TypeDoc plugins developed by the CKEditor 5 team.", "keywords": [], "author": "CKSource (http://cksource.com/)", From bc06926c132dc17fa4d1111af924f6a5933b7c4f Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 10:48:05 +0200 Subject: [PATCH 160/172] Revert "Fix (web-crawler): Use `jsonValue()` method to get the serialized arguments instead of calling `evaluate()` method, which may cause unhandled rejection due to destroyed context." This reverts commit c89444e9598bffbd7b3d1070df607ac05d54c2d9. --- .../lib/runcrawler.js | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js index 97c75863a..ea27f4f13 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js +++ b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js @@ -561,7 +561,25 @@ function registerErrorHandlers( page, { link, onError } ) { return; } - const serializeArguments = argument => argument.jsonValue(); + const serializeArgumentInPageContext = argument => { + // Since errors are not serializable, return the message with the call stack as the output text. + if ( argument instanceof Error ) { + return argument.stack; + } + + // Cast non-string iterable argument to an array. + if ( typeof argument !== 'string' && argument[ Symbol.iterator ] ) { + return [ ...argument ]; + } + + // Return argument right away. Since we use `executionContext().evaluate()`, it'll return JSON value of the + // argument if possible, or `undefined` if it fails to stringify it. + return argument; + }; + + const serializeArguments = argument => { + return argument.evaluate( serializeArgumentInPageContext, argument ); + }; const serializedArguments = await Promise.all( message.args().map( serializeArguments ) ); From 3c2df981b960e20d4de943f527375dc408a425d7 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 10:49:17 +0200 Subject: [PATCH 161/172] Other (web-crawler): Restored the previous version of the "puppeteer" package as the latest version is not too stable. --- packages/ckeditor5-dev-web-crawler/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index 188026838..eba2a1345 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -24,7 +24,7 @@ "dependencies": { "chalk": "^5.0.0", "ora": "^8.0.0", - "puppeteer": "^23.4.0", + "puppeteer": "^19.7.5", "strip-ansi": "^7.0.0" } } From 4a62989936f95233f7084e0e808e0ab417ca3780 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 11:20:57 +0200 Subject: [PATCH 162/172] Changelog for v44.0.0-alpha.5. --- CHANGELOG.md | 85 +++++++++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320b26d25..7902e5f1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,37 @@ Changelog ========= +## [44.0.0-alpha.5](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.4...v44.0.0-alpha.5) (2024-09-24) + +### Other changes + +* **[web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler)**: Restored the previous version of the "puppeteer" package as the latest version is not too stable. ([commit](https://github.com/ckeditor/ckeditor5-dev/commit/3c2df981b960e20d4de943f527375dc408a425d7)) + +### Released packages + +Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. + +
+Released packages (summary) + +Other releases: + +* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.5): v44.0.0-alpha.4 => v44.0.0-alpha.5 +
+ + ## [44.0.0-alpha.4](https://github.com/ckeditor/ckeditor5-dev/compare/v44.0.0-alpha.3...v44.0.0-alpha.4) (2024-09-24) ### Bug fixes @@ -135,60 +166,6 @@ Other releases: * [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.1): v44.0.0-alpha.0 => v44.0.0-alpha.1 - -## [44.0.0-alpha.0](https://github.com/ckeditor/ckeditor5-dev/compare/v43.0.0...v44.0.0-alpha.0) (2024-09-20) - -### MAJOR BREAKING CHANGES [ℹī¸](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes) - -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The `builds.getDllPluginWebpackConfig()` function is now asynchronous now. -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The following functions from the `bundler` object are no longer available: `createEntryFile()`, `getEditorConfig()`, `getPlugins()`. -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The following functions from the `stream` object are no longer available: `isTestFile()`, `isSourceFile()`, `isJSFile()`. -* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils)**: The `styles.themeLogger()` function is no longer exposed publicly. -* **[tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests)**: The IntelliJ Karma configuration path needs to be updated. Now, it ends with the `cjs` suffix instead of `js`. -* **[docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs)**: The package uses only TypeDoc to build the documentation. The `build()` function no longer supports `type` property in the configuration. -* The following packages have been converted to ESM. To load them, use an `import` statement instead of `require()`. - * [`@ckeditor/ckeditor5-dev-build-tools`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-build-tools) - * [`@ckeditor/ckeditor5-dev-bump-year`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-bump-year) - * [`@ckeditor/ckeditor5-dev-ci`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-ci) - * [`@ckeditor/ckeditor5-dev-dependency-checker`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-dependency-checker) - * [`@ckeditor/ckeditor5-dev-docs`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-docs) - * [`@ckeditor/ckeditor5-dev-release-tools`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-release-tools) - * [`@ckeditor/ckeditor5-dev-stale-bot`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-stale-bot) - * [`@ckeditor/ckeditor5-dev-tests`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-tests) - * [`@ckeditor/ckeditor5-dev-transifex`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-transifex) - * [`@ckeditor/ckeditor5-dev-translations`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-translations) - * [`@ckeditor/ckeditor5-dev-utils`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-utils) - * [`@ckeditor/ckeditor5-dev-web-crawler`](https://www.npmjs.com/package/@ckeditor/@ckeditor/ckeditor5-dev-web-crawler) - -### Other changes - -* The CKEditor 5 Dev packages are now ESM. -* The `@ckeditor/jsdoc-plugins` package is no longer available as CKEditor 5 documentation uses TypeScript sources to prepare API docs. The package is no longer use by us anywhere. Hence, we decided to remove a dead code. - -### Released packages - -Check out the [Versioning policy](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html) guide for more information. - -
-Released packages (summary) - -Other releases: - -* [@ckeditor/ckeditor5-dev-build-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-build-tools/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-bump-year](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-bump-year/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-ci](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-ci/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-dependency-checker](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-dependency-checker/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-docs](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-docs/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-release-tools](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-release-tools/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-stale-bot](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-stale-bot/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-tests](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-tests/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-transifex](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-transifex/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-translations](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-translations/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-utils/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/ckeditor5-dev-web-crawler](https://www.npmjs.com/package/@ckeditor/ckeditor5-dev-web-crawler/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -* [@ckeditor/typedoc-plugins](https://www.npmjs.com/package/@ckeditor/typedoc-plugins/v/44.0.0-alpha.0): v43.0.0 => v44.0.0-alpha.0 -
- --- To see all releases, visit the [release page](https://github.com/ckeditor/ckeditor5-dev/releases). From 02148174db7ee6ad85d37a583da764da163e0963 Mon Sep 17 00:00:00 2001 From: CKEditorBot Date: Tue, 24 Sep 2024 09:29:36 +0000 Subject: [PATCH 163/172] Release: v44.0.0-alpha.5. --- package.json | 8 ++++---- packages/ckeditor5-dev-build-tools/package.json | 2 +- packages/ckeditor5-dev-bump-year/package.json | 2 +- packages/ckeditor5-dev-ci/package.json | 2 +- packages/ckeditor5-dev-dependency-checker/package.json | 4 ++-- packages/ckeditor5-dev-docs/package.json | 6 +++--- packages/ckeditor5-dev-release-tools/package.json | 4 ++-- packages/ckeditor5-dev-stale-bot/package.json | 4 ++-- packages/ckeditor5-dev-tests/package.json | 6 +++--- packages/ckeditor5-dev-transifex/package.json | 6 +++--- packages/ckeditor5-dev-translations/package.json | 2 +- packages/ckeditor5-dev-utils/package.json | 4 ++-- packages/ckeditor5-dev-web-crawler/package.json | 2 +- packages/typedoc-plugins/package.json | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index cd2dea637..81e798001 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ckeditor5-dev", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "private": true, "author": "CKSource (http://cksource.com/)", "license": "GPL-2.0-or-later", @@ -15,9 +15,9 @@ }, "type": "module", "devDependencies": { - "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.4", - "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.4", - "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-ci": "^44.0.0-alpha.5", + "@ckeditor/ckeditor5-dev-release-tools": "^44.0.0-alpha.5", + "@ckeditor/ckeditor5-dev-bump-year": "^44.0.0-alpha.5", "eslint": "^8.21.0", "eslint-config-ckeditor5": "^7.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-build-tools/package.json b/packages/ckeditor5-dev-build-tools/package.json index 1cb08b722..047f4b115 100644 --- a/packages/ckeditor5-dev-build-tools/package.json +++ b/packages/ckeditor5-dev-build-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-build-tools", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Rollup-based tools used to build CKEditor 5 packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-bump-year/package.json b/packages/ckeditor5-dev-bump-year/package.json index 8691ae7b4..51e419bab 100644 --- a/packages/ckeditor5-dev-bump-year/package.json +++ b/packages/ckeditor5-dev-bump-year/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-bump-year", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Used to bump year in the licence text specified at the top of the file.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-ci/package.json b/packages/ckeditor5-dev-ci/package.json index cc08afe22..44154192c 100644 --- a/packages/ckeditor5-dev-ci/package.json +++ b/packages/ckeditor5-dev-ci/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-ci", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Utils used on various Continuous Integration services.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-dependency-checker/package.json b/packages/ckeditor5-dev-dependency-checker/package.json index dafd4420b..1c183a12b 100644 --- a/packages/ckeditor5-dev-dependency-checker/package.json +++ b/packages/ckeditor5-dev-dependency-checker/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-dependency-checker", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Contains tools for validating dependencies specified in package.json.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-dependency-checker": "bin/dependencychecker.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.5", "chalk": "^5.0.0", "depcheck": "^1.3.1", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-docs/package.json b/packages/ckeditor5-dev-docs/package.json index 2f1678598..035147e52 100644 --- a/packages/ckeditor5-dev-docs/package.json +++ b/packages/ckeditor5-dev-docs/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-docs", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Tasks used to build and verify the documentation for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,8 +22,8 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", - "@ckeditor/typedoc-plugins": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.5", + "@ckeditor/typedoc-plugins": "^44.0.0-alpha.5", "glob": "^10.0.0", "fs-extra": "^11.0.0", "tmp": "^0.2.1", diff --git a/packages/ckeditor5-dev-release-tools/package.json b/packages/ckeditor5-dev-release-tools/package.json index edac53434..6f691b956 100644 --- a/packages/ckeditor5-dev-release-tools/package.json +++ b/packages/ckeditor5-dev-release-tools/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-release-tools", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Tools used for releasing CKEditor 5 and related packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.5", "@octokit/rest": "^21.0.0", "chalk": "^5.0.0", "cli-columns": "^4.0.0", diff --git a/packages/ckeditor5-dev-stale-bot/package.json b/packages/ckeditor5-dev-stale-bot/package.json index aeb6dc2c6..50b1cf328 100644 --- a/packages/ckeditor5-dev-stale-bot/package.json +++ b/packages/ckeditor5-dev-stale-bot/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-stale-bot", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "A stale bot is used to mark issues and pull requests that have not recently been updated.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -25,7 +25,7 @@ "ckeditor5-dev-stale-bot": "bin/stale-bot.js" }, "dependencies": { - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.5", "chalk": "^5.0.0", "date-fns": "^4.0.0", "fs-extra": "^11.0.0", diff --git a/packages/ckeditor5-dev-tests/package.json b/packages/ckeditor5-dev-tests/package.json index 7f090ea38..54472b136 100644 --- a/packages/ckeditor5-dev-tests/package.json +++ b/packages/ckeditor5-dev-tests/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-tests", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Testing environment for CKEditor 5.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -28,8 +28,8 @@ }, "dependencies": { "@babel/core": "^7.10.5", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.4", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.5", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.5", "@ckeditor/ckeditor5-inspector": "^4.0.0", "@types/chai": "^4.3.5", "@types/karma-sinon-chai": "^2.0.2", diff --git a/packages/ckeditor5-dev-transifex/package.json b/packages/ckeditor5-dev-transifex/package.json index 4a255d886..e8aec5b56 100644 --- a/packages/ckeditor5-dev-transifex/package.json +++ b/packages/ckeditor5-dev-transifex/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-transifex", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Used to download and upload translations using the Transifex service.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -24,8 +24,8 @@ "dependencies": { "fs-extra": "^11.0.0", "del": "^7.0.0", - "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.4", - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-utils": "^44.0.0-alpha.5", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.5", "chalk": "^5.0.0", "inquirer": "^11.0.0", "@transifex/api": "^7.0.0", diff --git a/packages/ckeditor5-dev-translations/package.json b/packages/ckeditor5-dev-translations/package.json index 631b2bf30..27d2f641a 100644 --- a/packages/ckeditor5-dev-translations/package.json +++ b/packages/ckeditor5-dev-translations/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-translations", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "CKEditor 5 translations plugin for webpack.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/ckeditor5-dev-utils/package.json b/packages/ckeditor5-dev-utils/package.json index a1af2ccd7..a77bb5cc6 100644 --- a/packages/ckeditor5-dev-utils/package.json +++ b/packages/ckeditor5-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-utils", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Utils for CKEditor 5 development tools packages.", "keywords": [], "author": "CKSource (http://cksource.com/)", @@ -22,7 +22,7 @@ "lib" ], "dependencies": { - "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.4", + "@ckeditor/ckeditor5-dev-translations": "^44.0.0-alpha.5", "chalk": "^5.0.0", "cli-cursor": "^5.0.0", "cli-spinners": "^3.0.0", diff --git a/packages/ckeditor5-dev-web-crawler/package.json b/packages/ckeditor5-dev-web-crawler/package.json index eba2a1345..d39afa95a 100644 --- a/packages/ckeditor5-dev-web-crawler/package.json +++ b/packages/ckeditor5-dev-web-crawler/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/ckeditor5-dev-web-crawler", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Used to run a web crawler that checks for errors on specified pages.", "keywords": [], "author": "CKSource (http://cksource.com/)", diff --git a/packages/typedoc-plugins/package.json b/packages/typedoc-plugins/package.json index 719464603..874764145 100644 --- a/packages/typedoc-plugins/package.json +++ b/packages/typedoc-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/typedoc-plugins", - "version": "44.0.0-alpha.4", + "version": "44.0.0-alpha.5", "description": "Various TypeDoc plugins developed by the CKEditor 5 team.", "keywords": [], "author": "CKSource (http://cksource.com/)", From 1083ad7555c708cb4316946eb7f3bb709eac8499 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 11:40:56 +0200 Subject: [PATCH 164/172] Internal (release-tools): Escape the "mainBranch" option. --- .../ckeditor5-dev-release-tools/lib/utils/getcommits.js | 3 ++- .../ckeditor5-dev-release-tools/tests/utils/getcommits.js | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js index e1135ce4e..c7a170c73 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js @@ -9,6 +9,7 @@ import { getRawCommitsStream } from 'git-raw-commits'; import concat from 'concat-stream'; import parserOptions from './parseroptions.js'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import shellEscape from 'shell-escape'; /** * Returns a promise that resolves an array of commits since the last tag specified as `options.from`. @@ -39,7 +40,7 @@ export default function getCommits( transformCommit, options = {} ) { } else { // Otherwise, (release branch is other than the main branch) we need to merge arrays of commits. // See: https://github.com/ckeditor/ckeditor5/issues/7492. - const baseCommit = exec( `git merge-base ${ releaseBranch } ${ mainBranch }` ).trim(); + const baseCommit = exec( `git merge-base ${ shellEscape( [ releaseBranch, mainBranch ] ) }` ).trim(); const commitPromises = [ // 1. Commits from the last release and to the point where the release branch was created (the merge-base commit). diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js index 0dcee0018..8b0824d66 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/getcommits.js @@ -9,10 +9,13 @@ import fs from 'fs'; import path from 'path'; import { getRawCommitsStream } from 'git-raw-commits'; import { tools } from '@ckeditor/ckeditor5-dev-utils'; +import shellEscape from 'shell-escape'; const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); +vi.mock( 'shell-escape' ); + describe( 'getCommits()', () => { let tmpCwd, cwd, getCommits, stubs; @@ -35,6 +38,8 @@ describe( 'getCommits()', () => { exec( 'git config user.name "CKEditor5 CI"' ); } + vi.mocked( shellEscape ).mockImplementation( input => input.map( v => `'${ v }'` ).join( ' ' ) ); + vi.doMock( 'git-raw-commits', () => ( { getRawCommitsStream: vi.fn( getRawCommitsStream ) } ) ); @@ -284,6 +289,8 @@ describe( 'getCommits()', () => { merges: undefined, firstParent: true } ); + + expect( vi.mocked( shellEscape ) ).toHaveBeenCalledExactlyOnceWith( [ 'release', 'master' ] ); } ); } ); } ); From e56763108113c6a8b8cdc145c621396d9b8c0309 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 11:48:00 +0200 Subject: [PATCH 165/172] Addressed ESLint issues. --- .eslintrc.cjs | 25 +++---------------- .../src/plugins/translations.ts | 2 +- .../tests/_utils/utils.ts | 2 +- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 7d1bb09a5..f8cf0f0a8 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -26,8 +26,7 @@ module.exports = { ], rules: { 'no-console': 'off', - 'ckeditor5-rules/require-file-extensions-in-imports': 'off', - 'mocha/no-global-tests': 'off', // TODO: remove when all mocha tests are removed. + 'mocha/no-global-tests': 'off', 'ckeditor5-rules/license-header': [ 'error', { headerLines: [ '/**', @@ -40,28 +39,10 @@ module.exports = { overrides: [ { files: [ - // TODO: add packages as they are migrated to ESM. - './scripts/**/*', - './packages/ckeditor5-dev-tests/**/*', - './packages/ckeditor5-dev-utils/**/*', - './packages/ckeditor5-dev-translations/**/*', - './packages/ckeditor5-dev-release-tools/**/*', - './packages/ckeditor5-dev-bump-year/**/*', - './packages/ckeditor5-dev-dependency-checker/**/*', - './packages/ckeditor5-dev-stale-bot/**/*', - './packages/ckeditor5-dev-transifex/**/*', - './packages/ckeditor5-dev-ci/**/*', - './packages/ckeditor5-dev-web-crawler/**/*', - './packages/ckeditor5-dev-docs/**/*' + './packages/typedoc-plugins/**/*' ], rules: { - 'mocha/no-global-tests': 'error', - 'ckeditor5-rules/require-file-extensions-in-imports': [ - 'error', - { - extensions: [ '.ts', '.js', '.json' ] - } - ] + 'ckeditor5-rules/require-file-extensions-in-imports': 'off', } } ] diff --git a/packages/ckeditor5-dev-build-tools/src/plugins/translations.ts b/packages/ckeditor5-dev-build-tools/src/plugins/translations.ts index 1a53b59e5..5abb33aac 100644 --- a/packages/ckeditor5-dev-build-tools/src/plugins/translations.ts +++ b/packages/ckeditor5-dev-build-tools/src/plugins/translations.ts @@ -11,7 +11,7 @@ import PO from 'pofile'; import { groupBy, merge } from 'lodash-es'; import { glob } from 'glob'; import type { Plugin } from 'rollup'; -import { removeWhitespace } from '../utils'; +import { removeWhitespace } from '../utils.js'; const TYPINGS = removeWhitespace( ` import type { Translations } from '@ckeditor/ckeditor5-utils'; diff --git a/packages/ckeditor5-dev-build-tools/tests/_utils/utils.ts b/packages/ckeditor5-dev-build-tools/tests/_utils/utils.ts index 52873193b..75688e0a1 100644 --- a/packages/ckeditor5-dev-build-tools/tests/_utils/utils.ts +++ b/packages/ckeditor5-dev-build-tools/tests/_utils/utils.ts @@ -6,7 +6,7 @@ import { expect, vi } from 'vitest'; import swc from '@rollup/plugin-swc'; import type { RollupOutput, OutputChunk, OutputAsset, Plugin } from 'rollup'; -import * as utils from '../../src/utils'; +import * as utils from '../../src/utils.js'; /** * Helper function for validating Rollup asset. From 170d8c9d4f9caf9e04c8a632cee75137fc2b529b Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Tue, 24 Sep 2024 12:21:06 +0200 Subject: [PATCH 166/172] Lowercase simple types in JSDoc. --- .../ckeditor5-dev-bump-year/lib/bumpyear.js | 12 +- .../bin/circle-workflow-notifier.js | 8 +- .../lib/circle-update-auto-cancel-builds.js | 10 +- .../ckeditor5-dev-ci/lib/format-message.js | 62 ++++---- .../lib/is-job-triggered-by-member.js | 14 +- .../lib/process-job-statuses.js | 8 +- .../lib/trigger-circle-build.js | 14 +- .../lib/utils/get-job-approver.js | 8 +- .../bin/dependencychecker.js | 12 +- .../lib/checkdependencies.js | 60 ++++---- packages/ckeditor5-dev-docs/lib/build.js | 22 +-- .../lib/validators/fires-validator/index.js | 4 +- .../lib/validators/index.js | 6 +- .../lib/validators/link-validator/index.js | 4 +- .../lib/validators/module-validator/index.js | 4 +- .../validators/overloads-validator/index.js | 4 +- .../lib/validators/see-validator/index.js | 4 +- packages/ckeditor5-dev-docs/tests/_utils.js | 4 +- .../lib/tasks/cleanuppackages.js | 44 +++--- .../lib/tasks/commitandtag.js | 8 +- .../lib/tasks/creategithubrelease.js | 22 +-- .../generatechangelogformonorepository.js | 108 +++++++------- .../generatechangelogforsinglepackage.js | 22 +-- .../lib/tasks/preparerepository.js | 38 ++--- .../lib/tasks/publishpackages.js | 20 +-- .../lib/tasks/push.js | 8 +- .../lib/tasks/reassignnpmtags.js | 16 +- .../lib/tasks/updatedependencies.js | 28 ++-- .../lib/tasks/updateversions.js | 36 ++--- .../tasks/verifypackagespublishedcorrectly.js | 8 +- .../lib/utils/assertfilestopublish.js | 12 +- .../lib/utils/assertnpmauthorization.js | 2 +- .../lib/utils/assertnpmtag.js | 8 +- .../lib/utils/assertpackages.js | 8 +- .../lib/utils/checkversionavailability.js | 4 +- .../lib/utils/configurereleaseoptions.js | 2 +- .../lib/utils/confirmincludingpackage.js | 2 +- .../lib/utils/confirmnpmtag.js | 6 +- .../lib/utils/displaycommits.js | 8 +- .../lib/utils/executeinparallel.js | 36 ++--- .../lib/utils/generatechangelog.js | 46 +++--- .../lib/utils/getchangedfilesforcommit.js | 4 +- .../lib/utils/getchangelog.js | 4 +- .../lib/utils/getchangesforversion.js | 6 +- .../lib/utils/getcommits.js | 10 +- .../lib/utils/getformatteddate.js | 2 +- .../lib/utils/getnewversiontype.js | 2 +- .../lib/utils/getnpmtagfromversion.js | 4 +- .../lib/utils/getpackagejson.js | 4 +- .../lib/utils/getpackagespaths.js | 18 +-- .../lib/utils/getwriteroptions.js | 2 +- .../lib/utils/isversionpublishablefortag.js | 8 +- .../lib/utils/parallelworker.js | 4 +- .../providenewversionformonorepository.js | 12 +- .../lib/utils/providetoken.js | 2 +- .../lib/utils/provideversion.js | 24 +-- .../lib/utils/publishpackageonnpmcallback.js | 6 +- .../lib/utils/savechangelog.js | 4 +- .../lib/utils/transformcommitfactory.js | 54 +++---- .../lib/utils/transformcommitutils.js | 26 ++-- .../lib/utils/truncatechangelog.js | 4 +- .../lib/utils/validaterepositorytorelease.js | 12 +- .../lib/utils/versions.js | 28 ++-- .../tests/utils/generatechangelog.js | 2 +- .../ckeditor5-dev-stale-bot/bin/stale-bot.js | 18 +-- .../bin/utils/createspinner.js | 2 +- .../bin/utils/parsearguments.js | 8 +- .../bin/utils/parseconfig.js | 80 +++++----- .../lib/githubrepository.js | 122 ++++++++-------- .../lib/utils/findstaledate.js | 2 +- .../lib/utils/isissueorpullrequestactive.js | 4 +- .../lib/utils/isissueorpullrequesttoclose.js | 2 +- .../lib/utils/isissueorpullrequesttostale.js | 2 +- .../utils/isissueorpullrequesttounstale.js | 2 +- .../lib/utils/ispendingissuestale.js | 2 +- .../lib/utils/ispendingissuetostale.js | 2 +- .../lib/utils/ispendingissuetounlabel.js | 2 +- .../lib/utils/preparesearchquery.js | 12 +- .../lib/tasks/runmanualtests.js | 26 ++-- .../automated-tests/assertions/attribute.js | 4 +- .../assertions/equal-markup.js | 2 +- .../utils/automated-tests/getkarmaconfig.js | 8 +- .../utils/automated-tests/getwebpackconfig.js | 4 +- .../utils/automated-tests/parsearguments.js | 26 ++-- .../lib/utils/getdefinitionsfromfile.js | 8 +- .../lib/utils/getrelativefilepath.js | 6 +- .../utils/manual-tests/compilehtmlfiles.js | 26 ++-- .../lib/utils/manual-tests/compilescripts.js | 22 +-- .../lib/utils/manual-tests/createserver.js | 14 +- .../utils/manual-tests/getwebpackconfig.js | 24 +-- .../lib/utils/manual-tests/removedir.js | 6 +- .../manual-tests/webpacknotifierplugin.js | 6 +- .../lib/utils/requiredll.js | 4 +- .../utils/transformfileoptiontotestglob.js | 30 ++-- .../lib/createpotfiles.js | 78 +++++----- .../ckeditor5-dev-transifex/lib/download.js | 52 +++---- .../ckeditor5-dev-transifex/lib/gettoken.js | 2 +- .../lib/transifexservice.js | 94 ++++++------ .../ckeditor5-dev-transifex/lib/upload.js | 26 ++-- packages/ckeditor5-dev-transifex/lib/utils.js | 6 +- .../ckeditor5-dev-transifex/tests/upload.js | 10 +- .../lib/ckeditortranslationsplugin.js | 36 ++--- .../lib/cleanpofilecontent.js | 8 +- .../lib/createdictionaryfrompofilecontent.js | 2 +- .../lib/findmessages.js | 14 +- .../lib/multiplelanguagetranslationservice.js | 86 +++++------ .../lib/servetranslations.js | 38 ++--- .../lib/translatesourceloader.js | 4 +- .../lib/builds/getdllpluginwebpackconfig.js | 26 ++-- .../lib/loaders/ck-debug-loader.js | 2 +- .../lib/loaders/getcoverageloader.js | 10 +- .../lib/loaders/getdebugloader.js | 4 +- .../lib/loaders/getformattedtextloader.js | 2 +- .../lib/loaders/geticonsloader.js | 6 +- .../lib/loaders/getjavascriptloader.js | 6 +- .../lib/loaders/getstylesloader.js | 14 +- .../lib/loaders/gettypescriptloader.js | 10 +- .../ckeditor5-dev-utils/lib/logger/index.js | 14 +- .../lib/styles/getpostcssconfig.js | 8 +- .../lib/styles/themeimporter.js | 10 +- .../lib/styles/utils/getpackagename.js | 4 +- .../lib/tools/createspinner.js | 26 ++-- .../lib/tools/getdirectories.js | 2 +- .../ckeditor5-dev-utils/lib/tools/shexec.js | 22 +-- .../lib/tools/updatejsonfile.js | 4 +- .../lib/runcrawler.js | 138 +++++++++--------- .../ckeditor5-dev-web-crawler/lib/spinner.js | 10 +- .../ckeditor5-dev-web-crawler/lib/utils.js | 8 +- .../lib/purge-private-api-docs/index.js | 28 ++-- .../typedoc-plugins/lib/symbol-fixer/index.js | 4 +- .../typedoc-plugins/lib/tag-error/index.js | 2 +- .../typedoc-plugins/lib/tag-event/index.js | 8 +- .../lib/tag-observable/index.js | 8 +- packages/typedoc-plugins/lib/utils/index.js | 20 +-- .../fixtures/class-a.ts | 2 +- .../fixtures/interface-a.ts | 2 +- .../event-param-fixer/fixtures/example.ts | 2 +- .../tests/symbol-fixer/fixtures/fake.ts | 2 +- .../tests/symbol-fixer/fixtures/iterator.ts | 2 +- .../tests/tag-error/fixtures/customerror.ts | 12 +- .../fixtures/customexampleclass.ts | 2 +- packages/typedoc-plugins/tests/utils.js | 2 +- scripts/ci/generate-circleci-configuration.js | 26 ++-- scripts/runtest.js | 8 +- scripts/utils/getlistroptions.js | 2 +- scripts/utils/parsearguments.js | 18 +-- scripts/utils/runbuildcommand.js | 2 +- 147 files changed, 1202 insertions(+), 1202 deletions(-) diff --git a/packages/ckeditor5-dev-bump-year/lib/bumpyear.js b/packages/ckeditor5-dev-bump-year/lib/bumpyear.js index 362375b5b..b91a7cbd2 100644 --- a/packages/ckeditor5-dev-bump-year/lib/bumpyear.js +++ b/packages/ckeditor5-dev-bump-year/lib/bumpyear.js @@ -16,11 +16,11 @@ import { globSync } from 'glob'; * With: * Copyright (c) [initial year]-[current year] * - * @param {Object} params - * @param {String} params.cwd Current working directory from which all paths will be resolved. - * @param {Array} params.globPatterns An array of objects, where each object has string property `pattern`, + * @param {object} params + * @param {string} params.cwd Current working directory from which all paths will be resolved. + * @param {Array.} params.globPatterns An array of objects, where each object has string property `pattern`, * and optionally `options` property for this `glob` pattern. - * @param {String} [params.initialYear='2003'] Year from which the licenses should begin. + * @param {string} [params.initialYear='2003'] Year from which the licenses should begin. */ export default function bumpYear( params ) { if ( !params.initialYear ) { @@ -95,8 +95,8 @@ export default function bumpYear( params ) { /** * License headers are only required in JS and TS files. * - * @param {String} fileName - * @returns {Boolean} + * @param {string} fileName + * @returns {boolean} */ function isLicenseHeaderRequired( fileName ) { if ( fileName.endsWith( '.js' ) ) { diff --git a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js index fc73e8e39..3d6382c6b 100755 --- a/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js +++ b/packages/ckeditor5-dev-ci/bin/circle-workflow-notifier.js @@ -118,10 +118,10 @@ async function getOtherJobsData() { } /** - * @param {Array.} args - * @returns {Object} result - * @returns {String} result.task - * @returns {Array} result.ignore + * @param {Array.} args + * @returns {object} result + * @returns {string} result.task + * @returns {Array.} result.ignore */ function parseArguments( args ) { const config = { diff --git a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js index cde1d1501..351d115b7 100644 --- a/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js +++ b/packages/ckeditor5-dev-ci/lib/circle-update-auto-cancel-builds.js @@ -5,11 +5,11 @@ /** * @param options - * @param {String} options.circleToken - * @param {String} options.githubOrganization - * @param {String} options.githubRepository - * @param {Boolean} options.newValue - * @return {Promise.} + * @param {string} options.circleToken + * @param {string} options.githubOrganization + * @param {string} options.githubRepository + * @param {boolean} options.newValue + * @returns {Promise.} */ export default async function circleUpdateAutoCancelBuilds( options ) { const { diff --git a/packages/ckeditor5-dev-ci/lib/format-message.js b/packages/ckeditor5-dev-ci/lib/format-message.js index ce9758ad3..1d69bc66e 100644 --- a/packages/ckeditor5-dev-ci/lib/format-message.js +++ b/packages/ckeditor5-dev-ci/lib/format-message.js @@ -10,20 +10,20 @@ import { bots, members } from './data/index.js'; const REPOSITORY_REGEXP = /github\.com\/([^/]+)\/([^/]+)/; /** - * @param {Object} options - * @param {String} options.slackMessageUsername - * @param {String} options.iconUrl - * @param {String} options.repositoryOwner - * @param {String} options.repositoryName - * @param {String} options.branch - * @param {String} options.buildTitle - * @param {String} options.buildUrl - * @param {String} options.buildId - * @param {String} options.githubToken - * @param {String} options.triggeringCommitUrl - * @param {Number} options.startTime - * @param {Number} options.endTime - * @param {Boolean} options.shouldHideAuthor + * @param {object} options + * @param {string} options.slackMessageUsername + * @param {string} options.iconUrl + * @param {string} options.repositoryOwner + * @param {string} options.repositoryName + * @param {string} options.branch + * @param {string} options.buildTitle + * @param {string} options.buildUrl + * @param {string} options.buildId + * @param {string} options.githubToken + * @param {string} options.triggeringCommitUrl + * @param {number} options.startTime + * @param {number} options.endTime + * @param {boolean} options.shouldHideAuthor */ export default async function formatMessage( options ) { const commitDetails = await getCommitDetails( options.triggeringCommitUrl, options.githubToken ); @@ -64,11 +64,11 @@ export default async function formatMessage( options ) { /** * Returns the additional message that will be added to the notifier post. * - * @param {Object} options - * @param {Boolean} options.shouldHideAuthor - * @param {String|null} options.githubAccount - * @param {String} options.commitAuthor - * @returns {String} + * @param {object} options + * @param {boolean} options.shouldHideAuthor + * @param {string|null} options.githubAccount + * @param {string} options.commitAuthor + * @returns {string} */ function getNotifierMessage( options ) { if ( options.shouldHideAuthor ) { @@ -95,8 +95,8 @@ function getNotifierMessage( options ) { } /** - * @param {String|null} githubAccount - * @returns {String|null} + * @param {string|null} githubAccount + * @returns {string|null} */ function findSlackAccount( githubAccount ) { if ( !githubAccount ) { @@ -116,9 +116,9 @@ function findSlackAccount( githubAccount ) { * Returns string representing amount of time passed between two timestamps. * Timestamps should be in seconds instead of milliseconds. * - * @param {Number} startTime - * @param {Number} endTime - * @returns {String} + * @param {number} startTime + * @param {number} endTime + * @returns {string} */ function getExecutionTime( startTime, endTime ) { if ( !startTime || !endTime ) { @@ -155,8 +155,8 @@ function getExecutionTime( startTime, endTime ) { /** * Replaces `#Id` and `Repo/Owner#Id` with URls to Github Issues. * - * @param {String} commitMessage - * @param {String} triggeringCommitUrl + * @param {string} commitMessage + * @param {string} triggeringCommitUrl * @returns {string} */ function getFormattedMessage( commitMessage, triggeringCommitUrl ) { @@ -178,9 +178,9 @@ function getFormattedMessage( commitMessage, triggeringCommitUrl ) { /** * Returns a promise that resolves the commit details (author and message) based on the specified GitHub URL. * - * @param {String} triggeringCommitUrl The URL to the commit on GitHub. - * @param {String} githubToken Github token used for authorization a request, - * @returns {Promise.} + * @param {string} triggeringCommitUrl The URL to the commit on GitHub. + * @param {string} githubToken Github token used for authorization a request, + * @returns {Promise.} */ function getCommitDetails( triggeringCommitUrl, githubToken ) { const apiGithubUrlCommit = getGithubApiUrl( triggeringCommitUrl ); @@ -205,8 +205,8 @@ function getCommitDetails( triggeringCommitUrl, githubToken ) { /** * Returns a URL to GitHub API which returns details of the commit that caused the CI to fail its job. * - * @param {String} triggeringCommitUrl The URL to the commit on GitHub. - * @returns {String} + * @param {string} triggeringCommitUrl The URL to the commit on GitHub. + * @returns {string} */ function getGithubApiUrl( triggeringCommitUrl ) { return triggeringCommitUrl.replace( 'github.com/', 'api.github.com/repos/' ).replace( '/commit/', '/commits/' ); diff --git a/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js b/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js index e50ff62f1..2c9ead7d4 100644 --- a/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js +++ b/packages/ckeditor5-dev-ci/lib/is-job-triggered-by-member.js @@ -8,13 +8,13 @@ import getJobApprover from './utils/get-job-approver.js'; /** * @param options - * @param {String} options.circleToken - * @param {String} options.circleWorkflowId - * @param {String} options.circleApprovalJobName - * @param {String} options.githubOrganization - * @param {String} options.githubTeamSlug - * @param {String} options.githubToken - * @return {Promise.} + * @param {string} options.circleToken + * @param {string} options.circleWorkflowId + * @param {string} options.circleApprovalJobName + * @param {string} options.githubOrganization + * @param {string} options.githubTeamSlug + * @param {string} options.githubToken + * @returns {Promise.} */ export default async function isJobTriggeredByMember( options ) { const { diff --git a/packages/ckeditor5-dev-ci/lib/process-job-statuses.js b/packages/ckeditor5-dev-ci/lib/process-job-statuses.js index 64051a60d..4444d6959 100644 --- a/packages/ckeditor5-dev-ci/lib/process-job-statuses.js +++ b/packages/ckeditor5-dev-ci/lib/process-job-statuses.js @@ -57,7 +57,7 @@ export default function processJobStatuses( jobs ) { /** * @param {WorkflowJob} job - * @returns {Boolean} + * @returns {boolean} */ function isJobFailed( job ) { if ( job.status === 'failed' ) { @@ -81,11 +81,11 @@ function clone( obj ) { } /** - * @typedef {Object} WorkflowJob + * @typedef {object} WorkflowJob * - * @property {String} id + * @property {string} id * * @property {'blocked'|'running'|'failed'|'failed_parent'|'success'} status * - * @property {Array.} dependencies + * @property {Array.} dependencies */ diff --git a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js index 76c368799..a30ba7137 100644 --- a/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js +++ b/packages/ckeditor5-dev-ci/lib/trigger-circle-build.js @@ -7,13 +7,13 @@ /** * @param options - * @param {String} options.circleToken - * @param {String} options.commit - * @param {String} options.branch - * @param {String} options.repositorySlug A repository slug (org/name) where a new build will be started. - * @param {String|null} [options.releaseBranch=null] Define a branch that leads the release process. - * @param {String|null} [options.triggerRepositorySlug=null] A repository slug (org/name) that triggers a new build. - * @return {Promise} + * @param {string} options.circleToken + * @param {string} options.commit + * @param {string} options.branch + * @param {string} options.repositorySlug A repository slug (org/name) where a new build will be started. + * @param {string|null} [options.releaseBranch=null] Define a branch that leads the release process. + * @param {string|null} [options.triggerRepositorySlug=null] A repository slug (org/name) that triggers a new build. + * @returns {Promise} */ export default async function triggerCircleBuild( options ) { const { diff --git a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js index daed11455..9eb2b4175 100644 --- a/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js +++ b/packages/ckeditor5-dev-ci/lib/utils/get-job-approver.js @@ -6,10 +6,10 @@ /** * Returns a promise that resolves to GitHub name of a developer who approved the `jobName` job. * - * @param {String} circleCiToken - * @param {String} workflowId - * @param {String} jobName - * @returns {Promise.} + * @param {string} circleCiToken + * @param {string} workflowId + * @param {string} jobName + * @returns {Promise.} */ export default async function getJobApprover( circleCiToken, workflowId, jobName ) { const circleRequestOptions = { diff --git a/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js b/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js index d231072ef..dedd1bd4b 100755 --- a/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js +++ b/packages/ckeditor5-dev-dependency-checker/bin/dependencychecker.js @@ -22,10 +22,10 @@ checkDependencies( packagePaths, options ) /** * Parses CLI arguments and options. * - * @param {Array.} args CLI arguments containing package paths and options. - * @returns {Object} result - * @returns {Set.} result.packagePaths Relative package paths. - * @returns {Object.} result.options Configuration options. + * @param {Array.} args CLI arguments containing package paths and options. + * @returns {object} result + * @returns {Set.} result.packagePaths Relative package paths. + * @returns {Object.} result.options Configuration options. */ function parseArguments( args ) { const config = { @@ -56,8 +56,8 @@ function parseArguments( args ) { * Returns relative (to the current work directory) paths to packages. If the provided `args` array is empty, * the packages will be read from the `packages/` directory. * - * @param {Array.} args CLI arguments with relative or absolute package paths. - * @returns {Set.} Relative package paths. + * @param {Array.} args CLI arguments with relative or absolute package paths. + * @returns {Set.} Relative package paths. */ function getPackagePaths( args ) { if ( !args.length ) { diff --git a/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js b/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js index 6dc5a4c2a..abe80870c 100644 --- a/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js +++ b/packages/ckeditor5-dev-dependency-checker/lib/checkdependencies.js @@ -12,10 +12,10 @@ import chalk from 'chalk'; /** * Checks dependencies sequentially in all provided packages. * - * @param {Set.} packagePaths Relative paths to packages. - * @param {Object} options Options. - * @param {Boolean} [options.quiet=false] Whether to inform about the progress. - * @returns {Promise.} Resolves a promise with a flag informing whether detected an error. + * @param {Set.} packagePaths Relative paths to packages. + * @param {object} options Options. + * @param {boolean} [options.quiet=false] Whether to inform about the progress. + * @returns {Promise.} Resolves a promise with a flag informing whether detected an error. */ export default async function checkDependencies( packagePaths, options ) { let foundError = false; @@ -37,10 +37,10 @@ export default async function checkDependencies( packagePaths, options ) { /** * Checks dependencies in provided package. If the folder does not contain a package.json file the function quits with success. * - * @param {String} packagePath Relative path to package. - * @param {Object} options Options. - * @param {Boolean} [options.quiet=false] Whether to inform about the progress. - * @returns {Promise.} The result of checking the dependencies in the package: true = no errors found. + * @param {string} packagePath Relative path to package. + * @param {object} options Options. + * @param {boolean} [options.quiet=false] Whether to inform about the progress. + * @returns {Promise.} The result of checking the dependencies in the package: true = no errors found. */ async function checkDependenciesInPackage( packagePath, options ) { const packageAbsolutePath = upath.resolve( packagePath ); @@ -161,7 +161,7 @@ async function checkDependenciesInPackage( packagePath, options ) { * Returns an array that contains list of files that import modules using full package name instead of relative path. * * @param repositoryPath An absolute path to the directory which should be checked. - * @returns {Array.} + * @returns {Array.} */ function getInvalidItselfImports( repositoryPath ) { const packageJson = fs.readJsonSync( upath.join( repositoryPath, 'package.json' ) ); @@ -193,9 +193,9 @@ function getInvalidItselfImports( repositoryPath ) { /** * Groups missing dependencies returned by `depcheck` as `dependencies` or `devDependencies`. * - * @param {Object} missingPackages The `missing` value from object returned by `depcheck`. - * @param {String} currentPackage Name of current package. - * @returns {Promise.>>} + * @param {object} missingPackages The `missing` value from object returned by `depcheck`. + * @param {string} currentPackage Name of current package. + * @returns {Promise.>>} */ async function groupMissingPackages( missingPackages, currentPackage ) { delete missingPackages[ currentPackage ]; @@ -220,9 +220,9 @@ async function groupMissingPackages( missingPackages, currentPackage ) { * Checks whether all packages that have been imported by the CSS file are defined in `package.json` as `dependencies`. * Returned array contains list of used packages. * - * @param {String} filePath An absolute path to the checking file. - * @param {Function} onMissingCSSFile Error handler called when a CSS file is not found. - * @returns {Array.|undefined} + * @param {string} filePath An absolute path to the checking file. + * @param {function} onMissingCSSFile Error handler called when a CSS file is not found. + * @returns {Array.|undefined} */ function parsePostCSS( filePath, onMissingCSSFile ) { const fileContent = fs.readFileSync( filePath, 'utf-8' ); @@ -288,9 +288,9 @@ function parsePostCSS( filePath, onMissingCSSFile ) { * Checks whether packages specified as `devDependencies` are not duplicated with items defined as `dependencies`. * * @see https://github.com/ckeditor/ckeditor5/issues/7706#issuecomment-665569410 - * @param {Object|undefined} dependencies - * @param {Object|undefined} devDependencies - * @returns {Array.} + * @param {object|undefined} dependencies + * @param {object|undefined} devDependencies + * @returns {Array.} */ function findDuplicatedDependencies( dependencies, devDependencies ) { const deps = Object.keys( dependencies || {} ); @@ -318,11 +318,11 @@ function findDuplicatedDependencies( dependencies, devDependencies ) { * verifies wrongly placed ones. * * @see https://github.com/ckeditor/ckeditor5/issues/8817#issuecomment-759353134 - * @param {Object|undefined} options.dependencies Defined dependencies from package.json. - * @param {Object|undefined} options.devDependencies Defined development dependencies from package.json. - * @param {Object} options.dependenciesToCheck All dependencies that have been found and files where they are used. - * @param {Array} options.dependenciesToIgnore An array of package names that should not be checked. - * @returns {Promise.>} Misplaced packages. Each array item is an object containing + * @param {object|undefined} options.dependencies Defined dependencies from package.json. + * @param {object|undefined} options.devDependencies Defined development dependencies from package.json. + * @param {object} options.dependenciesToCheck All dependencies that have been found and files where they are used. + * @param {Array.} options.dependenciesToIgnore An array of package names that should not be checked. + * @returns {Promise.>} Misplaced packages. Each array item is an object containing * the `description` string and `packageNames` array of strings. */ async function findMisplacedDependencies( options ) { @@ -372,9 +372,9 @@ async function findMisplacedDependencies( options ) { * Checks if a given package is a development-only dependency. Package is considered a dev dependency * if it is used only in files that are not used in the final build, such as tests, demos or typings. * - * @param {String} packageName - * @param {Array.} absolutePaths Files where a given package has been imported. - * @returns {Promise.} + * @param {string} packageName + * @param {Array.} absolutePaths Files where a given package has been imported. + * @returns {Promise.} */ async function isDevDependency( packageName, absolutePaths ) { if ( packageName.startsWith( '@types/' ) ) { @@ -431,9 +431,9 @@ async function isDevDependency( packageName, absolutePaths ) { /** * Parses TS file from `absolutePath` and returns a list of import and export types from `packageName`. * - * @param {String} packageName - * @param {String} absolutePath File where a given package has been imported. - * @returns {Promise.>} Array of import kinds. + * @param {string} packageName + * @param {string} absolutePath File where a given package has been imported. + * @returns {Promise.>} Array of import kinds. */ async function getImportAndExportKinds( packageName, absolutePath ) { const astContent = await depCheck.parser.typescript( absolutePath ); @@ -457,7 +457,7 @@ async function getImportAndExportKinds( packageName, absolutePath ) { /** * Displays all found errors. * - * @param {Array.} data Collection of errors. + * @param {Array.} data Collection of errors. */ function showErrors( data ) { if ( data[ 0 ] ) { diff --git a/packages/ckeditor5-dev-docs/lib/build.js b/packages/ckeditor5-dev-docs/lib/build.js index 22312aa54..e54f406a1 100644 --- a/packages/ckeditor5-dev-docs/lib/build.js +++ b/packages/ckeditor5-dev-docs/lib/build.js @@ -94,29 +94,29 @@ export default async function build( config ) { } /** - * @typedef {Object} TypedocConfig + * @typedef {object} TypedocConfig * - * @property {Object} config + * @property {object} config * - * @property {String} cwd + * @property {string} cwd * - * @property {String} tsconfig + * @property {string} tsconfig * - * @property {Array.} sourceFiles Glob pattern with source files. + * @property {Array.} sourceFiles Glob pattern with source files. * - * @property {Array.} [ignoreFiles=[]] Glob pattern with files to ignore. + * @property {Array.} [ignoreFiles=[]] Glob pattern with files to ignore. * - * @property {Boolean} [strict=false] If `true`, errors found during the validation will finish the process + * @property {boolean} [strict=false] If `true`, errors found during the validation will finish the process * and exit code will be changed to `1`. - * @property {String} [outputPath] A path to the place where extracted doclets will be saved. Is an optional value due to tests. + * @property {string} [outputPath] A path to the place where extracted doclets will be saved. Is an optional value due to tests. * - * @property {String} [extraPlugins=[]] An array of path to extra plugins that will be added to Typedoc. + * @property {string} [extraPlugins=[]] An array of path to extra plugins that will be added to Typedoc. * * @property {TypedocValidator} [validatorOptions={}] An optional configuration object for validator. */ /** - * @typedef {Object} TypedocValidator + * @typedef {object} TypedocValidator * - * @property {Boolean} [enableOverloadValidator=false] If set to `true`, the overloads validator will be enabled. + * @property {boolean} [enableOverloadValidator=false] If set to `true`, the overloads validator will be enabled. */ diff --git a/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js index 46f36aaca..ab11d66e6 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/fires-validator/index.js @@ -11,8 +11,8 @@ import typedocPlugins from '@ckeditor/typedoc-plugins'; * * It checks if the event in the "@fires" tag exists. * - * @param {Object} project Generated output from TypeDoc to validate. - * @param {Function} onError A callback that is executed when a validation error is detected. + * @param {object} project Generated output from TypeDoc to validate. + * @param {function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { const { utils } = typedocPlugins; diff --git a/packages/ckeditor5-dev-docs/lib/validators/index.js b/packages/ckeditor5-dev-docs/lib/validators/index.js index 90947e59a..71a14bb63 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/index.js @@ -13,10 +13,10 @@ import overloadsValidator from './overloads-validator/index.js'; /** * Validates the CKEditor 5 documentation. * - * @param {Object} project Generated output from TypeDoc to validate. - * @param {Object} typeDoc A TypeDoc application instance. + * @param {object} project Generated output from TypeDoc to validate. + * @param {object} typeDoc A TypeDoc application instance. * @param {TypedocValidator} [options={}] A configuration object. - * @returns {Boolean} + * @returns {boolean} */ export default { validate( project, typeDoc, options = {} ) { diff --git a/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js index 8db19a347..fb0805036 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/link-validator/index.js @@ -11,8 +11,8 @@ import typedocPlugins from '@ckeditor/typedoc-plugins'; * * It checks if the identifier in the "@link" tag points to an existing doclet. * - * @param {Object} project Generated output from TypeDoc to validate. - * @param {Function} onError A callback that is executed when a validation error is detected. + * @param {object} project Generated output from TypeDoc to validate. + * @param {function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { const { utils } = typedocPlugins; diff --git a/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js index 8512b21c4..a9fd913d4 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/module-validator/index.js @@ -13,8 +13,8 @@ const AUGMENTATION_MODULE_REGEXP = /[^\\/]+[\\/]src[\\/]augmentation/; * * It checks if the module name matches the path to the file where the module is defined. * - * @param {Object} project Generated output from TypeDoc to validate. - * @param {Function} onError A callback that is executed when a validation error is detected. + * @param {object} project Generated output from TypeDoc to validate. + * @param {function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { const { utils } = typedocPlugins; diff --git a/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js index 2eaa895a8..b56fdcfa2 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/overloads-validator/index.js @@ -16,8 +16,8 @@ import typedocPlugins from '@ckeditor/typedoc-plugins'; * * Also, it prevents using the same name twice for overloaded structures. * - * @param {Object} project Generated output from TypeDoc to validate. - * @param {Function} onError A callback that is executed when a validation error is detected. + * @param {object} project Generated output from TypeDoc to validate. + * @param {function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { const { utils } = typedocPlugins; diff --git a/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js b/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js index 0c063851a..88d090c15 100644 --- a/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js +++ b/packages/ckeditor5-dev-docs/lib/validators/see-validator/index.js @@ -11,8 +11,8 @@ import typedocPlugins from '@ckeditor/typedoc-plugins'; * * It checks if the identifier in the "@see" tag points to an existing doclet. * - * @param {Object} project Generated output from TypeDoc to validate. - * @param {Function} onError A callback that is executed when a validation error is detected. + * @param {object} project Generated output from TypeDoc to validate. + * @param {function} onError A callback that is executed when a validation error is detected. */ export default function validate( project, onError ) { const { utils } = typedocPlugins; diff --git a/packages/ckeditor5-dev-docs/tests/_utils.js b/packages/ckeditor5-dev-docs/tests/_utils.js index 02563b3b5..99ffec142 100644 --- a/packages/ckeditor5-dev-docs/tests/_utils.js +++ b/packages/ckeditor5-dev-docs/tests/_utils.js @@ -7,7 +7,7 @@ * Replaces Windows style paths to Unix. * * @param value - * @returns {String} + * @returns {string} */ function normalizePath( ...value ) { return value.join( '/' ).replace( /\\/g, '/' ); @@ -17,7 +17,7 @@ function normalizePath( ...value ) { * Returns the source file path with line number from a reflection. * * @param {import('typedoc').Reflection} reflection - * @returns {String} + * @returns {string} */ function getSource( reflection ) { if ( reflection.sources ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js b/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js index 17dbb607a..7b33fe1d9 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/cleanuppackages.js @@ -19,12 +19,12 @@ import { glob } from 'glob'; * - file pointed by the `types` field from `package.json` * - Removes unnecessary fields from the `package.json` file. * - * @param {Object} options - * @param {String} options.packagesDirectory Relative path to a location of packages to be cleaned up. - * @param {Array.|PackageJsonFieldsToRemoveCallback} [options.packageJsonFieldsToRemove] Fields to remove from `package.json`. + * @param {object} options + * @param {string} options.packagesDirectory Relative path to a location of packages to be cleaned up. + * @param {Array.|PackageJsonFieldsToRemoveCallback} [options.packageJsonFieldsToRemove] Fields to remove from `package.json`. * If not set, a predefined list is used. If the callback is used, the first argument is the list with defaults. - * @param {Boolean} [options.preservePostInstallHook] Whether to preserve the postinstall hook in `package.json`. - * @param {String} [options.cwd] Current working directory from which all paths will be resolved. + * @param {boolean} [options.preservePostInstallHook] Whether to preserve the postinstall hook in `package.json`. + * @param {string} [options.cwd] Current working directory from which all paths will be resolved. * @returns {Promise} */ export default async function cleanUpPackages( options ) { @@ -50,12 +50,12 @@ export default async function cleanUpPackages( options ) { /** * Prepares the configuration options for the script. * - * @param {Object} options - * @param {String} options.packagesDirectory - * @param {Array.|PackageJsonFieldsToRemoveCallback} [options.packageJsonFieldsToRemove=DefaultFieldsToRemove] - * @param {Boolean} [options.preservePostInstallHook] - * @param {String} [options.cwd=process.cwd()] - * @returns {Object} + * @param {object} options + * @param {string} options.packagesDirectory + * @param {Array.|PackageJsonFieldsToRemoveCallback} [options.packageJsonFieldsToRemove=DefaultFieldsToRemove] + * @param {boolean} [options.preservePostInstallHook] + * @param {string} [options.cwd=process.cwd()] + * @returns {object} */ function parseOptions( options ) { const defaultPackageJsonFieldsToRemove = [ 'devDependencies', 'depcheckIgnore', 'scripts', 'private' ]; @@ -79,8 +79,8 @@ function parseOptions( options ) { /** * Removes unnecessary files and directories from the package directory. * - * @param {Object} packageJson - * @param {String} packagePath + * @param {object} packageJson + * @param {string} packagePath * @returns {Promise} */ async function cleanUpPackageDirectory( packageJson, packagePath ) { @@ -129,8 +129,8 @@ async function cleanUpPackageDirectory( packageJson, packagePath ) { /** * Creates an array of patterns to ignore for the `glob` calls. * - * @param {Object} packageJson - * @returns {Array.} + * @param {object} packageJson + * @returns {Array.} */ function getIgnoredFilePatterns( packageJson ) { // The patterns supported by `package.json` in the `files` field do not correspond 1:1 to the patterns expected by the `glob`. @@ -155,9 +155,9 @@ function getIgnoredFilePatterns( packageJson ) { /** * Removes unnecessary fields from the `package.json`. * - * @param {Object} packageJson - * @param {Array.} packageJsonFieldsToRemove - * @param {Boolean} preservePostInstallHook + * @param {object} packageJson + * @param {Array.} packageJsonFieldsToRemove + * @param {boolean} preservePostInstallHook */ function cleanUpPackageJson( packageJson, packageJsonFieldsToRemove, preservePostInstallHook ) { for ( const key of Object.keys( packageJson ) ) { @@ -176,9 +176,9 @@ function cleanUpPackageJson( packageJson, packageJsonFieldsToRemove, preservePos /** * Sort function that defines the order of the paths. It sorts paths from the most nested ones first. * - * @param {String} firstPath - * @param {String} secondPath - * @returns {Number} + * @param {string} firstPath + * @param {string} secondPath + * @returns {number} */ function sortPathsFromDeepestFirst( firstPath, secondPath ) { const firstPathSegments = firstPath.split( '/' ).length; @@ -194,5 +194,5 @@ function sortPathsFromDeepestFirst( firstPath, secondPath ) { /** * @callback PackageJsonFieldsToRemoveCallback * @param {DefaultFieldsToRemove} defaults - * @returns {Array.} + * @returns {Array.} */ diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js index b03b60f79..dedde23a1 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/commitandtag.js @@ -13,10 +13,10 @@ const { toUnix } = upath; /** * Creates a commit and a tag for specified version. * - * @param {Object} options - * @param {String} options.version The commit will contain this param in its message and the tag will have a `v` prefix. - * @param {Array.} options.files Array of glob patterns for files to be added to the release commit. - * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. + * @param {object} options + * @param {string} options.version The commit will contain this param in its message and the tag will have a `v` prefix. + * @param {Array.} options.files Array of glob patterns for files to be added to the release commit. + * @param {string} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ export default async function commitAndTag( { version, files, cwd = process.cwd() } ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js index 4c8eb5f77..5da69a476 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/creategithubrelease.js @@ -12,12 +12,12 @@ const { getRepositoryUrl } = transformCommitUtils; /** * Create a GitHub release. * - * @param {Object} options - * @param {String} options.token Token used to authenticate with GitHub. - * @param {String} options.version Name of tag connected with the release. - * @param {String} options.description Description of the release. - * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. - * @returns {Promise.} + * @param {object} options + * @param {string} options.token Token used to authenticate with GitHub. + * @param {string} options.version Name of tag connected with the release. + * @param {string} options.description Description of the release. + * @param {string} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. + * @returns {Promise.} */ export default async function createGithubRelease( options ) { const { @@ -51,8 +51,8 @@ export default async function createGithubRelease( options ) { /** * Returns an npm tag based on the specified release version. * - * @param {String} version - * @returns {String} + * @param {string} version + * @returns {string} */ function getVersionTag( version ) { const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ]; @@ -64,9 +64,9 @@ function getVersionTag( version ) { * Resolves a promise containing a flag if the GitHub contains the release page for given version. * * @param {Octokit} github - * @param {String} repositoryOwner - * @param {String} repositoryName - * @param {String} version + * @param {string} repositoryOwner + * @param {string} repositoryName + * @param {string} version * @returns {Promise.} */ async function shouldCreateRelease( github, repositoryOwner, repositoryName, version ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js index 2d7606b54..ff6e6643e 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogformonorepository.js @@ -35,37 +35,37 @@ const noteInfo = `[ℹī¸](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-c * * The typed version will be the same for all packages. See: https://github.com/ckeditor/ckeditor5/issues/7323. * - * @param {Object} options + * @param {object} options * - * @param {String} options.cwd Current working directory (packages) from which all paths will be resolved. + * @param {string} options.cwd Current working directory (packages) from which all paths will be resolved. * - * @param {String} options.packages Where to look for packages. + * @param {string} options.packages Where to look for packages. * - * @param {Function} options.transformScope A function that returns a URL to a package from a scope of a commit. + * @param {function} options.transformScope A function that returns a URL to a package from a scope of a commit. * - * @param {String} [options.scope] Package names have to match to specified glob pattern in order to be processed. + * @param {string} [options.scope] Package names have to match to specified glob pattern in order to be processed. * - * @param {Array.} [options.skipPackages=[]] Name of packages which won't be touched. + * @param {Array.} [options.skipPackages=[]] Name of packages which won't be touched. * - * @param {Boolean} [options.skipLinks=false] If set on true, links to release or commits will be omitted. + * @param {boolean} [options.skipLinks=false] If set on true, links to release or commits will be omitted. * - * @param {String} [options.from] A commit or tag name that will be the first param of the range of commits to collect. + * @param {string} [options.from] A commit or tag name that will be the first param of the range of commits to collect. * - * @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. + * @param {string} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. * - * @param {String} [options.mainBranch='master'] A name of the main branch in the repository. + * @param {string} [options.mainBranch='master'] A name of the main branch in the repository. * * @param {Array.} [options.externalRepositories=[]] An array of object with additional repositories * that the function takes into consideration while gathering commits. It assumes that those directories are also mono repositories. * - * @param {Boolean} [options.skipFileSave=false] Whether to resolve the changes instead of saving it to a file. + * @param {boolean} [options.skipFileSave=false] Whether to resolve the changes instead of saving it to a file. * - * @param {String|null} [options.nextVersion=null] Next version to use. If not provided, a user needs to provide via CLI. + * @param {string|null} [options.nextVersion=null] Next version to use. If not provided, a user needs to provide via CLI. * * @param {FormatDateCallback} [options.formatDate] A callback allowing defining a custom format of the date inserted into the changelog. * If not specified, the default date matches the `YYYY-MM-DD` pattern. * - * @returns {Promise.} + * @returns {Promise.} */ export default async function generateChangelogForMonoRepository( options ) { const log = logger(); @@ -161,11 +161,11 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Returns collections with packages found in the `options.cwd` directory and the external repositories. * - * @param {Object} options - * @param {String} options.cwd Current working directory (packages) from which all paths will be resolved. - * @param {String} options.packages Where to look for packages. - * @param {String} options.scope Package names have to match to specified glob pattern in order to be processed. - * @param {Array.} options.skipPackages Name of packages which won't be touched. + * @param {object} options + * @param {string} options.cwd Current working directory (packages) from which all paths will be resolved. + * @param {string} options.packages Where to look for packages. + * @param {string} options.scope Package names have to match to specified glob pattern in order to be processed. + * @param {Array.} options.skipPackages Name of packages which won't be touched. * @param {Array.} options.externalRepositories An array of object with additional repositories * that the function takes into consideration while gathering packages. * @returns {PathsCollection} @@ -210,10 +210,10 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Returns a promise that resolves an array of commits since the last tag specified as `options.from`. * - * @param {Object} options - * @param {String} options.cwd Current working directory (packages) from which all paths will be resolved. - * @param {String} options.from A commit or tag name that will be the first param of the range of commits to collect. - * @param {String} options.releaseBranch A name of the branch that should be used for releasing packages. + * @param {object} options + * @param {string} options.cwd Current working directory (packages) from which all paths will be resolved. + * @param {string} options.from A commit or tag name that will be the first param of the range of commits to collect. + * @param {string} options.releaseBranch A name of the branch that should be used for releasing packages. * @param {Array.} options.externalRepositories An array of object with additional repositories * that the function takes into consideration while gathering commits. * @returns {Promise.>} @@ -378,7 +378,7 @@ export default async function generateChangelogForMonoRepository( options ) { * Finds commits that touched the package under `packagePath` directory. * * @param {Array.} commits - * @param {String} packagePath + * @param {string} packagePath * @returns {Array.} */ function filterCommitsByPath( commits, packagePath ) { @@ -400,7 +400,7 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Generates a list of changes based on the commits in the main repository. * - * @returns {Promise.} + * @returns {Promise.} */ function generateChangelogFromCommits() { logProcess( 'Generating the changelog...' ); @@ -516,7 +516,7 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Prepares a summary that describes what has changed in all dependencies. * - * @returns {String} + * @returns {string} */ function generateSummaryOfChangesInPackages() { const dependencies = new Map(); @@ -597,8 +597,8 @@ export default async function generateChangelogForMonoRepository( options ) { } /** - * @param {Map.} dependencies - * @returns {Map.} + * @param {Map.} dependencies + * @returns {Map.} */ function getNewPackages( dependencies ) { const packages = new Map(); @@ -616,9 +616,9 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Returns packages where scope of changes described in the commits' notes match to packages' names. * - * @param {Map.} dependencies - * @param {String} noteTitle - * @returns {Map.} + * @param {Map.} dependencies + * @param {string} noteTitle + * @returns {Map.} */ function getPackagesMatchedToScopesFromNotes( dependencies, noteTitle ) { const packages = new Map(); @@ -649,8 +649,8 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Returns packages that contain new features. * - * @param {Map.} dependencies - * @returns {Map.} + * @param {Map.} dependencies + * @returns {Map.} */ function getPackagesWithNewFeatures( dependencies ) { const packages = new Map(); @@ -672,10 +672,10 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Returns a formatted entry (string) for the changelog. * - * @param {String} packageName - * @param {String} nextVersion - * @param {String} currentVersion - * @returns {String} + * @param {string} packageName + * @param {string} nextVersion + * @param {string} currentVersion + * @returns {string} */ function formatChangelogEntry( packageName, nextVersion, currentVersion = null ) { const npmUrl = `https://www.npmjs.com/package/${ packageName }/v/${ nextVersion }`; @@ -690,7 +690,7 @@ export default async function generateChangelogForMonoRepository( options ) { /** * Returns a function that is being used when sorting commits. * - * @param {String} scopeField A name of the field that saves the commit's scope. + * @param {string} scopeField A name of the field that saves the commit's scope. * @returns {Function} */ function sortFunctionFactory( scopeField ) { @@ -714,11 +714,11 @@ export default async function generateChangelogForMonoRepository( options ) { } /** - * @param {String} message - * @param {Object} [options={}] - * @param {Number} [options.indentLevel=0] - * @param {Boolean} [options.startWithNewLine=false] Whether to append a new line before the message. - * @param {Boolean} [options.isWarning=false] Whether to use `warning` method instead of `log`. + * @param {string} message + * @param {object} [options={}] + * @param {number} [options.indentLevel=0] + * @param {boolean} [options.startWithNewLine=false] Whether to append a new line before the message. + * @param {boolean} [options.isWarning=false] Whether to use `warning` method instead of `log`. */ function logInfo( message, options = {} ) { const indentLevel = options.indentLevel || 0; @@ -730,30 +730,30 @@ export default async function generateChangelogForMonoRepository( options ) { } /** - * @typedef {Object} Version + * @typedef {object} Version * - * @param {Boolean} current The current version defined in the `package.json` file. + * @param {boolean} current The current version defined in the `package.json` file. * - * @param {Boolean} next The next version defined during generating the changelog file. + * @param {boolean} next The next version defined during generating the changelog file. */ /** - * @typedef {Object} ExternalRepository + * @typedef {object} ExternalRepository * - * @param {String} cwd An absolute path to the repository. + * @param {string} cwd An absolute path to the repository. * - * @param {String} packages Subdirectory in a given `cwd` that should searched for packages. E.g. `'packages'`. + * @param {string} packages Subdirectory in a given `cwd` that should searched for packages. E.g. `'packages'`. * - * @param {String} [scope] Glob pattern for package names to be processed. + * @param {string} [scope] Glob pattern for package names to be processed. * - * @param {Array.} [skipPackages] Name of packages which won't be touched. + * @param {Array.} [skipPackages] Name of packages which won't be touched. * - * @param {Boolean} [skipLinks] If set on `true`, a URL to commit (hash) will be omitted. + * @param {boolean} [skipLinks] If set on `true`, a URL to commit (hash) will be omitted. * - * @param {String} [from] A commit or tag name that will be the first param of the range of commits to collect. If not specified, + * @param {string} [from] A commit or tag name that will be the first param of the range of commits to collect. If not specified, * the option will inherit its value from the function's `options` object. * - * @param {String} [releaseBranch] A name of the branch that should be used for releasing packages. If not specified, the branch + * @param {string} [releaseBranch] A name of the branch that should be used for releasing packages. If not specified, the branch * used for the main repository will be used. */ @@ -762,5 +762,5 @@ export default async function generateChangelogForMonoRepository( options ) { * * @param {Date} now The current date. * - * @returns {String} The formatted date inserted into the changelog. + * @returns {string} The formatted date inserted into the changelog. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js index 8d4dfc86a..4000fea04 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/generatechangelogforsinglepackage.js @@ -28,15 +28,15 @@ const SKIP_GENERATE_CHANGELOG = 'Typed "skip" as a new version. Aborting.'; * * If the package does not have any commit, the user has to confirm whether the changelog should be generated. * - * @param {Object} [options={}] Additional options. + * @param {object} [options={}] Additional options. * - * @param {Boolean} [options.skipLinks=false] If set on true, links to release or commits will be omitted. + * @param {boolean} [options.skipLinks=false] If set on true, links to release or commits will be omitted. * - * @param {String} [options.from] A commit or tag name that will be the first param of the range of commits to collect. + * @param {string} [options.from] A commit or tag name that will be the first param of the range of commits to collect. * - * @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. + * @param {string} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. * - * @param {String} [options.mainBranch='master'] A name of the main branch in the repository. + * @param {string} [options.mainBranch='master'] A name of the main branch in the repository. * * @param {FormatDateCallback} [options.formatDate] A callback allowing defining a custom format of the date inserted into the changelog. * If not specified, the default date matches the `YYYY-MM-DD` pattern. @@ -186,11 +186,11 @@ export default async function generateChangelogForSinglePackage( options = {} ) } /** - * @param {String} message - * @param {Object} [options={}] - * @param {Number} [options.indentLevel=0] - * @param {Boolean} [options.startWithNewLine=false] Whether to append a new line before the message. - * @param {Boolean} [options.isWarning=false] Whether to use `warning` method instead of `log`. + * @param {string} message + * @param {object} [options={}] + * @param {number} [options.indentLevel=0] + * @param {boolean} [options.startWithNewLine=false] Whether to append a new line before the message. + * @param {boolean} [options.isWarning=false] Whether to use `warning` method instead of `log`. */ function logInfo( message, options = {} ) { const indentLevel = options.indentLevel || 0; @@ -206,5 +206,5 @@ export default async function generateChangelogForSinglePackage( options = {} ) * * @param {Date} now The current date. * - * @returns {String} The formatted date inserted into the changelog. + * @returns {string} The formatted date inserted into the changelog. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js index cce9f1d08..79e09c372 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/preparerepository.js @@ -10,12 +10,12 @@ import upath from 'upath'; /** * The goal is to prepare the release directory containing the packages we want to publish. * - * @param {Object} options - * @param {String} options.outputDirectory Relative path to the destination directory where packages will be stored. - * @param {String} [options.cwd] Root of the repository to prepare. `process.cwd()` by default. - * @param {String} [options.packagesDirectory] Relative path to a location of packages. + * @param {object} options + * @param {string} options.outputDirectory Relative path to the destination directory where packages will be stored. + * @param {string} [options.cwd] Root of the repository to prepare. `process.cwd()` by default. + * @param {string} [options.packagesDirectory] Relative path to a location of packages. * If specified, all of the found packages will be copied. - * @param {Array.} [options.packagesToCopy] List of packages that should be processed. + * @param {Array.} [options.packagesToCopy] List of packages that should be processed. * If not specified, all packages found in `packagesDirectory` are considered. * @param {RootPackageJson} [options.rootPackageJson] Object containing values to use in the created the `package.json` file. * If not specified, the root package will not be created. @@ -71,9 +71,9 @@ export default async function prepareRepository( options ) { } /** - * @param {Object} packageJson - * @param {String} [packageJson.name] - * @param {Array.} [packageJson.files] + * @param {object} packageJson + * @param {string} [packageJson.name] + * @param {Array.} [packageJson.files] */ function validateRootPackage( packageJson ) { if ( !packageJson.name ) { @@ -86,10 +86,10 @@ function validateRootPackage( packageJson ) { } /** - * @param {Object} options - * @param {String} options.cwd + * @param {object} options + * @param {string} options.cwd * @param {RootPackageJson} options.rootPackageJson - * @param {String} options.outputDirectoryPath + * @param {string} options.outputDirectoryPath * @returns {Promise} */ async function processRootPackage( { cwd, rootPackageJson, outputDirectoryPath } ) { @@ -110,11 +110,11 @@ async function processRootPackage( { cwd, rootPackageJson, outputDirectoryPath } } /** - * @param {Object} options - * @param {String} options.cwd - * @param {String} options.packagesDirectory - * @param {String} options.outputDirectoryPath - * @param {Array.} [options.packagesToCopy] + * @param {object} options + * @param {string} options.cwd + * @param {string} options.packagesDirectory + * @param {string} options.outputDirectoryPath + * @param {Array.} [options.packagesToCopy] * @returns {Promise} */ async function processMonorepoPackages( { cwd, packagesDirectory, packagesToCopy, outputDirectoryPath } ) { @@ -141,9 +141,9 @@ async function processMonorepoPackages( { cwd, packagesDirectory, packagesToCopy } /** - * @typedef {Object} RootPackageJson + * @typedef {object} RootPackageJson * - * @param {String} options.rootPackageJson.name Name of the package. Required value. + * @param {string} options.rootPackageJson.name Name of the package. Required value. * - * @param {Array.} options.rootPackageJson.files Array containing a list of files or directories to copy. Required value. + * @param {Array.} options.rootPackageJson.files Array containing a list of files or directories to copy. Required value. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js index 6b4a3891c..e4aa9c5bf 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js @@ -24,24 +24,24 @@ import publishPackageOnNpmCallback from '../utils/publishpackageonnpmcallback.js * When the validation for each package passes, packages are published on npm. Optional callback is called for confirmation whether to * continue. * - * @param {Object} options - * @param {String} options.packagesDirectory Relative path to a location of packages to release. - * @param {String} options.npmOwner The account name on npm, which should be used to publish the packages. + * @param {object} options + * @param {string} options.packagesDirectory Relative path to a location of packages to release. + * @param {string} options.npmOwner The account name on npm, which should be used to publish the packages. * @param {ListrTaskObject} options.listrTask An instance of `ListrTask`. * @param {AbortSignal|null} [options.signal=null] Signal to abort the asynchronous process. - * @param {String} [options.npmTag='staging'] The npm distribution tag. - * @param {Object.>|null} [options.optionalEntries=null] Specifies which entries from the `files` field in the + * @param {string} [options.npmTag='staging'] The npm distribution tag. + * @param {Object.>|null} [options.optionalEntries=null] Specifies which entries from the `files` field in the * `package.json` are optional. The key is a package name, and its value is an array of optional entries from the `files` field, for which * it is allowed not to match any file. The `options.optionalEntries` object may also contain the `default` key, which is used for all * packages that do not have own definition. - * @param {String} [options.confirmationCallback=null] An callback whose response decides to continue the publishing packages. Synchronous + * @param {string} [options.confirmationCallback=null] An callback whose response decides to continue the publishing packages. Synchronous * and asynchronous callbacks are supported. - * @param {Boolean} [options.requireEntryPoint=false] Whether to verify if packages to publish define an entry point. In other words, + * @param {boolean} [options.requireEntryPoint=false] Whether to verify if packages to publish define an entry point. In other words, * whether their `package.json` define the `main` field. - * @param {Array.} [options.optionalEntryPointPackages=[]] If the entry point validator is enabled (`requireEntryPoint=true`), + * @param {Array.} [options.optionalEntryPointPackages=[]] If the entry point validator is enabled (`requireEntryPoint=true`), * this array contains a list of packages that will not be checked. In other words, they do not have to define the entry point. - * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. - * @param {Number} [options.concurrency=4] Number of CPUs that will execute the task. + * @param {string} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. + * @param {number} [options.concurrency=4] Number of CPUs that will execute the task. * @returns {Promise} */ export default async function publishPackages( options ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/push.js b/packages/ckeditor5-dev-release-tools/lib/tasks/push.js index 70f77b7e0..451bbb699 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/push.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/push.js @@ -9,10 +9,10 @@ import shellEscape from 'shell-escape'; /** * Push the local changes to a remote server. * - * @param {Object} options - * @param {String} options.releaseBranch A name of the branch that should be used for releasing packages. - * @param {String} options.version Name of tag connected with the release. - * @param {String} [options.cwd] Root of the repository to prepare. `process.cwd()` by default. + * @param {object} options + * @param {string} options.releaseBranch A name of the branch that should be used for releasing packages. + * @param {string} options.version Name of tag connected with the release. + * @param {string} [options.cwd] Root of the repository to prepare. `process.cwd()` by default. * @returns {Promise} */ export default async function push( options ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js index a1d0c7309..1746dfc6a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/reassignnpmtags.js @@ -19,10 +19,10 @@ const execPromise = util.promisify( exec ); * Used to switch the tags from `staging` to `latest` for specified array of packages. * Each operation will be retried up to 3 times in case of failure. * - * @param {Object} options - * @param {String} options.npmOwner User that is authorized to release packages. - * @param {String} options.version Specifies the version of packages to reassign the tags for. - * @param {Array.} options.packages Array of packages' names to reassign tags for. + * @param {object} options + * @param {string} options.npmOwner User that is authorized to release packages. + * @param {string} options.version Specifies the version of packages to reassign the tags for. + * @param {Array.} options.packages Array of packages' names to reassign tags for. * @returns {Promise} */ export default async function reassignNpmTags( { npmOwner, version, packages } ) { @@ -79,16 +79,16 @@ export default async function reassignNpmTags( { npmOwner, version, packages } ) } /** - * @param {String} message - * @returns {String} + * @param {string} message + * @returns {string} */ function trimErrorMessage( message ) { return message.replace( /npm ERR!.*\n/g, '' ).trim(); } /** - * @param {Function} callback - * @param {Number} times + * @param {function} callback + * @param {number} times * @returns {RetryCallback} */ function retry( callback, times = 3 ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js index 7327965a4..8e22c2a2f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updatedependencies.js @@ -17,16 +17,16 @@ import upath from 'upath'; * The eligible dependencies are distinguished by the return value from the `options.shouldUpdateVersionCallback` function. Only if this * callback returns a truthy value for a given dependency, its version will be updated. * - * @param {Object} options - * @param {String} options.version Target version or a range version to which all eligible dependencies will be updated. + * @param {object} options + * @param {string} options.version Target version or a range version to which all eligible dependencies will be updated. * Examples: `1.0.0`, `^1.0.0`, etc. * @param {UpdateVersionCallback} options.shouldUpdateVersionCallback Callback function that decides whether to update a version * for a dependency. It receives a package name as an argument and should return a boolean value. * @param {UpdateDependenciesPackagesDirectoryFilter|null} [options.packagesDirectoryFilter=null] An optional callback allowing * filtering out directories/packages that should not be touched by the task. - * @param {String} [options.packagesDirectory] Relative path to a location of packages to update their dependencies. If not specified, + * @param {string} [options.packagesDirectory] Relative path to a location of packages to update their dependencies. If not specified, * only the root package is checked. - * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. + * @param {string} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ export default async function updateDependencies( options ) { @@ -62,9 +62,9 @@ export default async function updateDependencies( options ) { /** * Updates the version for each eligible dependency. * - * @param {String} version - * @param {Function} callback - * @param {Object} [dependencies] + * @param {string} version + * @param {function} callback + * @param {object} [dependencies] */ function updateVersion( version, callback, dependencies ) { if ( !dependencies ) { @@ -79,10 +79,10 @@ function updateVersion( version, callback, dependencies ) { } /** - * @param {String} cwd - * @param {Array.} globPatterns + * @param {string} cwd + * @param {Array.} globPatterns * @param {UpdateDependenciesPackagesDirectoryFilter|null} packagesDirectoryFilter - * @returns {Promise.>} + * @returns {Promise.>} */ async function getPackageJsonPaths( cwd, globPatterns, packagesDirectoryFilter ) { const globOptions = { @@ -103,15 +103,15 @@ async function getPackageJsonPaths( cwd, globPatterns, packagesDirectoryFilter ) /** * @callback UpdateVersionCallback * - * @param {String} packageName A package name. + * @param {string} packageName A package name. * - * @returns {Boolean} Whether to update (`true`) or ignore (`false`) bumping the package version. + * @returns {boolean} Whether to update (`true`) or ignore (`false`) bumping the package version. */ /** * @callback UpdateDependenciesPackagesDirectoryFilter * - * @param {String} packageJsonPath An absolute path to a `package.json` file. + * @param {string} packageJsonPath An absolute path to a `package.json` file. * - * @returns {Boolean} Whether to include (`true`) or skip (`false`) processing the given directory/package. + * @returns {boolean} Whether to include (`true`) or skip (`false`) processing the given directory/package. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js index 570c5e892..bd1416b48 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/updateversions.js @@ -22,13 +22,13 @@ const { normalizeTrim, toUnix, dirname, join } = upath; * Exception: passing a version starting with the `0.0.0-nightly` string. It is used for publishing * a nightly release. * - * @param {Object} options - * @param {String} options.version Version to store in a `package.json` file under the `version` key. + * @param {object} options + * @param {string} options.version Version to store in a `package.json` file under the `version` key. * @param {UpdateVersionsPackagesDirectoryFilter|null} [options.packagesDirectoryFilter=null] An optional callback allowing filtering out * directories/packages that should not be touched by the task. - * @param {String} [options.packagesDirectory] Relative path to a location of packages to update. If not specified, + * @param {string} [options.packagesDirectory] Relative path to a location of packages to update. If not specified, * only the root package is checked. - * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. + * @param {string} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @returns {Promise} */ export default async function updateVersions( options ) { @@ -66,10 +66,10 @@ export default async function updateVersions( options ) { } /** - * @param {String} cwd - * @param {Array.} globPatterns + * @param {string} cwd + * @param {Array.} globPatterns * @param {UpdateVersionsPackagesDirectoryFilter|null} packagesDirectoryFilter - * @returns {Promise.>} + * @returns {Promise.>} */ async function getPackageJsonPaths( cwd, globPatterns, packagesDirectoryFilter ) { const pkgJsonPaths = await glob( globPatterns, { @@ -86,8 +86,8 @@ async function getPackageJsonPaths( cwd, globPatterns, packagesDirectoryFilter ) } /** - * @param {String} packagesDirectory - * @returns {Promise.} + * @param {string} packagesDirectory + * @returns {Promise.} */ function readPackageJson( packagesDirectory ) { const packageJsonPath = join( packagesDirectory, 'package.json' ); @@ -96,8 +96,8 @@ function readPackageJson( packagesDirectory ) { } /** - * @param {String|null} packagesDirectory - * @returns {Array.} + * @param {string|null} packagesDirectory + * @returns {Array.} */ function getGlobPatterns( packagesDirectory ) { const patterns = [ 'package.json' ]; @@ -110,9 +110,9 @@ function getGlobPatterns( packagesDirectory ) { } /** - * @param {Array.} pkgJsonPaths - * @param {String|null} packagesDirectory - * @returns {Object} + * @param {Array.} pkgJsonPaths + * @param {string|null} packagesDirectory + * @returns {object} */ function getRandomPackagePath( pkgJsonPaths, packagesDirectory ) { const randomPkgJsonPaths = packagesDirectory ? @@ -128,8 +128,8 @@ function getRandomPackagePath( pkgJsonPaths, packagesDirectory ) { * * A nightly version is always considered as valid. * - * @param {String} newVersion - * @param {String} currentVersion + * @param {string} newVersion + * @param {string} currentVersion */ function checkIfVersionIsValid( newVersion, currentVersion ) { if ( newVersion.startsWith( '0.0.0-nightly' ) ) { @@ -144,7 +144,7 @@ function checkIfVersionIsValid( newVersion, currentVersion ) { /** * @callback UpdateVersionsPackagesDirectoryFilter * - * @param {String} packageJsonPath An absolute path to a `package.json` file. + * @param {string} packageJsonPath An absolute path to a `package.json` file. * - * @returns {Boolean} Whether to include (`true`) or skip (`false`) processing the given directory/package. + * @returns {boolean} Whether to include (`true`) or skip (`false`) processing the given directory/package. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js index 495d81e95..a6d0a4a77 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/verifypackagespublishedcorrectly.js @@ -12,10 +12,10 @@ import checkVersionAvailability from '../utils/checkversionavailability.js'; * Npm sometimes throws incorrect error 409 while publishing, while the package uploads correctly. * The purpose of the script is to validate if packages that threw 409 are uploaded correctly to npm. * - * @param {Object} options - * @param {String} options.packagesDirectory Relative path to a location of packages to release. - * @param {String} options.version Version of the current release. - * @param {Function} options.onSuccess Callback fired when function is successful. + * @param {object} options + * @param {string} options.packagesDirectory Relative path to a location of packages to release. + * @param {string} options.version Version of the current release. + * @param {function} options.onSuccess Callback fired when function is successful. * @returns {Promise} */ export default async function verifyPackagesPublishedCorrectly( options ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js index 20b94d5f5..9a261bca5 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertfilestopublish.js @@ -10,8 +10,8 @@ import { glob } from 'glob'; /** * Checks if all files expected to be released actually exist in the package directory. Verification takes place for all packages. * - * @param {String} packagePaths - * @param {Object.>|null} optionalEntries + * @param {string} packagePaths + * @param {Object.>|null} optionalEntries * @returns {Promise} */ export default async function assertFilesToPublish( packagePaths, optionalEntries = null ) { @@ -62,10 +62,10 @@ export default async function assertFilesToPublish( packagePaths, optionalEntrie /** * Filters out the optional entries from the `files` field and returns only the required ones. * - * @param {Array.} entries - * @param {String} packageName - * @param {Object.>|null} optionalEntries - * @returns {Array.} + * @param {Array.} entries + * @param {string} packageName + * @param {Object.>|null} optionalEntries + * @returns {Array.} */ function getRequiredEntries( entries, packageName, optionalEntries ) { if ( !optionalEntries ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js index fa117233b..da7768037 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmauthorization.js @@ -8,7 +8,7 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** * Checks whether a user is logged to npm as the provided account name. * - * @param {String} npmOwner Expected npm account name that should be logged into npm. + * @param {string} npmOwner Expected npm account name that should be logged into npm. * @returns {Promise} */ export default async function assertNpmAuthorization( npmOwner ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js index 17a5a459a..8c8aa60ca 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertnpmtag.js @@ -10,8 +10,8 @@ import semver from 'semver'; /** * Checks if the npm tag matches the tag calculated from the package version. Verification takes place for all packages. * - * @param {Array.} packagePaths - * @param {String} npmTag + * @param {Array.} packagePaths + * @param {string} npmTag * @returns {Promise} */ export default async function assertNpmTag( packagePaths, npmTag ) { @@ -44,8 +44,8 @@ export default async function assertNpmTag( packagePaths, npmTag ) { * For the official release, returns the "latest" tag. For a non-official release (pre-release), returns the version tag extracted from * the package version. * - * @param {String} version - * @returns {String} + * @param {string} version + * @returns {string} */ function getVersionTag( version ) { const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ]; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js b/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js index 10699c69f..7fc39a05b 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/assertpackages.js @@ -9,11 +9,11 @@ import upath from 'upath'; /** * Checks if all packages in the provided directories contain the `package.json` file. * - * @param {Array.} packagePaths - * @param {Object} options - * @param {Boolean} options.requireEntryPoint Whether to verify if packages to publish define an entry point. In other words, + * @param {Array.} packagePaths + * @param {object} options + * @param {boolean} options.requireEntryPoint Whether to verify if packages to publish define an entry point. In other words, * whether their `package.json` define the `main` field. - * @param {Array.} options.optionalEntryPointPackages If the entry point validator is enabled (`requireEntryPoint=true`), + * @param {Array.} options.optionalEntryPointPackages If the entry point validator is enabled (`requireEntryPoint=true`), * this array contains a list of packages that will not be checked. In other words, they do not have to define the entry point. * @returns {Promise} */ diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js b/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js index af181049e..197257d71 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/checkversionavailability.js @@ -12,8 +12,8 @@ import shellEscape from 'shell-escape'; * Returns a promise that resolves to `true` if the provided version does not exist or resolves the promise to `false` otherwise. * If the `npm show` command exits with an error, it is re-thrown. * - * @param {String} version - * @param {String} packageName + * @param {string} version + * @param {string} packageName * @returns {Promise} */ export default async function checkVersionAvailability( version, packageName ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js index 26b9d89bc..dd52761c5 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/configurereleaseoptions.js @@ -11,7 +11,7 @@ import provideToken from './providetoken.js'; * * If the user choices a GitHub, required token also has to be provided. * - * @returns {Promise.} + * @returns {Promise.} */ export default async function configureReleaseOptions() { const options = {}; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js b/packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js index 160e8eab4..5bdb7dfca 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/confirmincludingpackage.js @@ -8,7 +8,7 @@ import inquirer from 'inquirer'; /** * Asks a user for a confirmation for including a package that does not contain all required files. * - * @returns {Promise.} + * @returns {Promise.} */ export default async function confirmIncludingPackage() { const confirmQuestion = { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js b/packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js index 94efd99a4..3839eb650 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/confirmnpmtag.js @@ -9,9 +9,9 @@ import chalk from 'chalk'; /** * Asks a user for a confirmation for updating and tagging versions of the packages. * - * @param {String} versionTag A version tag based on a package version specified in `package.json`. - * @param {String} npmTag A tag typed by the user when using the release tools. - * @returns {Promise.} + * @param {string} versionTag A version tag based on a package version specified in `package.json`. + * @param {string} npmTag A tag typed by the user when using the release tools. + * @returns {Promise.} */ export default function confirmNpmTag( versionTag, npmTag ) { const areVersionsEqual = versionTag === npmTag; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js index dc7c59118..2df792c87 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/displaycommits.js @@ -10,9 +10,9 @@ import { CLI_COMMIT_INDENT_SIZE, CLI_INDENT_SIZE } from './constants.js'; /** * @param {Array.|Set.} commits - * @param {Object} [options={}] - * @param {Boolean} [options.attachLinkToCommit=false] Whether to attach a link to parsed commit. - * @param {Number} [options.indentLevel=1] The indent level. + * @param {object} [options={}] + * @param {boolean} [options.attachLinkToCommit=false] Whether to attach a link to parsed commit. + * @param {number} [options.indentLevel=1] The indent level. */ export default function displayCommits( commits, options = {} ) { const log = logger(); @@ -28,7 +28,7 @@ export default function displayCommits( commits, options = {} ) { const COMMITS_SEPARATOR = listIndent + chalk.gray( '-'.repeat( 112 ) ); // Group of commits by the commit's hash. - /** @type {Map.>} */ + /** @type {Map.>} */ const commitGroups = new Map(); for ( const singleCommit of commits ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js index 77e49cd79..57bee865a 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/executeinparallel.js @@ -23,17 +23,17 @@ const WORKER_SCRIPT = new URL( './parallelworker.js', import.meta.url ); * Functions cannot be passed to workers. Hence, we store the callback as a Node.js file loaded by workers. * * @see https://nodejs.org/api/worker_threads.html - * @param {Object} options - * @param {String} options.packagesDirectory Relative path to a location of packages to execute a task. - * @param {Function} options.taskToExecute A callback that is executed on all found packages. + * @param {object} options + * @param {string} options.packagesDirectory Relative path to a location of packages to execute a task. + * @param {function} options.taskToExecute A callback that is executed on all found packages. * It receives an absolute path to a package as an argument. It can be synchronous or may return a promise. * @param {ListrTaskObject} [options.listrTask={}] An instance of `ListrTask`. * @param {AbortSignal|null} [options.signal=null] Signal to abort the asynchronous process. If not set, default AbortController is created. - * @param {Object} [options.taskOptions=null] Optional data required by the task. + * @param {object} [options.taskOptions=null] Optional data required by the task. * @param {ExecuteInParallelPackagesDirectoryFilter|null} [options.packagesDirectoryFilter=null] An optional callback allowing filtering out * directories/packages that should not be touched by the task. - * @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. - * @param {Number} [options.concurrency=require( 'os' ).cpus().length / 2] Number of CPUs that will execute the task. + * @param {string} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. + * @param {number} [options.concurrency=require( 'os' ).cpus().length / 2] Number of CPUs that will execute the task. * @returns {Promise} */ export default async function executeInParallel( options ) { @@ -99,7 +99,7 @@ export default async function executeInParallel( options ) { /** * @param {ListrTaskObject} listrTask - * @param {Number} total + * @param {number} total * @returns {Function} */ function progressFactory( listrTask, total ) { @@ -112,10 +112,10 @@ function progressFactory( listrTask, total ) { } /** - * @param {Object} options + * @param {object} options * @param {AbortSignal} options.signal - * @param {Function} options.onPackageDone - * @param {Object} options.workerData + * @param {function} options.onPackageDone + * @param {object} options.workerData * @returns {Promise} */ function createWorker( { signal, onPackageDone, workerData } ) { @@ -149,9 +149,9 @@ function createWorker( { signal, onPackageDone, workerData } ) { * * To avoid having packages with a common prefix in a single thread, use a loop for attaching packages to threads. * - * @param {Array.} packages An array of absolute paths to packages. - * @param {Number} concurrency A number of threads. - * @returns {Array.>} + * @param {Array.} packages An array of absolute paths to packages. + * @param {number} concurrency A number of threads. + * @returns {Array.>} */ function getPackagesGroupedByThreads( packages, concurrency ) { return packages.reduce( ( collection, packageItem, index ) => { @@ -168,19 +168,19 @@ function getPackagesGroupedByThreads( packages, concurrency ) { } /** - * @typedef {Object} ListrTaskObject + * @typedef {object} ListrTaskObject * * @see https://listr2.kilic.dev/api/classes/ListrTaskObject.html * - * @property {String} title Title of the task. + * @property {string} title Title of the task. * - * @property {String} output Update the current output of the task. + * @property {string} output Update the current output of the task. */ /** * @callback ExecuteInParallelPackagesDirectoryFilter * - * @param {String} directoryPath An absolute path to a directory. + * @param {string} directoryPath An absolute path to a directory. * - * @returns {Boolean} Whether to include (`true`) or skip (`false`) processing the given directory. + * @returns {boolean} Whether to include (`true`) or skip (`false`) processing the given directory. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js index a0906993c..8d7f39a67 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/generatechangelog.js @@ -14,27 +14,27 @@ const UPDATED_TRANSLATION_COMMIT = '* Updated translations.'; * * @param {Array.} commits * - * @param {Object} context - * @param {String} context.version Current version for the release. - * @param {String} context.repoUrl The repository URL. - * @param {String} context.currentTag A tag for the current version. - * @param {String} context.commit Commit keyword in the URL. - * @param {String} [context.previousTag] A tag for the previous version. - * @param {Boolean} [context.skipCommitsLink=false] Whether to skip adding links to commit. - * @param {Boolean} [context.skipCompareLink=false] Whether to remove the compare URL in the header. + * @param {object} context + * @param {string} context.version Current version for the release. + * @param {string} context.repoUrl The repository URL. + * @param {string} context.currentTag A tag for the current version. + * @param {string} context.commit Commit keyword in the URL. + * @param {string} [context.previousTag] A tag for the previous version. + * @param {boolean} [context.skipCommitsLink=false] Whether to skip adding links to commit. + * @param {boolean} [context.skipCompareLink=false] Whether to remove the compare URL in the header. * - * @param {Object} options - * @param {Object} options.transform - * @param {Function} options.transform.hash A function for mapping the commit's hash. - * @param {Array.|String} options.groupBy A key for grouping the commits. - * @param {Function} options.commitGroupsSort A sort function for the groups. - * @param {Function} options.noteGroupsSort A soft function for the notes. - * @param {String} options.mainTemplate The main template for the changelog. - * @param {String} options.headerPartial The "header" partial used in the main template. - * @param {String} options.commitPartial The "commit" partial used in the main template. - * @param {String} options.footerPartial The "footer" partial used in the main template. + * @param {object} options + * @param {object} options.transform + * @param {function} options.transform.hash A function for mapping the commit's hash. + * @param {Array.|string} options.groupBy A key for grouping the commits. + * @param {function} options.commitGroupsSort A sort function for the groups. + * @param {function} options.noteGroupsSort A soft function for the notes. + * @param {string} options.mainTemplate The main template for the changelog. + * @param {string} options.headerPartial The "header" partial used in the main template. + * @param {string} options.commitPartial The "commit" partial used in the main template. + * @param {string} options.footerPartial The "footer" partial used in the main template. * - * @returns {Promise.} + * @returns {Promise.} */ export default function generateChangelog( commits, context, options ) { const commitStream = new Readable( { objectMode: true } ); @@ -64,10 +64,10 @@ export default function generateChangelog( commits, context, options ) { /** * Merges multiple "Updated translations." entries into the single commit. * - * @param {String} changelog Generated changelog. - * @param {Object} [options={}] - * @param {Boolean} [options.skipCommitsLink=false] Whether to skip adding links to commit. - * @returns {String} + * @param {string} changelog Generated changelog. + * @param {object} [options={}] + * @param {boolean} [options.skipCommitsLink=false] Whether to skip adding links to commit. + * @returns {string} */ function mergeUpdateTranslationsCommits( changelog, options = {} ) { let foundUpdatedTranslationCommit = false; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js b/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js index c6089876b..77a4dfc8d 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getchangedfilesforcommit.js @@ -8,8 +8,8 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** * Returns an array with paths to changed files for given commit. * - * @param {String} commitId - * @returns {Array.} + * @param {string} commitId + * @returns {Array.} */ export default function getChangedFilesForCommit( commitId ) { const gitCommand = `git log -m -1 --name-only --pretty="format:" ${ commitId }`; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js index 6d0d39381..1425db472 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getchangelog.js @@ -8,8 +8,8 @@ import path from 'path'; import { CHANGELOG_FILE } from './constants.js'; /** - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} + * @param {string} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {string|null} */ export default function getChangelog( cwd = process.cwd() ) { const changelogFile = path.join( cwd, CHANGELOG_FILE ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js b/packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js index 1c61c7981..4ba9f9f29 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getchangesforversion.js @@ -9,9 +9,9 @@ import getChangelog from './getchangelog.js'; /** * Retrieves changes from the changelog for the given version (tag). * - * @param {String} version - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} + * @param {string} version + * @param {string} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {string|null} */ export default function getChangesForVersion( version, cwd = process.cwd() ) { version = version.replace( /^v/, '' ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js index c7a170c73..d95d605bd 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getcommits.js @@ -14,11 +14,11 @@ import shellEscape from 'shell-escape'; /** * Returns a promise that resolves an array of commits since the last tag specified as `options.from`. * - * @param {Function} transformCommit - * @param {Object} options - * @param {String} [options.from] A commit or tag name that will be the first param of the range of commits to collect. - * @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. - * @param {String} [options.mainBranch='master'] A name of the main branch in the repository. + * @param {function} transformCommit + * @param {object} options + * @param {string} [options.from] A commit or tag name that will be the first param of the range of commits to collect. + * @param {string} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages. + * @param {string} [options.mainBranch='master'] A name of the main branch in the repository. * @returns {Promise.>} */ export default function getCommits( transformCommit, options = {} ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js b/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js index 6ed97df4e..9d4659ecc 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getformatteddate.js @@ -6,7 +6,7 @@ import { format } from 'date-fns'; /** - * @returns {String} + * @returns {string} */ export default function getFormattedDate() { return format( new Date(), 'yyyy-MM-dd' ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js b/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js index cee74e04c..1a7e88b51 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getnewversiontype.js @@ -7,7 +7,7 @@ * Proposes new version based on commits. * * @param {Array.} commits - * @returns {String|null} + * @returns {string|null} */ export default function getNewVersionType( commits ) { // No commits = no changes. diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js b/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js index 4c67d1da8..b3a45becd 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getnpmtagfromversion.js @@ -6,8 +6,8 @@ import semver from 'semver'; /** - * @param {String} version - * @returns {String} + * @param {string} version + * @returns {string} */ export default function getNpmTagFromVersion( version ) { const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ]; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js index 10d0bfcdd..d7edb01f9 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagejson.js @@ -12,8 +12,8 @@ import upath from 'upath'; * This function is helpful for testing the whole process. Allows mocking the file * instead of create the fixtures. * - * @param {String} [cwd=process.cwd()] Where to look for package.json. - * @returns {Object} + * @param {string} [cwd=process.cwd()] Where to look for package.json. + * @returns {object} */ export default function getPackageJson( cwd = process.cwd() ) { let pkgJsonPath = cwd; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js index 3f41ed1a8..5467e18fc 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getpackagespaths.js @@ -17,13 +17,13 @@ import getPackageJson from './getpackagejson.js'; * - The second one is marked as `skipped` and means that packages should not be processed. They were listed as packages to skip * (`options.skipPackages` or don't mach to `options.scope`). * - * @param {Object} options - * @param {String} options.cwd Current work directory. - * @param {String|null} options.packages Name of directory where to look for packages. If `null`, only repository specified under + * @param {object} options + * @param {string} options.cwd Current work directory. + * @param {string|null} options.packages Name of directory where to look for packages. If `null`, only repository specified under * `options.cwd` will be returned. - * @param {String|Array.} options.skipPackages Glob pattern(s) which describes which packages should be skipped. - * @param {String} [options.scope] Package names have to match to specified glob pattern. - * @param {Boolean} [options.skipMainRepository=false] If set on true, package found in `options.cwd` will be skipped. + * @param {string|Array.} options.skipPackages Glob pattern(s) which describes which packages should be skipped. + * @param {string} [options.scope] Package names have to match to specified glob pattern. + * @param {boolean} [options.skipMainRepository=false] If set on true, package found in `options.cwd` will be skipped. * @returns {PathsCollection} */ export default function getPackagesPaths( options ) { @@ -80,9 +80,9 @@ export default function getPackagesPaths( options ) { } /** - * @typedef {Object} PathsCollection + * @typedef {object} PathsCollection * - * @property {Set.} matched Packages that match given criteria. + * @property {Set.} matched Packages that match given criteria. * - * @property {Set.} skipped Packages that do not match given criteria. + * @property {Set.} skipped Packages that do not match given criteria. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js index f60b58664..0773bfd19 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/getwriteroptions.js @@ -15,7 +15,7 @@ const templatePath = path.join( __dirname, '..', 'templates' ); /** * @param {WriterOptionsTransformCallback} transform - * @returns {Object} + * @returns {object} */ export default function getWriterOptions( transform ) { return { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js b/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js index 6c5e0863f..888cf46cf 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/isversionpublishablefortag.js @@ -10,10 +10,10 @@ import shellEscape from 'shell-escape'; /** * This util aims to verify if the given `packageName` can be published with the given `version` on the `npmTag`. * - * @param {String} packageName - * @param {String} version - * @param {String} npmTag - * @return {Promise.} + * @param {string} packageName + * @param {string} version + * @param {string} npmTag + * @returns {Promise.} */ export default async function isVersionPublishableForTag( packageName, version, npmTag ) { const command = `npm view ${ shellEscape( [ packageName ] ) }@${ shellEscape( [ npmTag ] ) } version --silent`; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js index 0f74809fb..d7491b852 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/parallelworker.js @@ -10,8 +10,8 @@ import { parentPort, workerData } from 'worker_threads'; // Required due to top-level await. ( async () => { /** - * @param {String} callbackModule - * @param {Array.} packages + * @param {string} callbackModule + * @param {Array.} packages */ const { default: callback } = await import( workerData.callbackModule ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js b/packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js index 80c171f21..dee9fcec4 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/providenewversionformonorepository.js @@ -11,12 +11,12 @@ import { CLI_INDENT_SIZE } from './constants.js'; /** * Asks a user for providing the new version for a major release. * - * @param {String} version - * @param {String} foundPackage - * @param {String} bumpType - * @param {Object} [options={}] - * @param {Number} [options.indentLevel=0] The indent level. - * @returns {Promise.} + * @param {string} version + * @param {string} foundPackage + * @param {string} bumpType + * @param {object} [options={}] + * @param {number} [options.indentLevel=0] The indent level. + * @returns {Promise.} */ export default async function provideNewVersionForMonoRepository( version, foundPackage, bumpType, options = {} ) { const indentLevel = options.indentLevel || 0; diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js b/packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js index a552df67c..d3ae24c7f 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/providetoken.js @@ -8,7 +8,7 @@ import inquirer from 'inquirer'; /** * Asks a user for providing the GitHub token. * - * @returns {Promise.} + * @returns {Promise.} */ export default async function provideToken() { const tokenQuestion = { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js b/packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js index a3e14ef34..4b7642dcf 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/provideversion.js @@ -11,13 +11,13 @@ import { CLI_INDENT_SIZE } from './constants.js'; /** * Asks a user for providing the new version. * - * @param {String} packageVersion - * @param {String|null} releaseTypeOrNewVersion - * @param {Object} [options] - * @param {Boolean} [options.disableInternalVersion=false] Whether to "internal" version is enabled. - * @param {Boolean} [options.disableSkipVersion=false] Whether to "skip" version is enabled. - * @param {Number} [options.indentLevel=0] The indent level. - * @returns {Promise.} + * @param {string} packageVersion + * @param {string|null} releaseTypeOrNewVersion + * @param {object} [options] + * @param {boolean} [options.disableInternalVersion=false] Whether to "internal" version is enabled. + * @param {boolean} [options.disableSkipVersion=false] Whether to "skip" version is enabled. + * @param {number} [options.indentLevel=0] The indent level. + * @returns {Promise.} */ export default function provideVersion( packageVersion, releaseTypeOrNewVersion, options = {} ) { const indentLevel = options.indentLevel || 0; @@ -66,11 +66,11 @@ export default function provideVersion( packageVersion, releaseTypeOrNewVersion, } /** - * @param {Object} options - * @param {String} options.packageVersion - * @param {String|null} options.releaseTypeOrNewVersion - * @param {Boolean} options.disableInternalVersion - * @return {String} + * @param {object} options + * @param {string} options.packageVersion + * @param {string|null} options.releaseTypeOrNewVersion + * @param {boolean} options.disableInternalVersion + * @returns {string} */ function getSuggestedVersion( { packageVersion, releaseTypeOrNewVersion, disableInternalVersion } ) { if ( !releaseTypeOrNewVersion || releaseTypeOrNewVersion === 'skip' ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js index 2bbb13cfe..b7bb7683c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js @@ -6,9 +6,9 @@ /** * Calls the npm command to publish the package. When a package is successfully published, it is removed from the filesystem. * - * @param {String} packagePath - * @param {Object} taskOptions - * @param {String} taskOptions.npmTag + * @param {string} packagePath + * @param {object} taskOptions + * @param {string} taskOptions.npmTag * @returns {Promise} */ export default async function publishPackageOnNpmCallback( packagePath, taskOptions ) { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js index 412c948c0..d4e1e3683 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/savechangelog.js @@ -8,8 +8,8 @@ import path from 'path'; import { CHANGELOG_FILE } from './constants.js'; /** - * @param {String} content - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + * @param {string} content + * @param {string} [cwd=process.cwd()] Where to look for the changelog file. */ export default function saveChangelog( content, cwd = process.cwd() ) { const changelogFile = path.join( cwd, CHANGELOG_FILE ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js index f8b91ce96..e2d6ef55b 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitfactory.js @@ -20,11 +20,11 @@ const SQUASH_COMMIT_REGEXP = /^[\W\w]+ \(#\d+\)$/; * - normalizes notes (e.g. "MAJOR BREAKING CHANGE" will be replaced with "MAJOR BREAKING CHANGES"), * - the commit is always being returned. Even, if it should not be added to the changelog. * - * @param {Object} [options={}] - * @param {Boolean} [options.treatMajorAsMinorBreakingChange=false] If set on true, all "MAJOR BREAKING CHANGES" notes will be replaced + * @param {object} [options={}] + * @param {boolean} [options.treatMajorAsMinorBreakingChange=false] If set on true, all "MAJOR BREAKING CHANGES" notes will be replaced * with "MINOR BREAKING CHANGES". This behaviour is being disabled automatically if `options.useExplicitBreakingChangeGroups` is * set on `false` because all commits will be treated as "BREAKING CHANGES". - * @param {Boolean} [options.useExplicitBreakingChangeGroups] If set on `true`, notes from parsed commits will be grouped as + * @param {boolean} [options.useExplicitBreakingChangeGroups] If set on `true`, notes from parsed commits will be grouped as * "MINOR BREAKING CHANGES" and "MAJOR BREAKING CHANGES'. If set on `false` (by default), all breaking changes notes will be treated * as "BREAKING CHANGES". * @returns {TransformCommit} @@ -262,8 +262,8 @@ export default function transformCommitFactory( options = {} ) { /** * Merges multiple "Closes #x" references as "Closes #x, #y.". * - * @param {String} subject - * @returns {String} + * @param {string} subject + * @returns {string} */ function mergeCloseReferences( subject ) { const refs = []; @@ -353,8 +353,8 @@ export default function transformCommitFactory( options = {} ) { * * For commits with no scope, `null` will be returned instead of the array (as `scope`). * - * @param {String} type First line from the commit message. - * @returns {Object} + * @param {string} type First line from the commit message. + * @returns {object} */ function extractScopeFromPrefix( type ) { if ( !type ) { @@ -387,8 +387,8 @@ export default function transformCommitFactory( options = {} ) { * * For notes with no scope, `null` will be returned instead of the array (as `scope`). * - * @param {String} text A text that describes a note. - * @returns {Object} + * @param {string} text A text that describes a note. + * @returns {object} */ function extractScopeFromNote( text ) { const scopeAsText = text.match( /\(([^)]+)\):/ ); @@ -471,7 +471,7 @@ export default function transformCommitFactory( options = {} ) { /** * @param {Array.} commits - * @returns {Boolean} + * @returns {boolean} */ function isSquashMergeCommit( commits ) { const [ squashCommit ] = commits; @@ -491,41 +491,41 @@ export default function transformCommitFactory( options = {} ) { */ /** - * @typedef {Object} Commit + * @typedef {object} Commit * - * @property {Boolean} isPublicCommit Whether the commit should be added in the changelog. + * @property {boolean} isPublicCommit Whether the commit should be added in the changelog. * - * @property {String} rawType Type of the commit without any modifications. + * @property {string} rawType Type of the commit without any modifications. * - * @property {String|null} type Type of the commit (it can be modified). + * @property {string|null} type Type of the commit (it can be modified). * - * @property {String} header First line of the commit message. + * @property {string} header First line of the commit message. * - * @property {String} subject Subject of the commit. It's the header without the type. + * @property {string} subject Subject of the commit. It's the header without the type. * - * @property {Array.|null} scope Scope of the changes. + * @property {Array.|null} scope Scope of the changes. * - * @property {Array.} files A list of files tha the commit modified. + * @property {Array.} files A list of files tha the commit modified. * - * @property {String} hash The full commit SHA-1 id. + * @property {string} hash The full commit SHA-1 id. * - * @property {String} repositoryUrl The URL to the repository where the parsed commit has been done. + * @property {string} repositoryUrl The URL to the repository where the parsed commit has been done. ** - * @property {String|null} [body] Body of the commit message. + * @property {string|null} [body] Body of the commit message. * - * @property {String|null} [footer] Footer of the commit message. + * @property {string|null} [footer] Footer of the commit message. * * @property {Array.} [notes] Notes for the commit. * - * @property {Boolean} [skipCommitsLink] Whether to skip generating a URL to the commit by the generator. + * @property {boolean} [skipCommitsLink] Whether to skip generating a URL to the commit by the generator. */ /** - * @typedef {Object} CommitNote + * @typedef {object} CommitNote * - * @property {String} title Type of the note. + * @property {string} title Type of the note. * - * @property {String} text Text of the note. + * @property {string} text Text of the note. * - * @property {Array.} scope Scope of the note. + * @property {Array.} scope Scope of the note. */ diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js index c76d26e74..fbd8b0e97 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/transformcommitutils.js @@ -43,8 +43,8 @@ export const typesOrder = { /** * Returns an order of a message in the changelog. * - * @param {String} title - * @returns {Number} + * @param {string} title + * @returns {number} */ export function getTypeOrder( title ) { for ( const typeTitle of Object.keys( typesOrder ) ) { @@ -59,8 +59,8 @@ export function getTypeOrder( title ) { /** * Replaces reference to the user (`@name`) with a link to the user's profile. * - * @param {String} comment - * @returns {String} + * @param {string} comment + * @returns {string} */ export function linkToGithubUser( comment ) { return comment.replace( /(^|[\s(])@([\w-]+)(?![/\w-])/ig, ( matchedText, charBefore, nickName ) => { @@ -72,8 +72,8 @@ export function linkToGithubUser( comment ) { * Replaces reference to issue (#ID) with a link to the issue. * If comment matches to "organization/repository#ID", link will lead to the specified repository. * - * @param {String} comment - * @returns {String} + * @param {string} comment + * @returns {string} */ export function linkToGithubIssue( comment ) { return comment.replace( /(\/?[\w-]+\/[\w-]+)?#([\d]+)(?=$|[\s,.)\]])/igm, ( matchedText, maybeRepository, issueId ) => { @@ -97,8 +97,8 @@ export function linkToGithubIssue( comment ) { * * The switch cases must be synchronized with the `MULTI_ENTRIES_COMMIT_REGEXP` regexp. * - * @param {String} commitType - * @returns {String} + * @param {string} commitType + * @returns {string} */ export function getCommitType( commitType ) { switch ( commitType ) { @@ -117,9 +117,9 @@ export function getCommitType( commitType ) { } /** - * @param {String} sentence - * @param {Number} length - * @returns {String} + * @param {string} sentence + * @param {number} length + * @returns {string} */ export function truncate( sentence, length ) { if ( sentence.length <= length ) { @@ -132,8 +132,8 @@ export function truncate( sentence, length ) { /** * Returns a URL to the repository whether the commit is being parsed. * - * @param {String} [cwd=process.cwd()] - * @returns {String} + * @param {string} [cwd=process.cwd()] + * @returns {string} */ export function getRepositoryUrl( cwd = process.cwd() ) { const packageJson = getPackageJson( cwd ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js b/packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js index a1b24298a..979e2be9d 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/truncatechangelog.js @@ -9,8 +9,8 @@ import saveChangelog from './savechangelog.js'; import getChangelog from './getchangelog.js'; /** - * @param {Number} length - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. + * @param {number} length + * @param {string} [cwd=process.cwd()] Where to look for the changelog file. */ export default function truncateChangelog( length, cwd = process.cwd() ) { const changelog = getChangelog( cwd ); diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js b/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js index 999046324..6b5afe9f7 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/validaterepositorytorelease.js @@ -6,12 +6,12 @@ import { tools } from '@ckeditor/ckeditor5-dev-utils'; /** - * @param {Object} options - * @param {String|null} options.version Version of the current release. - * @param {String} options.changes Changelog entries for the current release. - * @param {Boolean} [options.ignoreBranchCheck=false] If set on true, branch checking will be skipped. - * @param {String} [options.branch='master'] A name of the branch that should be used for releasing packages. - * @returns {Promise.>} + * @param {object} options + * @param {string|null} options.version Version of the current release. + * @param {string} options.changes Changelog entries for the current release. + * @param {boolean} [options.ignoreBranchCheck=false] If set on true, branch checking will be skipped. + * @param {string} [options.branch='master'] A name of the branch that should be used for releasing packages. + * @returns {Promise.>} */ export default async function validateRepositoryToRelease( options ) { const { diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js index 0499955ac..556fe3de6 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/versions.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/versions.js @@ -10,8 +10,8 @@ import getPackageJson from './getpackagejson.js'; /** * Returns a last created version in changelog file. * - * @param {String} [cwd=process.cwd()] Where to look for the changelog file. - * @returns {String|null} + * @param {string} [cwd=process.cwd()] Where to look for the changelog file. + * @returns {string|null} */ export function getLastFromChangelog( cwd = process.cwd() ) { const changelog = getChangelog( cwd ); @@ -31,8 +31,8 @@ export function getLastFromChangelog( cwd = process.cwd() ) { * If the package does not have any pre-releases with the provided identifier yet, `null` is returned. * * @param {ReleaseIdentifier} releaseIdentifier - * @param {String} [cwd=process.cwd()] - * @returns {Promise.} + * @param {string} [cwd=process.cwd()] + * @returns {Promise.} */ export function getLastPreRelease( releaseIdentifier, cwd = process.cwd() ) { const packageName = getPackageJson( cwd ).name; @@ -54,8 +54,8 @@ export function getLastPreRelease( releaseIdentifier, cwd = process.cwd() ) { * last nightly release and the "X" is the sequential number starting from 0. If the package does not have any nightly releases yet, * `null` is returned. * - * @param {String} [cwd=process.cwd()] - * @returns {Promise.} + * @param {string} [cwd=process.cwd()] + * @returns {Promise.} */ export function getLastNightly( cwd = process.cwd() ) { return getLastPreRelease( '0.0.0-nightly', cwd ); @@ -66,8 +66,8 @@ export function getLastNightly( cwd = process.cwd() ) { * next available pre-release sequential number starting from 0. * * @param {ReleaseIdentifier} releaseIdentifier - * @param {String} [cwd=process.cwd()] - * @returns {Promise} + * @param {string} [cwd=process.cwd()] + * @returns {Promise} */ export async function getNextPreRelease( releaseIdentifier, cwd = process.cwd() ) { const currentPreReleaseVersion = await getLastPreRelease( releaseIdentifier, cwd ); @@ -88,8 +88,8 @@ export async function getNextPreRelease( releaseIdentifier, cwd = process.cwd() * Returns the next available nightly version in the format of "0.0.0-nightly-YYYYMMDD.X", where the "YYYYMMDD" is the current date for * the nightly release and the "X" is the sequential number starting from 0. * - * @param {String} [cwd=process.cwd()] - * @returns {Promise} + * @param {string} [cwd=process.cwd()] + * @returns {Promise} */ export async function getNextNightly( cwd = process.cwd() ) { const today = new Date(); @@ -105,7 +105,7 @@ export async function getNextNightly( cwd = process.cwd() ) { /** * Returns a name of the last created tag. * - * @returns {String|null} + * @returns {string|null} */ export function getLastTagFromGit() { try { @@ -121,15 +121,15 @@ export function getLastTagFromGit() { /** * Returns version of current package from `package.json`. * - * @param {String} [cwd=process.cwd()] Current work directory. - * @returns {String} + * @param {string} [cwd=process.cwd()] Current work directory. + * @returns {string} */ export function getCurrent( cwd = process.cwd() ) { return getPackageJson( cwd ).version; } /** - * @typedef {String} ReleaseIdentifier The pre-release identifier without the last dynamic part (the pre-release sequential number). + * @typedef {string} ReleaseIdentifier The pre-release identifier without the last dynamic part (the pre-release sequential number). * It consists of the core base version (".."), a hyphen ("-"), and a pre-release identifier name (e.g. "alpha"). * * Examples: diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js index 8d85feb05..29b81e94b 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/generatechangelog.js @@ -1040,7 +1040,7 @@ function replaceDates( changelog ) { } /** - * @param {Number} length + * @param {number} length * @returns {WriterOptionsTransformCallback} */ function transformCommitCallback( length ) { diff --git a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js index 418001bf9..fed7a0cd9 100755 --- a/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js +++ b/packages/ckeditor5-dev-stale-bot/bin/stale-bot.js @@ -208,7 +208,7 @@ async function handleActions( githubRepository, entries, actions, spinner ) { /** * Prints in the console a welcome message. * - * @param {Boolean} dryRun Indicates if dry run mode is enabled. + * @param {boolean} dryRun Indicates if dry run mode is enabled. */ function printWelcomeMessage( dryRun ) { const message = [ @@ -225,7 +225,7 @@ function printWelcomeMessage( dryRun ) { /** * Prints in the console status messages about actions required to be executed on found issues and pull requests. * - * @param {Boolean} dryRun Indicates if dry run mode is enabled. + * @param {boolean} dryRun Indicates if dry run mode is enabled. * @param {SearchResult} searchResult Found issues and pull requests that require an action. * @param {Options} options Configuration options. */ @@ -284,7 +284,7 @@ function printStatus( dryRun, searchResult, options ) { /** * Prints in the console issues and pull requests from a single section. * - * @param {String} statusMessage Section header. + * @param {string} statusMessage Section header. * @param {Array.} entries Found issues and pull requests. */ function printStatusSection( statusMessage, entries ) { @@ -296,7 +296,7 @@ function printStatusSection( statusMessage, entries ) { } /** - * @typedef {Object} SearchResult + * @typedef {object} SearchResult * @property {Array.} issuesOrPullRequestsToClose * @property {Array.} issuesOrPullRequestsToStale * @property {Array.} issuesOrPullRequestsToUnstale @@ -305,21 +305,21 @@ function printStatusSection( statusMessage, entries ) { */ /** - * @typedef {Object} Actions + * @typedef {object} Actions * @property {HandleActionsCommentToAdd} [commentToAdd] * @property {HandleActionsLabelsToAdd} [labelsToAdd] - * @property {Array.} [labelsToRemove] - * @property {Boolean} [close] + * @property {Array.} [labelsToRemove] + * @property {boolean} [close] */ /** * @callback HandleActionsLabelsToAdd * @param {IssueOrPullRequestResult} entry - * @returns {Array.} + * @returns {Array.} */ /** * @callback HandleActionsCommentToAdd * @param {IssueOrPullRequestResult} entry - * @returns {String} + * @returns {string} */ diff --git a/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js b/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js index 1da67107d..3886327be 100644 --- a/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js +++ b/packages/ckeditor5-dev-stale-bot/bin/utils/createspinner.js @@ -41,7 +41,7 @@ export default function createSpinner() { } /** - * @typedef {Object} Spinner + * @typedef {object} Spinner * @property {ora.Ora} instance * @property {Function} printStatus * @property {Function} onProgress diff --git a/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js b/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js index 1f6220d23..c113c4829 100644 --- a/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js +++ b/packages/ckeditor5-dev-stale-bot/bin/utils/parsearguments.js @@ -9,10 +9,10 @@ import upath from 'upath'; /** * Parses CLI arguments. * - * @param {Array.} args - * @returns {Object} result - * @returns {Boolean} result.dryRun - * @returns {String} result.configPath + * @param {Array.} args + * @returns {object} result + * @returns {boolean} result.dryRun + * @returns {string} result.configPath */ export default function parseArguments( args ) { const config = { diff --git a/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js b/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js index ccfa3e54b..aa055a397 100644 --- a/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js +++ b/packages/ckeditor5-dev-stale-bot/bin/utils/parseconfig.js @@ -8,7 +8,7 @@ import { subDays, formatISO } from 'date-fns'; /** * Converts configuration options into format required by the GitHubRepository. * - * @param {String} viewerLogin The GitHub login of the currently authenticated user. + * @param {string} viewerLogin The GitHub login of the currently authenticated user. * @param {Config} config Configuration options. * @returns {Options} */ @@ -64,46 +64,46 @@ export default function parseConfig( viewerLogin, config ) { } /** - * @typedef {Object} Config - * @property {String} GITHUB_TOKEN - * @property {String} REPOSITORY_SLUG - * @property {Array.} STALE_LABELS - * @property {String} STALE_ISSUE_MESSAGE - * @property {String} STALE_PR_MESSAGE - * @property {Array.} CLOSE_ISSUE_LABELS - * @property {String} CLOSE_ISSUE_MESSAGE - * @property {Array.} CLOSE_PR_LABELS - * @property {String} CLOSE_PR_MESSAGE - * @property {String} [STALE_PENDING_ISSUE_MESSAGE=STALE_ISSUE_MESSAGE] - * @property {Array.} [PENDING_ISSUE_LABELS=[]] - * @property {Number} [DAYS_BEFORE_STALE=365] - * @property {Number} [DAYS_BEFORE_STALE_PENDING_ISSUE=14] - * @property {Number} [DAYS_BEFORE_CLOSE=30] - * @property {Boolean} [IGNORE_VIEWER_ACTIVITY=true] - * @property {Array.} [IGNORED_ISSUE_LABELS=[]] - * @property {Array.} [IGNORED_PR_LABELS=[]] - * @property {Array.} [IGNORED_ACTIVITY_LABELS=[]] - * @property {Array.} [IGNORED_ACTIVITY_LOGINS=[]] + * @typedef {object} Config + * @property {string} GITHUB_TOKEN + * @property {string} REPOSITORY_SLUG + * @property {Array.} STALE_LABELS + * @property {string} STALE_ISSUE_MESSAGE + * @property {string} STALE_PR_MESSAGE + * @property {Array.} CLOSE_ISSUE_LABELS + * @property {string} CLOSE_ISSUE_MESSAGE + * @property {Array.} CLOSE_PR_LABELS + * @property {string} CLOSE_PR_MESSAGE + * @property {string} [STALE_PENDING_ISSUE_MESSAGE=STALE_ISSUE_MESSAGE] + * @property {Array.} [PENDING_ISSUE_LABELS=[]] + * @property {number} [DAYS_BEFORE_STALE=365] + * @property {number} [DAYS_BEFORE_STALE_PENDING_ISSUE=14] + * @property {number} [DAYS_BEFORE_CLOSE=30] + * @property {boolean} [IGNORE_VIEWER_ACTIVITY=true] + * @property {Array.} [IGNORED_ISSUE_LABELS=[]] + * @property {Array.} [IGNORED_PR_LABELS=[]] + * @property {Array.} [IGNORED_ACTIVITY_LABELS=[]] + * @property {Array.} [IGNORED_ACTIVITY_LOGINS=[]] */ /** - * @typedef {Object} Options - * @property {String} repositorySlug - * @property {String} staleDate - * @property {String} staleDatePendingIssue - * @property {String} closeDate - * @property {Array.} staleLabels - * @property {Boolean} shouldProcessPendingIssues - * @property {Array.} pendingIssueLabels - * @property {String} staleIssueMessage - * @property {String} stalePendingIssueMessage - * @property {String} stalePullRequestMessage - * @property {Array.} closeIssueLabels - * @property {String} closeIssueMessage - * @property {Array.} closePullRequestLabels - * @property {String} closePullRequestMessage - * @property {Array.} ignoredIssueLabels - * @property {Array.} ignoredPullRequestLabels - * @property {Array.} ignoredActivityLabels - * @property {Array.} ignoredActivityLogins + * @typedef {object} Options + * @property {string} repositorySlug + * @property {string} staleDate + * @property {string} staleDatePendingIssue + * @property {string} closeDate + * @property {Array.} staleLabels + * @property {boolean} shouldProcessPendingIssues + * @property {Array.} pendingIssueLabels + * @property {string} staleIssueMessage + * @property {string} stalePendingIssueMessage + * @property {string} stalePullRequestMessage + * @property {Array.} closeIssueLabels + * @property {string} closeIssueMessage + * @property {Array.} closePullRequestLabels + * @property {string} closePullRequestMessage + * @property {Array.} ignoredIssueLabels + * @property {Array.} ignoredPullRequestLabels + * @property {Array.} ignoredActivityLabels + * @property {Array.} ignoredActivityLogins */ diff --git a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js index 0f89d0654..d00404b05 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js +++ b/packages/ckeditor5-dev-stale-bot/lib/githubrepository.js @@ -73,7 +73,7 @@ export default class GitHubRepository { /** * Returns the GitHub login of the currently authenticated user. * - * @returns {Promise.} + * @returns {Promise.} */ async getViewerLogin() { return this.sendRequest( await this.queries.getViewerLogin ) @@ -90,7 +90,7 @@ export default class GitHubRepository { * * @param {'Issue'|'PullRequest'} type Type of resource to search. * @param {Options} options Configuration options. - * @param {Function} onProgress Callback function called each time a response is received. + * @param {function} onProgress Callback function called each time a response is received. * @param {PageInfo} [pageInfo] Describes the current page of the returned result. * @returns {Promise.} */ @@ -140,7 +140,7 @@ export default class GitHubRepository { * Searches for all stale issues and pull requests that should be closed or unstaled. * * @param {Options} options Configuration options. - * @param {Function} onProgress Callback function called each time a response is received. + * @param {function} onProgress Callback function called each time a response is received. * @param {PageInfo} [pageInfo] Describes the current page of the returned result. * @returns {Promise.} */ @@ -202,7 +202,7 @@ export default class GitHubRepository { * Searches for all pending issues that should be staled or unlabeled. * * @param {Options} options Configuration options. - * @param {Function} onProgress Callback function called each time a response is received. + * @param {function} onProgress Callback function called each time a response is received. * @param {PageInfo} [pageInfo] Describes the current page of the returned result. * @returns {Promise.} */ @@ -258,7 +258,7 @@ export default class GitHubRepository { /** * Fetches all timeline items for provided issue or pull request. * - * @param {String} nodeId Issue or pull request identifier for which we want to fetch timeline items. + * @param {string} nodeId Issue or pull request identifier for which we want to fetch timeline items. * @param {PageInfo} [pageInfo] Describes the current page of the returned result. * @returns {Promise.>} */ @@ -290,8 +290,8 @@ export default class GitHubRepository { /** * Adds new comment to the specified issue or pull request on GitHub. * - * @param {String} nodeId Issue or pull request identifier for which we want to add new comment. - * @param {String} comment Comment to add. + * @param {string} nodeId Issue or pull request identifier for which we want to add new comment. + * @param {string} comment Comment to add. * @returns {Promise} */ async addComment( nodeId, comment ) { @@ -311,9 +311,9 @@ export default class GitHubRepository { /** * Fetches the specified labels from GitHub. * - * @param {String} repositorySlug Identifies the repository, where the provided labels exist. - * @param {Array.} labelNames Label names to fetch. - * @returns {Promise.>} + * @param {string} repositorySlug Identifies the repository, where the provided labels exist. + * @param {Array.} labelNames Label names to fetch. + * @returns {Promise.>} */ async getLabels( repositorySlug, labelNames ) { if ( !labelNames.length ) { @@ -344,8 +344,8 @@ export default class GitHubRepository { /** * Adds new labels to the specified issue or pull request on GitHub. * - * @param {String} nodeId Issue or pull request identifier for which we want to add labels. - * @param {Array.} labelIds Labels to add. + * @param {string} nodeId Issue or pull request identifier for which we want to add labels. + * @param {Array.} labelIds Labels to add. * @returns {Promise} */ async addLabels( nodeId, labelIds ) { @@ -365,8 +365,8 @@ export default class GitHubRepository { /** * Removes labels from the specified issue or pull request on GitHub. * - * @param {String} nodeId Issue or pull request identifier for which we want to remove labels. - * @param {Array.} labelIds Labels to remove. + * @param {string} nodeId Issue or pull request identifier for which we want to remove labels. + * @param {Array.} labelIds Labels to remove. * @returns {Promise} */ async removeLabels( nodeId, labelIds ) { @@ -387,7 +387,7 @@ export default class GitHubRepository { * Closes issue or pull request. * * @param {'Issue'|'PullRequest'} type Type of resource to close. - * @param {String} nodeId Issue or pull request identifier to close. + * @param {string} nodeId Issue or pull request identifier to close. * @returns {Promise} */ async closeIssueOrPullRequest( type, nodeId ) { @@ -409,10 +409,10 @@ export default class GitHubRepository { * Prepares the page pointers and search options for the next search request. * * @private - * @param {Object} data Received response to parse. + * @param {object} data Received response to parse. * @param {Options} options Configuration options. * @param {PageInfo} pageInfo Describes the current page of the returned result. - * @returns {Object} result + * @returns {object} result * @returns {PageInfo} result.nextPageInfo * @returns {Options} result.nextOptions */ @@ -457,7 +457,7 @@ export default class GitHubRepository { * initial request. * * @private - * @param {Object} data Received response to parse. + * @param {object} data Received response to parse. * @returns {Promise.>} */ parseIssuesOrPullRequests( data ) { @@ -484,7 +484,7 @@ export default class GitHubRepository { * Parses the received array of timeline items for an issue or pull request. * * @private - * @param {Object} data Received response to parse. + * @param {object} data Received response to parse. * @returns {Array.} */ parseIssueOrPullRequestTimelineItems( data ) { @@ -514,7 +514,7 @@ export default class GitHubRepository { * initial request. * * @private - * @param {Object} data Received response to parse. + * @param {object} data Received response to parse. * @returns {Array.} */ parsePendingIssues( data ) { @@ -537,9 +537,9 @@ export default class GitHubRepository { * Then, the request is sent again. * * @private - * @param {String} query The GraphQL query to send. - * @param {Object} [variables={}] Variables required by the GraphQL query. - * @returns {Promise.} + * @param {string} query The GraphQL query to send. + * @param {object} [variables={}] Variables required by the GraphQL query. + * @returns {Promise.} */ async sendRequest( query, variables = {} ) { return this.graphql.request( query, variables ) @@ -566,8 +566,8 @@ export default class GitHubRepository { /** * Reads the GraphQL query from filesystem. * - * @param {String} queryName Filename of the GraphQL query to read. - * @returns {Promise.} + * @param {string} queryName Filename of the GraphQL query to read. + * @returns {Promise.} */ function readGraphQL( queryName ) { return fs.readFile( upath.join( GRAPHQL_PATH, `${ queryName }.graphql` ), 'utf-8' ); @@ -577,7 +577,7 @@ function readGraphQL( queryName ) { * Parses the received error from GitHub API and checks if it concerns exceeding the API rate limit. If yes, it returns information when the * rate limit will be reset. * - * @param {Object} error An error that was received from the GitHub API. + * @param {object} error An error that was received from the GitHub API. * @returns {RateLimitExceeded} */ function checkApiRateLimit( error ) { @@ -632,69 +632,69 @@ function mapNodeToResult( node ) { } /** - * @typedef {Object} TimelineItem - * @property {String} eventDate - * @property {String} [author] - * @property {String} [label] + * @typedef {object} TimelineItem + * @property {string} eventDate + * @property {string} [author] + * @property {string} [label] */ /** - * @typedef {Object} Comment - * @property {String} createdAt - * @property {Boolean} isExternal + * @typedef {object} Comment + * @property {string} createdAt + * @property {boolean} isExternal */ /** - * @typedef {Object} IssueOrPullRequest - * @property {String} id + * @typedef {object} IssueOrPullRequest + * @property {string} id * @property {'Issue'|'PullRequest'} type - * @property {Number} number - * @property {String} title - * @property {String} url - * @property {String} createdAt - * @property {String|null} lastEditedAt - * @property {String|null} lastReactedAt + * @property {number} number + * @property {string} title + * @property {string} url + * @property {string} createdAt + * @property {string|null} lastEditedAt + * @property {string|null} lastReactedAt * @property {Array.} timelineItems */ /** - * @typedef {Object} PendingIssue - * @property {String} id + * @typedef {object} PendingIssue + * @property {string} id * @property {'Issue'} type - * @property {String} title - * @property {String} url - * @property {Array.} labels + * @property {string} title + * @property {string} url + * @property {Array.} labels * @property {Comment|null} lastComment */ /** - * @typedef {Object} IssueOrPullRequestResult - * @property {String} id + * @typedef {object} IssueOrPullRequestResult + * @property {string} id * @property {'Issue'|'PullRequest'} type - * @property {String} url - * @property {String} title + * @property {string} url + * @property {string} title */ /** - * @typedef {Object} PageInfo - * @property {Boolean} [hasNextPage] - * @property {String} [cursor] - * @property {Number} [done] - * @property {Number} [total] + * @typedef {object} PageInfo + * @property {boolean} [hasNextPage] + * @property {string} [cursor] + * @property {number} [done] + * @property {number} [total] */ /** - * @typedef {Object} Logger + * @typedef {object} Logger * @property {Function} info * @property {Function} warning * @property {Function} error */ /** - * @typedef {Object} RateLimitExceeded - * @property {Boolean} isExceeded - * @property {String} [resetDate] - * @property {Number} [timeToWait] + * @typedef {object} RateLimitExceeded + * @property {boolean} isExceeded + * @property {string} [resetDate] + * @property {number} [timeToWait] */ /** @@ -702,7 +702,7 @@ function mapNodeToResult( node ) { */ /** - * @typedef {Object} SearchStaleIssuesOrPullRequestsResult + * @typedef {object} SearchStaleIssuesOrPullRequestsResult * @property {Array.} issuesOrPullRequestsToClose * @property {Array.} issuesOrPullRequestsToUnstale */ diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js b/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js index f4156042c..4af89dbf1 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/findstaledate.js @@ -10,7 +10,7 @@ import { isAfter, parseISO } from 'date-fns'; * * @param {IssueOrPullRequest} issueOrPullRequest Issue or pull request to check. * @param {Options} options Configuration options. - * @returns {String} + * @returns {string} */ export default function findStaleDate( issueOrPullRequest, options ) { const { staleLabels } = options; diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js index 843082da3..3e591eed4 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequestactive.js @@ -19,9 +19,9 @@ import { isAfter, parseISO } from 'date-fns'; * Some activity entries may be ignored and not used in the calculation, if so specified in the configuration (e.g. the author of an event). * * @param {IssueOrPullRequest} issueOrPullRequest Issue or pull request to check. - * @param {String} staleDate Date specifying the moment of checking the activity in the issue or pull request. + * @param {string} staleDate Date specifying the moment of checking the activity in the issue or pull request. * @param {Options} options Configuration options. - * @returns {Boolean} + * @returns {boolean} */ export default function isIssueOrPullRequestActive( issueOrPullRequest, staleDate, options ) { const { ignoredActivityLogins, ignoredActivityLabels } = options; diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js index 97b5b70a9..b9f709d9f 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttoclose.js @@ -12,7 +12,7 @@ import findStaleDate from './findstaledate.js'; * * @param {IssueOrPullRequest} issueOrPullRequest Issue or pull request to check. * @param {Options} options Configuration options. - * @returns {Boolean} + * @returns {boolean} */ export default function isIssueOrPullRequestToClose( issueOrPullRequest, options ) { const staleDate = findStaleDate( issueOrPullRequest, options ); diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js index 12c6c12cf..4b9d254af 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttostale.js @@ -10,7 +10,7 @@ import isIssueOrPullRequestActive from './isissueorpullrequestactive.js'; * * @param {IssueOrPullRequest} issueOrPullRequest Issue or pull request to check. * @param {Options} options Configuration options. - * @returns {Boolean} + * @returns {boolean} */ export default function isIssueOrPullRequestToStale( issueOrPullRequest, options ) { const { staleDate } = options; diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js index 7d0d04348..a9252c6ff 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/isissueorpullrequesttounstale.js @@ -11,7 +11,7 @@ import findStaleDate from './findstaledate.js'; * * @param {IssueOrPullRequest} issueOrPullRequest Issue or pull request to check. * @param {Options} options Configuration options. - * @returns {Boolean} + * @returns {boolean} */ export default function isIssueOrPullRequestToUnstale( issueOrPullRequest, options ) { const staleDate = findStaleDate( issueOrPullRequest, options ); diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js index d24656a21..8381ea826 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuestale.js @@ -8,7 +8,7 @@ * * @param {PendingIssue} pendingIssue Pending issue to check. * @param {Options} options Configuration options. - * @returns {Boolean} + * @returns {boolean} */ export default function isPendingIssueStale( pendingIssue, options ) { return options.staleLabels.every( staleLabel => pendingIssue.labels.includes( staleLabel ) ); diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js index 049e28712..13fd1b00c 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetostale.js @@ -11,7 +11,7 @@ import isPendingIssueStale from './ispendingissuestale.js'; * * @param {PendingIssue} pendingIssue Pending issue to check. * @param {Options} options Configuration options. - * @returns {Boolean} + * @returns {boolean} */ export default function isPendingIssueToStale( pendingIssue, options ) { const { lastComment } = pendingIssue; diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js index c27877b1f..f72856545 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/ispendingissuetounlabel.js @@ -7,7 +7,7 @@ * Checks whether pending issue should be unlabeled, because it was answered by a community member. * * @param {PendingIssue} pendingIssue Pending issue to check. - * @returns {Boolean} + * @returns {boolean} */ export default function isPendingIssueToUnlabel( pendingIssue ) { if ( !pendingIssue.lastComment ) { diff --git a/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js b/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js index 34faa943b..7e2c9fff7 100644 --- a/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js +++ b/packages/ckeditor5-dev-stale-bot/lib/utils/preparesearchquery.js @@ -6,13 +6,13 @@ /** * Creates a query to search for issues or pull requests. * - * @param {Object} options - * @param {String} options.repositorySlug - * @param {String} [options.searchDate] + * @param {object} options + * @param {string} options.repositorySlug + * @param {string} [options.searchDate] * @param {'Issue'|'PullRequest'} [options.type] - * @param {Array.} [options.labels=[]] - * @param {Array.} [options.ignoredLabels=[]] - * @returns {String} + * @param {Array.} [options.labels=[]] + * @param {Array.} [options.ignoredLabels=[]] + * @returns {string} */ export default function prepareSearchQuery( options ) { const { diff --git a/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js b/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js index bcb315fc1..14e0e40ed 100644 --- a/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js +++ b/packages/ckeditor5-dev-tests/lib/tasks/runmanualtests.js @@ -23,20 +23,20 @@ import requireDll from '../utils/requiredll.js'; /** * Main function that runs manual tests. * - * @param {Object} options - * @param {Array.} options.files Glob patterns specifying which tests to run. - * @param {String} options.themePath A path to the theme the PostCSS theme-importer plugin is supposed to load. - * @param {Boolean} [options.disableWatch=false] Whether to disable the watch mechanism. If set to true, changes in source files + * @param {object} options + * @param {Array.} options.files Glob patterns specifying which tests to run. + * @param {string} options.themePath A path to the theme the PostCSS theme-importer plugin is supposed to load. + * @param {boolean} [options.disableWatch=false] Whether to disable the watch mechanism. If set to true, changes in source files * will not trigger webpack. - * @param {String} [options.language] A language passed to `CKEditorTranslationsPlugin`. - * @param {Array.} [options.additionalLanguages] Additional languages passed to `CKEditorTranslationsPlugin`. - * @param {Number} [options.port] A port number used by the `createManualTestServer`. - * @param {String} [options.identityFile] A file that provides secret keys used in the test scripts. - * @param {String} [options.tsconfig] Path the TypeScript configuration file. - * @param {Boolean|null} [options.dll=null] If `null`, user is asked to create DLL builds, if they are required by test files. + * @param {string} [options.language] A language passed to `CKEditorTranslationsPlugin`. + * @param {Array.} [options.additionalLanguages] Additional languages passed to `CKEditorTranslationsPlugin`. + * @param {number} [options.port] A port number used by the `createManualTestServer`. + * @param {string} [options.identityFile] A file that provides secret keys used in the test scripts. + * @param {string} [options.tsconfig] Path the TypeScript configuration file. + * @param {boolean|null} [options.dll=null] If `null`, user is asked to create DLL builds, if they are required by test files. * If `true`, DLL builds are created automatically, if required by test files. User is not asked. * If `false`, DLL builds are not created. User is not asked. - * @param {Boolean} [options.silent=false] Whether to hide files that will be processed by the script. + * @param {boolean} [options.silent=false] Whether to hide files that will be processed by the script. * @returns {Promise} */ export default function runManualTests( options ) { @@ -111,7 +111,7 @@ export default function runManualTests( options ) { /** * Checks if building the DLLs is needed. * - * @param {Array.} sourceFiles + * @param {Array.} sourceFiles * @returns {Promise} */ function isDllBuildRequired( sourceFiles ) { @@ -189,7 +189,7 @@ export default function runManualTests( options ) { /** * Executes the script for building DLLs in the specified repository. * - * @param {String} repositoryPath + * @param {string} repositoryPath * @returns {Promise} */ function buildDllInRepository( repositoryPath ) { diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js index c4556ff09..acce3f4a5 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/attribute.js @@ -20,8 +20,8 @@ export default chai => { * * Negations works as well. * - * @param {String} key Key of attribute to assert. - * @param {String} [value] Attribute value to assert. + * @param {string} key Key of attribute to assert. + * @param {string} [value] Attribute value to assert. */ chai.Assertion.addMethod( 'attribute', function attributeAssertion( key, value ) { const obj = this._obj; diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js index 73f1d7d1c..87d7c5ce9 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/assertions/equal-markup.js @@ -35,7 +35,7 @@ export default chai => { * '[]foobar' * ); * - * @param {String} expected Markup to compare. + * @param {string} expected Markup to compare. */ chai.Assertion.addMethod( 'equalMarkup', function( expected ) { const actual = this._obj; diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js index 6e66ed469..1b4b6a68a 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getkarmaconfig.js @@ -18,8 +18,8 @@ const AVAILABLE_REPORTERS = [ ]; /** - * @param {Object} options - * @returns {Object} + * @param {object} options + * @returns {object} */ export default function getKarmaConfig( options ) { if ( !AVAILABLE_REPORTERS.includes( options.reporter ) ) { @@ -223,8 +223,8 @@ function getBrowsers( options ) { // Returns the array of configuration flags for given browser. // -// @param {String} browser -// @returns {Array.} +// @param {string} browser +// @returns {Array.} function getFlagsForBrowser( browser ) { const commonFlags = [ '--disable-background-timer-throttling', diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js index 1021e5da8..64fc4e03f 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/getwebpackconfig.js @@ -14,8 +14,8 @@ const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); /** - * @param {Object} options - * @returns {Object} + * @param {object} options + * @returns {object} */ export default function getWebpackConfigForAutomatedTests( options ) { const definitions = Object.assign( {}, getDefinitionsFromFile( options.identityFile ) ); diff --git a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js index d201b89ae..26c9cd9e9 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js +++ b/packages/ckeditor5-dev-tests/lib/utils/automated-tests/parsearguments.js @@ -9,8 +9,8 @@ import minimist from 'minimist'; import { tools, logger } from '@ckeditor/ckeditor5-dev-utils'; /** - * @param {Array.} args - * @returns {Object} + * @param {Array.} args + * @returns {object} */ export default function parseArguments( args ) { const log = logger(); @@ -115,8 +115,8 @@ export default function parseArguments( args ) { * Replaces all kebab-case keys in the `options` object with camelCase entries. * Kebab-case keys will be removed. * - * @param {Object} options - * @param {Array.} keys Kebab-case keys in `options` object. + * @param {object} options + * @param {Array.} keys Kebab-case keys in `options` object. */ function replaceKebabCaseWithCamelCase( options, keys ) { for ( const key of keys ) { @@ -130,7 +130,7 @@ export default function parseArguments( args ) { /** * Parses the `--debug` option. * - * @param {Object} options + * @param {object} options */ function parseDebugOption( options ) { if ( options.debug === 'false' || options.debug === false ) { @@ -153,7 +153,7 @@ export default function parseArguments( args ) { * * The `ckeditor5-` prefix will be removed. * - * @param {Object} options + * @param {object} options */ function parseRepositoriesOption( options ) { if ( !options.repositories.length ) { @@ -207,7 +207,7 @@ export default function parseArguments( args ) { * Parses the `--tsconfig` options to be an absolute path. If argument is not provided, * it will check if `tsconfig.test.json` file exists and use it if it does. * - * @param {Object} options + * @param {object} options */ function parseTsconfigPath( options ) { if ( options.tsconfig ) { @@ -224,8 +224,8 @@ export default function parseArguments( args ) { /** * Splits by a comma (`,`) all values specified under keys to array. * - * @param {Object} options - * @param {Array.} keys Kebab-case keys in `options` object. + * @param {object} options + * @param {Array.} keys Kebab-case keys in `options` object. */ function splitOptionsToArray( options, keys ) { for ( const key of keys ) { @@ -238,8 +238,8 @@ export default function parseArguments( args ) { /** * Returns a camel case value for specified kebab-case `value`. * - * @param {String} value Kebab-case string. - * @returns {String} + * @param {string} value Kebab-case string. + * @returns {string} */ function toCamelCase( value ) { return value.split( '-' ) @@ -254,8 +254,8 @@ export default function parseArguments( args ) { } /** - * @param {String} path - * @returns {Boolean} + * @param {string} path + * @returns {boolean} */ function isDirectory( path ) { try { diff --git a/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js b/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js index fdca410ff..467b803aa 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js +++ b/packages/ckeditor5-dev-tests/lib/utils/getdefinitionsfromfile.js @@ -9,8 +9,8 @@ import { createRequire } from 'module'; const require = createRequire( import.meta.url ); /** - * @param {String|null} definitionSource - * @returns {Object} + * @param {string|null} definitionSource + * @returns {object} */ export default function getDefinitionsFromFile( definitionSource ) { if ( !definitionSource ) { @@ -35,8 +35,8 @@ export default function getDefinitionsFromFile( definitionSource ) { } /** - * @param {String|null} definitionSource - * @returns {String|null} + * @param {string|null} definitionSource + * @returns {string|null} */ function normalizeDefinitionSource( definitionSource ) { // Passed an absolute path. diff --git a/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js b/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js index 431d76ec1..b673735d2 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js +++ b/packages/ckeditor5-dev-tests/lib/utils/getrelativefilepath.js @@ -15,9 +15,9 @@ import path from 'path'; * - /work/space/packages/ckeditor5-foo/tests/manual/foo.js -> ckeditor5-foo/tests/manual/foo.js * - /work/space/packages/ckeditor-foo/tests/manual/foo.js -> ckeditor-foo/tests/manual/foo.js * - * @param {String} filePath - * @param {String} [cwd=process.cwd()] - * @returns {String} + * @param {string} filePath + * @param {string} [cwd=process.cwd()] + * @returns {string} */ export default function getRelativeFilePath( filePath, cwd = process.cwd() ) { // The path ends with the directory separator. diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js index cad036e20..57a8d684a 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilehtmlfiles.js @@ -22,14 +22,14 @@ const reader = new commonmark.Parser(); const writer = new commonmark.HtmlRenderer(); /** - * @param {Object} options - * @param {String} options.buildDir A path where compiled files will be saved. - * @param {Array.} options.sourceFiles An array of paths to JavaScript files from manual tests to be compiled. - * @param {String} options.language A language passed to `CKEditorTranslationsPlugin`. - * @param {Boolean} options.disableWatch Whether to disable the watch mechanism. If set to true, changes in source files + * @param {object} options + * @param {string} options.buildDir A path where compiled files will be saved. + * @param {Array.} options.sourceFiles An array of paths to JavaScript files from manual tests to be compiled. + * @param {string} options.language A language passed to `CKEditorTranslationsPlugin`. + * @param {boolean} options.disableWatch Whether to disable the watch mechanism. If set to true, changes in source files * will not trigger webpack. - * @param {Array.} [options.additionalLanguages] Additional languages passed to `CKEditorTranslationsPlugin`. - * @param {Boolean} [options.silent=false] Whether to hide files that will be processed by the script. + * @param {Array.} [options.additionalLanguages] Additional languages passed to `CKEditorTranslationsPlugin`. + * @param {boolean} [options.silent=false] Whether to hide files that will be processed by the script. * @returns {Promise} */ export default function compileHtmlFiles( options ) { @@ -85,12 +85,12 @@ export default function compileHtmlFiles( options ) { } /** - * @param {String} buildDir An absolute path to the directory where the processed file should be saved. - * @param {Object} options - * @param {String} options.filePath An absolute path to the manual test assets without the extension. - * @param {String} options.template The HTML template which will be merged with the manual test HTML file. - * @param {Array.} options.languages Name of translations that should be added to the manual test. - * @param {Boolean} options.silent Whether to hide files that will be processed by the script. + * @param {string} buildDir An absolute path to the directory where the processed file should be saved. + * @param {object} options + * @param {string} options.filePath An absolute path to the manual test assets without the extension. + * @param {string} options.template The HTML template which will be merged with the manual test HTML file. + * @param {Array.} options.languages Name of translations that should be added to the manual test. + * @param {boolean} options.silent Whether to hide files that will be processed by the script. */ function compileHtmlFile( buildDir, options ) { const sourceFilePathBase = options.filePath; diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js index 600b8906f..0d204fe67 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/compilescripts.js @@ -9,18 +9,18 @@ import getRelativeFilePath from '../getrelativefilepath.js'; import requireDll from '../requiredll.js'; /** - * @param {Object} options - * @param {String} options.cwd Current working directory. Usually it points to the CKEditor 5 root directory. - * @param {String} options.buildDir A path where compiled files will be saved. - * @param {Array.} options.sourceFiles An array of paths to JavaScript files from manual tests to be compiled. - * @param {String} options.themePath A path to the theme the PostCSS theme-importer plugin is supposed to load. - * @param {String} options.language A language passed to `CKEditorTranslationsPlugin`. - * @param {Boolean} options.disableWatch Whether to disable the watch mechanism. If set to true, changes in source files + * @param {object} options + * @param {string} options.cwd Current working directory. Usually it points to the CKEditor 5 root directory. + * @param {string} options.buildDir A path where compiled files will be saved. + * @param {Array.} options.sourceFiles An array of paths to JavaScript files from manual tests to be compiled. + * @param {string} options.themePath A path to the theme the PostCSS theme-importer plugin is supposed to load. + * @param {string} options.language A language passed to `CKEditorTranslationsPlugin`. + * @param {boolean} options.disableWatch Whether to disable the watch mechanism. If set to true, changes in source files * will not trigger webpack. - * @param {Function} options.onTestCompilationStatus A callback called whenever the script compilation occurrs. - * @param {String} [options.tsconfig] Path the TypeScript configuration file. - * @param {Array.} [options.additionalLanguages] Additional languages passed to `CKEditorTranslationsPlugin`. - * @param {String} [options.identityFile] A file that provides secret keys used in the test scripts. + * @param {function} options.onTestCompilationStatus A callback called whenever the script compilation occurrs. + * @param {string} [options.tsconfig] Path the TypeScript configuration file. + * @param {Array.} [options.additionalLanguages] Additional languages passed to `CKEditorTranslationsPlugin`. + * @param {string} [options.identityFile] A file that provides secret keys used in the test scripts. * @returns {Promise} */ export default function compileManualTestScripts( options ) { diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js index 4baa9a4c3..429b1d1b8 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/createserver.js @@ -18,9 +18,9 @@ const __dirname = path.dirname( __filename ); /** * Basic HTTP server. * - * @param {String} sourcePath Base path where the compiler saved the files. - * @param {Number} [port=8125] Port to listen at. - * @param {Function} [onCreate] A callback called with the reference to the HTTP server when it is up and running. + * @param {string} sourcePath Base path where the compiler saved the files. + * @param {number} [port=8125] Port to listen at. + * @param {function} [onCreate] A callback called with the reference to the HTTP server when it is up and running. */ export default function createManualTestServer( sourcePath, port = 8125, onCreate ) { return new Promise( resolve => { @@ -94,8 +94,8 @@ function onRequest( sourcePath, request, response ) { // Returns content type based on file extension. // -// @params {String} fileExtension -// @returns {String} +// @params {string} fileExtension +// @returns {string} function getContentType( fileExtension ) { switch ( fileExtension ) { case '.js': @@ -124,8 +124,8 @@ function getContentType( fileExtension ) { // Generates a list with available manual tests. // -// @param {String} sourcePath Base path that will be used to resolve all patterns. -// @returns {String} +// @param {string} sourcePath Base path that will be used to resolve all patterns. +// @returns {string} function generateIndex( sourcePath ) { const viewTemplate = fs.readFileSync( path.join( __dirname, 'template.html' ), 'utf-8' ); const globPattern = path.join( sourcePath, '**', '*.html' ).replace( /\\/g, '/' ); diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js index 5f918c110..913121759 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/getwebpackconfig.js @@ -18,18 +18,18 @@ const __dirname = path.dirname( __filename ); const require = createRequire( import.meta.url ); /** - * @param {Object} options - * @param {String} options.cwd Current working directory. Usually it points to the CKEditor 5 root directory. - * @param {Boolean} options.requireDll A flag describing whether DLL builds are required for starting the manual test server. - * @param {Object} options.entries - * @param {String} options.buildDir - * @param {String} options.themePath - * @param {Boolean} options.disableWatch - * @param {String} [options.tsconfig] - * @param {String} [options.language] - * @param {Array.} [options.additionalLanguages] - * @param {String|null} [options.identityFile] - * @returns {Object} + * @param {object} options + * @param {string} options.cwd Current working directory. Usually it points to the CKEditor 5 root directory. + * @param {boolean} options.requireDll A flag describing whether DLL builds are required for starting the manual test server. + * @param {object} options.entries + * @param {string} options.buildDir + * @param {string} options.themePath + * @param {boolean} options.disableWatch + * @param {string} [options.tsconfig] + * @param {string} [options.language] + * @param {Array.} [options.additionalLanguages] + * @param {string|null} [options.identityFile] + * @returns {object} */ export default function getWebpackConfigForManualTests( options ) { const definitions = Object.assign( {}, getDefinitionsFromFile( options.identityFile ) ); diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js index 8bce9e107..b5b1186b5 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/removedir.js @@ -12,9 +12,9 @@ import chalk from 'chalk'; * * The `del` package protects you against deleting the current working directory and above. * - * @param {String} dir Directory to remove. - * @param {Object} [options={}] options - * @param {Boolean} [options.silent=false] Whether to hide the path to the directory on the console. + * @param {string} dir Directory to remove. + * @param {object} [options={}] options + * @param {boolean} [options.silent=false] Whether to hide the path to the directory on the console. * @returns {Promise} */ export default function removeDir( dir, options = {} ) { diff --git a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js index 48d9e80ed..e6f4eb4d0 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js +++ b/packages/ckeditor5-dev-tests/lib/utils/manual-tests/webpacknotifierplugin.js @@ -10,9 +10,9 @@ import { logger } from '@ckeditor/ckeditor5-dev-utils'; */ export default class WebpackNotifierPlugin { /** - * @param {Object} options - * @param {Function} options.onTestCompilationStatus - * @param {String} options.processName + * @param {object} options + * @param {function} options.onTestCompilationStatus + * @param {string} options.processName */ constructor( options ) { this.log = logger(); diff --git a/packages/ckeditor5-dev-tests/lib/utils/requiredll.js b/packages/ckeditor5-dev-tests/lib/utils/requiredll.js index ebf320ce8..ec8b69144 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/requiredll.js +++ b/packages/ckeditor5-dev-tests/lib/utils/requiredll.js @@ -6,8 +6,8 @@ /** * Returns `true` if any of the source files represent a DLL test. * - * @param {String|Array.} sourceFiles - * @returns {Boolean} + * @param {string|Array.} sourceFiles + * @returns {boolean} */ export default function requireDll( sourceFiles ) { sourceFiles = Array.isArray( sourceFiles ) ? sourceFiles : [ sourceFiles ]; diff --git a/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js b/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js index 7d60c4da6..62faf1625 100644 --- a/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js +++ b/packages/ckeditor5-dev-tests/lib/utils/transformfileoptiontotestglob.js @@ -18,9 +18,9 @@ const EXTERNAL_DIR_NAME = 'external'; * * "foo/bar/" - matches all tests from a package and a subdirectory. * * "foo/bar" - matches all tests from a package (or root) with specific filename. * - * @param {String} pattern A path or pattern to determine the tests to execute. - * @param {Boolean} [isManualTest=false] Whether the tests are manual or automated. - * @returns {Array.} + * @param {string} pattern A path or pattern to determine the tests to execute. + * @param {boolean} [isManualTest=false] Whether the tests are manual or automated. + * @returns {Array.} */ export default function transformFileOptionToTestGlob( pattern, isManualTest = false ) { if ( doesPatternMatchExternalRepositoryName( pattern ) ) { @@ -55,10 +55,10 @@ export default function transformFileOptionToTestGlob( pattern, isManualTest = f } /** - * @param {String} pattern - * @param {Object} [options] - * @param {Boolean} [options.isManualTest] Controlls the path for manual and automated tests. - * @returns {Array.} + * @param {string} pattern + * @param {object} [options] + * @param {boolean} [options.isManualTest] Controlls the path for manual and automated tests. + * @returns {Array.} */ function getExternalRepositoryGlob( pattern, { isManualTest } ) { const externalPath = path.join( process.cwd(), EXTERNAL_DIR_NAME ); @@ -73,8 +73,8 @@ function getExternalRepositoryGlob( pattern, { isManualTest } ) { } /** - * @param {String} pattern - * @returns {Boolean} + * @param {string} pattern + * @returns {boolean} */ function doesPatternMatchExternalRepositoryName( pattern ) { const externalPath = path.join( process.cwd(), EXTERNAL_DIR_NAME ); @@ -89,12 +89,12 @@ function doesPatternMatchExternalRepositoryName( pattern ) { } /** - * @param {String} pattern - * @param {Object} [options={}] - * @param {Boolean} [options.isManualTest=false] Whether the tests are manual or automated. - * @param {Boolean} [options.useCKEditorPrefix=false] If true, the returned path will use 'ckeditor' prefix instead of 'ckeditor5'. - * @param {Boolean} [options.externalPackages] If true, the returned path will contain "external\/**\/packages". - * @returns {String} + * @param {string} pattern + * @param {object} [options={}] + * @param {boolean} [options.isManualTest=false] Whether the tests are manual or automated. + * @param {boolean} [options.useCKEditorPrefix=false] If true, the returned path will use 'ckeditor' prefix instead of 'ckeditor5'. + * @param {boolean} [options.externalPackages] If true, the returned path will contain "external\/**\/packages". + * @returns {string} */ function transformSinglePattern( pattern, options ) { const chunks = pattern.match( /[a-z1-9|*-]+/g ); diff --git a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js index 00165f622..b7c4eafc6 100644 --- a/packages/ckeditor5-dev-transifex/lib/createpotfiles.js +++ b/packages/ckeditor5-dev-transifex/lib/createpotfiles.js @@ -16,14 +16,14 @@ const corePackageName = 'ckeditor5-core'; * Collects i18n messages for all packages using source messages from `t()` calls * and context files and saves them as POT files in the `build/.transifex` directory. * - * @param {Object} options - * @param {Array.} options.sourceFiles An array of source files that contain messages to translate. - * @param {Array.} options.packagePaths An array of paths to packages, which will be used to find message contexts. - * @param {String} options.corePackagePath A relative to `process.cwd()` path to the `@ckeditor/ckeditor5-core` package. - * @param {String} options.translationsDirectory An absolute path to the directory where the results should be saved. - * @param {Boolean} [options.ignoreUnusedCorePackageContexts=false] Whether to hide unused context errors related to + * @param {object} options + * @param {Array.} options.sourceFiles An array of source files that contain messages to translate. + * @param {Array.} options.packagePaths An array of paths to packages, which will be used to find message contexts. + * @param {string} options.corePackagePath A relative to `process.cwd()` path to the `@ckeditor/ckeditor5-core` package. + * @param {string} options.translationsDirectory An absolute path to the directory where the results should be saved. + * @param {boolean} [options.ignoreUnusedCorePackageContexts=false] Whether to hide unused context errors related to * the `@ckeditor/ckeditor5-core` package. - * @param {Boolean} [options.skipLicenseHeader=false] Whether to skip the license header in created `*.pot` files. + * @param {boolean} [options.skipLicenseHeader=false] Whether to skip the license header in created `*.pot` files. * @param {Logger} [options.logger] A logger. */ export default function createPotFiles( options ) { @@ -89,8 +89,8 @@ export default function createPotFiles( options ) { * Traverses all packages and returns a map of all found language contexts * (file content and file name). * - * @param {Array.} packagePaths An array of paths to packages, which will be used to find message contexts. - * @returns {Map.} + * @param {Array.} packagePaths An array of paths to packages, which will be used to find message contexts. + * @returns {Map.} */ function getPackageContexts( packagePaths, corePackagePath, langContextSuffix ) { // Add path to core package if not included in the package paths. @@ -137,10 +137,10 @@ function collectSourceMessages( { sourceFiles, logger } ) { } /** - * @param {Object} options - * @param {Map.} options.packageContexts A map of language contexts. + * @param {object} options + * @param {Map.} options.packageContexts A map of language contexts. * @param {Array.} options.sourceMessages An array of i18n source messages. - * @returns {Array.} + * @returns {Array.} */ function assertNoMissingContext( { packageContexts, sourceMessages } ) { const errors = []; @@ -162,10 +162,10 @@ function assertNoMissingContext( { packageContexts, sourceMessages } ) { } /** - * @param {Object} options - * @param {Map.} options.packageContexts A map of language contexts. + * @param {object} options + * @param {Map.} options.packageContexts A map of language contexts. * @param {Array.} options.sourceMessages An array of i18n source messages. - * @returns {Array.} + * @returns {Array.} */ function assertAllContextUsed( options ) { const { packageContexts, sourceMessages, ignoreUnusedCorePackageContexts, corePackagePath, langContextSuffix } = options; @@ -207,9 +207,9 @@ function assertAllContextUsed( options ) { } /** - * @param {Object} options - * @param {Map.} options.packageContexts A map of language contexts. - * @returns {Array.} + * @param {object} options + * @param {Map.} options.packageContexts A map of language contexts. + * @returns {Array.} */ function assertNoRepeatedContext( { packageContexts } ) { const errors = []; @@ -236,11 +236,11 @@ function removeExistingPotFiles( translationsDirectory ) { * Creates a POT file for the given package and POT file content. * The default place is `build/.transifex/[packageName]/en.pot`. * - * @param {Object} options + * @param {object} options * @param {Logger} options.logger - * @param {String} options.packageName - * @param {String} options.translationsDirectory - * @param {String} options.fileContent + * @param {string} options.packageName + * @param {string} options.translationsDirectory + * @param {string} options.fileContent */ function savePotFile( { packageName, fileContent, translationsDirectory, logger } ) { const outputFilePath = path.join( translationsDirectory, packageName, 'en.pot' ); @@ -253,7 +253,7 @@ function savePotFile( { packageName, fileContent, translationsDirectory, logger /** * Creates a POT file header. * - * @returns {String} + * @returns {string} */ function createPotFileHeader() { const year = new Date().getFullYear(); @@ -264,8 +264,8 @@ function createPotFileHeader() { /** * Returns source messages found in the given file with additional data (`filePath` and `packageName`). * - * @param {String} filePath - * @param {String} fileContent + * @param {string} filePath + * @param {string} fileContent * @returns {Array.} */ function getSourceMessagesFromFile( { filePath, fileContent, logger } ) { @@ -291,7 +291,7 @@ function getSourceMessagesFromFile( { filePath, fileContent, logger } ) { * Creates a POT file from the given i18n messages. * * @param {Array.} messages - * @returns {String} + * @returns {string} */ function createPotFileContent( messages ) { return messages.map( message => { @@ -322,28 +322,28 @@ function createPotFileContent( messages ) { } /** - * @param {String} packageDirectory + * @param {string} packageDirectory */ function containsContextFile( packageDirectory, langContextSuffix ) { return fs.existsSync( path.join( packageDirectory, langContextSuffix ) ); } /** - * @typedef {Object} Message + * @typedef {object} Message * - * @property {String} id - * @property {String} string - * @property {String} filePath - * @property {String} packagePath - * @property {String} context - * @property {String} [plural] + * @property {string} id + * @property {string} string + * @property {string} filePath + * @property {string} packagePath + * @property {string} context + * @property {string} [plural] */ /** - * @typedef {Object} Context + * @typedef {object} Context * - * @property {String} filePath A path to the context file. - * @property {Object} content The context file content - a map of messageId->messageContext records. - * @property {String} packagePath The owner of the context file. - * @property {String} packageName The owner package name. + * @property {string} filePath A path to the context file. + * @property {object} content The context file content - a map of messageId->messageContext records. + * @property {string} packagePath The owner of the context file. + * @property {string} packageName The owner package name. */ diff --git a/packages/ckeditor5-dev-transifex/lib/download.js b/packages/ckeditor5-dev-transifex/lib/download.js index d6c54f5a8..1d26b5b50 100644 --- a/packages/ckeditor5-dev-transifex/lib/download.js +++ b/packages/ckeditor5-dev-transifex/lib/download.js @@ -18,16 +18,16 @@ import { languageCodeMap } from './data/index.js'; * file is created, containing information about the packages and languages for which the translations could not be downloaded. This file is * then used next time this script is run: it will try to download translations only for packages and languages that failed previously. * - * @param {Object} config - * @param {String} config.organizationName Name of the organization to which the project belongs. - * @param {String} config.projectName Name of the project for downloading the translations. - * @param {String} config.token Token to the Transifex API. - * @param {Map.} config.packages A resource name -> package path map for which translations should be downloaded. + * @param {object} config + * @param {string} config.organizationName Name of the organization to which the project belongs. + * @param {string} config.projectName Name of the project for downloading the translations. + * @param {string} config.token Token to the Transifex API. + * @param {Map.} config.packages A resource name -> package path map for which translations should be downloaded. * The resource name must be the same as the name used in the Transifex service. The package path could be any local path fragment, where * the downloaded translation will be stored. The final path for storing the translations is a combination of the `config.cwd` with the * mentioned package path and the `lang/translations` subdirectory. - * @param {String} config.cwd Current work directory. - * @param {Boolean} [config.simplifyLicenseHeader=false] Whether to skip adding the contribute guide URL in the output `*.po` files. + * @param {string} config.cwd Current work directory. + * @param {boolean} [config.simplifyLicenseHeader=false] Whether to skip adding the contribute guide URL in the output `*.po` files. */ export default async function downloadTranslations( config ) { const logger = createLogger(); @@ -113,11 +113,11 @@ export default async function downloadTranslations( config ) { * (2) Check if the language code should be mapped to another string on the filesystem. * (3) Prepare the translation for storing on the filesystem: remove personal data and add a banner with information how to contribute. * - * @param {Object} config - * @param {String} config.pathToTranslations Path to translations. - * @param {Map.} config.translations The translation map: language code -> translation content. - * @param {Boolean} config.simplifyLicenseHeader Whether to skip adding the contribute guide URL in the output `*.po` files. - * @returns {Number} Number of saved files. + * @param {object} config + * @param {string} config.pathToTranslations Path to translations. + * @param {Map.} config.translations The translation map: language code -> translation content. + * @param {boolean} config.simplifyLicenseHeader Whether to skip adding the contribute guide URL in the output `*.po` files. + * @returns {number} Number of saved files. */ function saveNewTranslations( { pathToTranslations, translations, simplifyLicenseHeader } ) { let savedFiles = 0; @@ -149,13 +149,13 @@ function saveNewTranslations( { pathToTranslations, translations, simplifyLicens * (1) If previous download procedure ended successfully, all translations for all resources will be downloaded. * (2) Otherwise, only packages and their failed translation downloads defined in `.transifex-failed-downloads.json` are taken into account. * - * @param {Object} config - * @param {String} config.cwd Current work directory. - * @param {Array.} config.resources All found resource instances for which translations could be downloaded. - * @param {Array.} config.languages All found language instances in the project. - * @returns {Object} result - * @returns {Boolean} result.isFailedDownloadFileAvailable Indicates whether previous download procedure did not fetch all translations. - * @returns {Array.} result.resourcesToProcess Resource instances and their associated language instances to use during downloading + * @param {object} config + * @param {string} config.cwd Current work directory. + * @param {Array.} config.resources All found resource instances for which translations could be downloaded. + * @param {Array.} config.languages All found language instances in the project. + * @returns {object} result + * @returns {boolean} result.isFailedDownloadFileAvailable Indicates whether previous download procedure did not fetch all translations. + * @returns {Array.} result.resourcesToProcess Resource instances and their associated language instances to use during downloading * the translations. */ function getResourcesToProcess( { cwd, resources, languages } ) { @@ -193,9 +193,9 @@ function getResourcesToProcess( { cwd, resources, languages } ) { /** * Saves all the failed downloads to `.transifex-failed-downloads.json` file. If there are no failures, the file is removed. * - * @param {Object} config - * @param {String} config.cwd Current work directory. - * @param {Array.} config.failedDownloads Collection of all the failed downloads. + * @param {object} config + * @param {string} config.cwd Current work directory. + * @param {Array.} config.failedDownloads Collection of all the failed downloads. */ function updateFailedDownloads( { cwd, failedDownloads } ) { const pathToFailedDownloads = getPathToFailedDownloads( cwd ); @@ -224,8 +224,8 @@ function updateFailedDownloads( { cwd, failedDownloads } ) { /** * Checks if the received data is a translation. * - * @param {String} poFileContent Received data. - * @returns {Boolean} + * @param {string} poFileContent Received data. + * @returns {boolean} */ function isPoFileContainingTranslations( poFileContent ) { const translations = createDictionaryFromPoFileContent( poFileContent ); @@ -237,8 +237,8 @@ function isPoFileContainingTranslations( poFileContent ) { /** * Returns an absolute path to the file containing failed downloads. * - * @param {String} cwd Current working directory. - * @returns {String} + * @param {string} cwd Current working directory. + * @returns {string} */ function getPathToFailedDownloads( cwd ) { return path.join( cwd, '.transifex-failed-downloads.json' ); diff --git a/packages/ckeditor5-dev-transifex/lib/gettoken.js b/packages/ckeditor5-dev-transifex/lib/gettoken.js index 09890291b..6db12f86d 100644 --- a/packages/ckeditor5-dev-transifex/lib/gettoken.js +++ b/packages/ckeditor5-dev-transifex/lib/gettoken.js @@ -8,7 +8,7 @@ import inquirer from 'inquirer'; /** * Takes username and password from prompt and returns promise that resolves with object that contains them. * - * @returns {Promise.} + * @returns {Promise.} */ export default async function getToken() { const { token } = await inquirer.prompt( [ { diff --git a/packages/ckeditor5-dev-transifex/lib/transifexservice.js b/packages/ckeditor5-dev-transifex/lib/transifexservice.js index d16e67ebb..e74eeb70b 100644 --- a/packages/ckeditor5-dev-transifex/lib/transifexservice.js +++ b/packages/ckeditor5-dev-transifex/lib/transifexservice.js @@ -33,7 +33,7 @@ export default { /** * Configures the API token for Transifex service if it has not been set yet. * - * @param {String} token Token for the Transifex API. + * @param {string} token Token for the Transifex API. */ function init( token ) { if ( !transifexApi.auth ) { @@ -44,10 +44,10 @@ function init( token ) { /** * Creates a new resource on Transifex. * - * @param {Object} options - * @param {String} options.organizationName The name of the organization to which the project belongs. - * @param {String} options.projectName The name of the project for creating the resource. - * @param {String} options.resourceName The name of the resource to create. + * @param {object} options + * @param {string} options.organizationName The name of the organization to which the project belongs. + * @param {string} options.projectName The name of the project for creating the resource. + * @param {string} options.resourceName The name of the resource to create. * @returns {Promise} */ async function createResource( options ) { @@ -77,12 +77,12 @@ async function createResource( options ) { /** * Uploads a new translations source for the specified resource (package). * - * @param {Object} options - * @param {String} options.organizationName The name of the organization to which the project belongs. - * @param {String} options.projectName The name of the project for uploading the translations entries. - * @param {String} options.resourceName The The name of resource. - * @param {String} options.content A content of the `*.po` file containing source for translations. - * @returns {Promise.} + * @param {object} options + * @param {string} options.organizationName The name of the organization to which the project belongs. + * @param {string} options.projectName The name of the project for uploading the translations entries. + * @param {string} options.resourceName The The name of resource. + * @param {string} options.content A content of the `*.po` file containing source for translations. + * @returns {Promise.} */ async function createSourceFile( options ) { const { organizationName, projectName, resourceName, content } = options; @@ -110,8 +110,8 @@ async function createSourceFile( options ) { * Resolves a promise containing an object with a summary of processing the uploaded source * file created by the Transifex service if the upload task is completed. * - * @param {String} uploadId - * @param {Number} [numberOfAttempts=1] A number containing a current attempt. + * @param {string} uploadId + * @param {number} [numberOfAttempts=1] A number containing a current attempt. * @returns {Promise} */ async function getResourceUploadDetails( uploadId, numberOfAttempts = 1 ) { @@ -140,12 +140,12 @@ async function getResourceUploadDetails( uploadId, numberOfAttempts = 1 ) { /** * Retrieves all the resources and languages associated with the requested project within given organization from the Transifex service. * - * @param {String} organizationName Name of the organization to which the project belongs. - * @param {String} projectName Name of the project for downloading the translations. - * @param {Array.} localizablePackageNames Names of all packages for which translations should be downloaded. - * @returns {Promise.} result - * @returns {Array.} result.resources All found resource instances for which translations could be downloaded. - * @returns {Array.} result.languages All found language instances in the project. + * @param {string} organizationName Name of the organization to which the project belongs. + * @param {string} projectName Name of the project for downloading the translations. + * @param {Array.} localizablePackageNames Names of all packages for which translations should be downloaded. + * @returns {Promise.} result + * @returns {Array.} result.resources All found resource instances for which translations could be downloaded. + * @returns {Array.} result.languages All found language instances in the project. */ async function getProjectData( organizationName, projectName, localizablePackageNames ) { const organization = await transifexApi.Organization.get( { slug: organizationName } ); @@ -187,11 +187,11 @@ async function getProjectData( organizationName, projectName, localizablePackage * The download procedure is not interrupted when any request has failed for any reason, but continues until the end for each language. * Failed requests and all fetched translations are collected and returned to the caller for further processing. * - * @param {Object} resource The resource instance for which translations should be downloaded. - * @param {Array.} languages An array of all the language instances found in the project. - * @returns {Promise.} result - * @returns {Map.} result.translations The translation map: language code -> translation content. - * @returns {Array.} result.failedDownloads Collection of all the failed downloads. + * @param {object} resource The resource instance for which translations should be downloaded. + * @param {Array.} languages An array of all the language instances found in the project. + * @returns {Promise.} result + * @returns {Map.} result.translations The translation map: language code -> translation content. + * @returns {Array.} result.failedDownloads Collection of all the failed downloads. */ async function getTranslations( resource, languages ) { const downloadRequests = await Promise @@ -241,9 +241,9 @@ async function getTranslations( resource, languages ) { * Fetches all the translations for the specified resource and language. The returned array contains translation items (objects) with * attributes and relationships to other Transifex entities. * - * @param {String} resourceId The resource id for which translation should be downloaded. - * @param {String} languageId The language id for which translation should be downloaded. - * @returns {Promise.>} + * @param {string} resourceId The resource id for which translation should be downloaded. + * @param {string} languageId The language id for which translation should be downloaded. + * @returns {Promise.>} */ async function getResourceTranslations( resourceId, languageId ) { const translations = transifexApi.ResourceTranslation @@ -274,10 +274,10 @@ async function getResourceTranslations( resourceId, languageId ) { /** * Creates the download request for the given resource and the language. * - * @param {Object} resource The resource instance for which translation should be downloaded. - * @param {Object} language The language instance for which translation should be downloaded. - * @param {Number} [numberOfAttempts=1] Current number of request attempt. - * @returns {Promise.} + * @param {object} resource The resource instance for which translation should be downloaded. + * @param {object} language The language instance for which translation should be downloaded. + * @param {number} [numberOfAttempts=1] Current number of request attempt. + * @returns {Promise.} */ function createDownloadRequest( resource, language, numberOfAttempts = 1 ) { const attributes = { @@ -327,12 +327,12 @@ function createDownloadRequest( resource, language, numberOfAttempts = 1 ) { * (3) Otherwise, there is either a problem with downloading a file (the request has failed) or the number of retries has reached the limit, * so rejected promise is returned. * - * @param {Object} downloadRequest Data that defines the requested file. - * @param {String} downloadRequest.url URL where generated PO file will be available to download. - * @param {String} downloadRequest.resourceName Package name for which the URL is generated. - * @param {String} downloadRequest.languageCode Language code for which the URL is generated. - * @param {Number} [numberOfAttempts=1] Current number of download attempt. - * @returns {Promise.>} The 2-element array: the language code at index 0 and the translation content at index 1. + * @param {object} downloadRequest Data that defines the requested file. + * @param {string} downloadRequest.url URL where generated PO file will be available to download. + * @param {string} downloadRequest.resourceName Package name for which the URL is generated. + * @param {string} downloadRequest.languageCode Language code for which the URL is generated. + * @param {number} [numberOfAttempts=1] Current number of download attempt. + * @returns {Promise.>} The 2-element array: the language code at index 0 and the translation content at index 1. */ async function downloadFile( downloadRequest, numberOfAttempts = 1 ) { const { url, resourceName, languageCode } = downloadRequest; @@ -374,8 +374,8 @@ async function downloadFile( downloadRequest, numberOfAttempts = 1 ) { /** * Retrieves the resource name (the package name) from the resource instance. * - * @param {Object} resource Resource instance. - * @returns {String} + * @param {object} resource Resource instance. + * @returns {string} */ function getResourceName( resource ) { return resource.attributes.slug; @@ -384,8 +384,8 @@ function getResourceName( resource ) { /** * Retrieves the language code from the language instance. * - * @param {Object} language Language instance. - * @returns {String} + * @param {object} language Language instance. + * @returns {string} */ function getLanguageCode( language ) { return language.attributes.code; @@ -395,7 +395,7 @@ function getLanguageCode( language ) { * Creates an artificial Transifex language instance for the source language, which is English. The language instance for the source strings * is needed, because Transifex service has two dedicated API resources: one for the translations and another one for the source strings. * - * @returns {Object} + * @returns {object} */ function createSourceLanguage() { return { @@ -408,8 +408,8 @@ function createSourceLanguage() { /** * Checks if the language instance is the source language, which is English. * - * @param {Object} language Language instance. - * @returns {Boolean} + * @param {object} language Language instance. + * @returns {boolean} */ function isSourceLanguage( language ) { return getLanguageCode( language ) === 'en'; @@ -418,7 +418,7 @@ function isSourceLanguage( language ) { /** * Returns results from each rejected promise, which are returned from the `Promise.allSettled()` method. * - * @param {Array.} results Collection of objects that each describes the outcome of each promise. + * @param {Array.} results Collection of objects that each describes the outcome of each promise. * @returns {Array.<*>} */ function getFailedResults( results ) { @@ -430,7 +430,7 @@ function getFailedResults( results ) { /** * Returns results from each fulfilled promise, which are returned from the `Promise.allSettled()` method. * - * @param {Array.} results Collection of objects that each describes the outcome of each promise. + * @param {Array.} results Collection of objects that each describes the outcome of each promise. * @returns {Array.<*>} */ function getSuccessfulResults( results ) { @@ -442,7 +442,7 @@ function getSuccessfulResults( results ) { /** * Simple promisified timeout that resolves after defined number of milliseconds. * - * @param {Number} numberOfMilliseconds Number of milliseconds after which the promise will be resolved. + * @param {number} numberOfMilliseconds Number of milliseconds after which the promise will be resolved. * @returns {Promise} */ function wait( numberOfMilliseconds ) { diff --git a/packages/ckeditor5-dev-transifex/lib/upload.js b/packages/ckeditor5-dev-transifex/lib/upload.js index 40ddba76d..6deb7c143 100644 --- a/packages/ckeditor5-dev-transifex/lib/upload.js +++ b/packages/ckeditor5-dev-transifex/lib/upload.js @@ -26,12 +26,12 @@ const RESOURCE_REGEXP = /r:(?[a-z0-9_-]+)$/i; * The Transifex API may end with an error at any stage. In such a case, the resource is not processed anymore. * It is saved to a file (`.transifex-failed-uploads.json`). Rerunning the script will process only packages specified in the file. * - * @param {Object} config - * @param {String} config.token Token to the Transifex API. - * @param {String} config.organizationName Name of the organization to which the project belongs. - * @param {String} config.projectName Name of the project for downloading the translations. - * @param {String} config.cwd Current work directory. - * @param {Map.} config.packages A resource name -> package path map for which translations should be uploaded. + * @param {object} config + * @param {string} config.token Token to the Transifex API. + * @param {string} config.organizationName Name of the organization to which the project belongs. + * @param {string} config.projectName Name of the project for downloading the translations. + * @param {string} config.cwd Current work directory. + * @param {Map.} config.packages A resource name -> package path map for which translations should be uploaded. * @returns {Promise} */ export default async function upload( config ) { @@ -266,9 +266,9 @@ function formatTableRow() { * * If the `packageName` is not specified, returns `true` if any error occurs. * - * @param {Object} [TRANSIFEX_RESOURCE_ERRORS] - * @param {String|null} [packageName=null] - * @returns {Boolean} + * @param {object} [TRANSIFEX_RESOURCE_ERRORS] + * @param {string|null} [packageName=null] + * @returns {boolean} */ function hasError( TRANSIFEX_RESOURCE_ERRORS, packageName = null ) { if ( !packageName ) { @@ -281,8 +281,8 @@ function hasError( TRANSIFEX_RESOURCE_ERRORS, packageName = null ) { /** * Creates a callback that stores errors from Transifex for the given `packageName`. * - * @param {Object} [TRANSIFEX_RESOURCE_ERRORS] - * @param {String} packageName + * @param {object} [TRANSIFEX_RESOURCE_ERRORS] + * @param {string} packageName * @param {CKEditor5Spinner|null} [spinner=null] * @returns {Function} */ @@ -299,8 +299,8 @@ function errorHandlerFactory( TRANSIFEX_RESOURCE_ERRORS, packageName, spinner ) } /** - * @param {String} pathToFile - * @returns {Promise.} + * @param {string} pathToFile + * @returns {Promise.} */ function isFile( pathToFile ) { return fs.lstat( pathToFile ) diff --git a/packages/ckeditor5-dev-transifex/lib/utils.js b/packages/ckeditor5-dev-transifex/lib/utils.js index 7f04fa5fd..939ce35c8 100644 --- a/packages/ckeditor5-dev-transifex/lib/utils.js +++ b/packages/ckeditor5-dev-transifex/lib/utils.js @@ -11,8 +11,8 @@ import { logger as loggerFactory } from '@ckeditor/ckeditor5-dev-utils'; * * Throws an error if any property is missing. * - * @param {Object} objectToCheck - * @param {Array.} properties + * @param {object} objectToCheck + * @param {Array.} properties */ export function verifyProperties( objectToCheck, properties ) { const nonExistingProperties = properties.filter( property => objectToCheck[ property ] === undefined ); @@ -25,7 +25,7 @@ export function verifyProperties( objectToCheck, properties ) { /** * Creates logger instance. * - * @returns {Object} logger + * @returns {object} logger * @returns {Function} logger.progress * @returns {Function} logger.info * @returns {Function} logger.warning diff --git a/packages/ckeditor5-dev-transifex/tests/upload.js b/packages/ckeditor5-dev-transifex/tests/upload.js index b72bbdc8a..65995558a 100644 --- a/packages/ckeditor5-dev-transifex/tests/upload.js +++ b/packages/ckeditor5-dev-transifex/tests/upload.js @@ -659,11 +659,11 @@ describe( 'dev-transifex/upload()', () => { /** * Returns an object that looks like a response from Transifex API. * - * @param {String} packageName - * @param {Number} created - * @param {Number} updated - * @param {Number} deleted - * @returns {Object} + * @param {string} packageName + * @param {number} created + * @param {number} updated + * @param {number} deleted + * @returns {object} */ function createResourceUploadDetailsResponse( packageName, created, updated, deleted ) { return { diff --git a/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js b/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js index 70c6b92cf..d789479b8 100644 --- a/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js +++ b/packages/ckeditor5-dev-translations/lib/ckeditortranslationsplugin.js @@ -104,37 +104,37 @@ export default class CKEditorTranslationsPlugin { /** * @callback AssetNamesFilter - * @param {String} name Webpack asset name/path - * @returns {Boolean} + * @param {string} name Webpack asset name/path + * @returns {boolean} */ /** - * @typedef {Object} CKEditorTranslationsPluginOptions CKEditorTranslationsPluginOptions options. + * @typedef {object} CKEditorTranslationsPluginOptions CKEditorTranslationsPluginOptions options. * - * @property {String} language The main language for internationalization - translations for that language + * @property {string} language The main language for internationalization - translations for that language * will be added to the bundle(s). - * @property {Array.|'all'} [additionalLanguages] Additional languages. Build is optimized when this option is not set. + * @property {Array.|'all'} [additionalLanguages] Additional languages. Build is optimized when this option is not set. * When `additionalLanguages` is set to 'all' then script will be looking for all languages and according translations during * the compilation. - * @property {String} [outputDirectory='translations'] The output directory for the emitted translation files, + * @property {string} [outputDirectory='translations'] The output directory for the emitted translation files, * should be relative to the webpack context. - * @property {Boolean} [strict] An option that make the plugin throw when the error is found during the compilation. - * @property {Boolean} [verbose] An option that make this plugin log all warnings into the console. - * @property {Boolean} [addMainLanguageTranslationsToAllAssets] An option that allows outputting translations to more than one + * @property {boolean} [strict] An option that make the plugin throw when the error is found during the compilation. + * @property {boolean} [verbose] An option that make this plugin log all warnings into the console. + * @property {boolean} [addMainLanguageTranslationsToAllAssets] An option that allows outputting translations to more than one * JS asset. - * @property {String} [corePackageSampleResourcePath] A path (ES6 import) to the file that determines whether the `ckeditor5-core` package + * @property {string} [corePackageSampleResourcePath] A path (ES6 import) to the file that determines whether the `ckeditor5-core` package * exists. The package contains common translations used by many packages. To avoid duplications, they are shared by the core package. - * @property {String} [corePackageContextsResourcePath] A path (ES6 import) to the file where all contexts are specified + * @property {string} [corePackageContextsResourcePath] A path (ES6 import) to the file where all contexts are specified * for the `ckeditor5-core` package. - * @property {Boolean} [buildAllTranslationsToSeparateFiles] An option that makes all translations output to separate files. - * @property {String} [sourceFilesPattern] An option that allows override the default pattern for CKEditor 5 source files. - * @property {String} [packageNamesPattern] An option that allows override the default pattern for CKEditor 5 package names. - * @property {String} [corePackagePattern] An option that allows override the default CKEditor 5 core package pattern. - * @property {String|Function|RegExp} [translationsOutputFile] An option allowing outputting all translation file to the given file. + * @property {boolean} [buildAllTranslationsToSeparateFiles] An option that makes all translations output to separate files. + * @property {string} [sourceFilesPattern] An option that allows override the default pattern for CKEditor 5 source files. + * @property {string} [packageNamesPattern] An option that allows override the default pattern for CKEditor 5 package names. + * @property {string} [corePackagePattern] An option that allows override the default CKEditor 5 core package pattern. + * @property {string|Function|RegExp} [translationsOutputFile] An option allowing outputting all translation file to the given file. * If a file specified by a path (string) does not exist, then it will be created. Otherwise, translations will be outputted to the file. - * @property {Boolean} [includeCorePackageTranslations=false] A flag that determines whether all translations found in the core package + * @property {boolean} [includeCorePackageTranslations=false] A flag that determines whether all translations found in the core package * should be added to the output bundle file. If set to true, translations from the core package will be saved even if are not * used in the source code (*.js files). - * @property {Boolean} [skipPluralFormFunction=false] Whether the `getPluralForm()` function should be added in the output bundle file. + * @property {boolean} [skipPluralFormFunction=false] Whether the `getPluralForm()` function should be added in the output bundle file. * @property {AssetNamesFilter} [assetNamesFilter] A function to filter assets probably importing CKEditor 5 modules. */ diff --git a/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js b/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js index 0bf291c8b..e695ab4e6 100644 --- a/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js +++ b/packages/ckeditor5-dev-translations/lib/cleanpofilecontent.js @@ -9,10 +9,10 @@ import PO from 'pofile'; * Returns translations stripped from the personal data, but with an added banner * containing information where to add new translations or fix the existing ones. * - * @param {String} poFileContent Content of the translation file. - * @param {Object} [options={}] - * @param {Boolean} [options.simplifyLicenseHeader] Whether to skip adding the contribute URL in the header. - * @returns {String} + * @param {string} poFileContent Content of the translation file. + * @param {object} [options={}] + * @param {boolean} [options.simplifyLicenseHeader] Whether to skip adding the contribute URL in the header. + * @returns {string} */ export default function cleanPoFileContent( poFileContent, options = {} ) { const po = PO.parse( poFileContent ); diff --git a/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js b/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js index 4d0cfb944..9cf6a83a3 100644 --- a/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js +++ b/packages/ckeditor5-dev-translations/lib/createdictionaryfrompofilecontent.js @@ -8,7 +8,7 @@ import PO from 'pofile'; /** * Returns object with key-value pairs from parsed po file. * - * @param {String} poFileContent Content of the translation file. + * @param {string} poFileContent Content of the translation file. * @returns {Object.} */ export default function createDictionaryFromPoFileContent( poFileContent ) { diff --git a/packages/ckeditor5-dev-translations/lib/findmessages.js b/packages/ckeditor5-dev-translations/lib/findmessages.js index 5e1369bd9..a228d70a1 100644 --- a/packages/ckeditor5-dev-translations/lib/findmessages.js +++ b/packages/ckeditor5-dev-translations/lib/findmessages.js @@ -9,11 +9,11 @@ import { default as traverse } from '@babel/traverse'; /** * Parses source and finds messages from the first argument of `t()` calls. * - * @param {String} source A content of the JS file that will be translated. - * @param {String} sourceFile A path to source file, used only for creating error messages. + * @param {string} source A content of the JS file that will be translated. + * @param {string} sourceFile A path to source file, used only for creating error messages. * @param {(msg: Message) => void} onMessageFound * @param {(err: string) => void} onErrorFound - * @returns {String} Transformed source. + * @returns {string} Transformed source. */ export default function findMessages( source, sourceFile, onMessageFound, onErrorFound ) { const ast = parser.parse( source, { @@ -144,9 +144,9 @@ function isTMethodCallExpression( node ) { } /** - * @typedef {Object} Message + * @typedef {object} Message * - * @property {String} id - * @property {String} string - * @property {String} [plural] + * @property {string} id + * @property {string} string + * @property {string} [plural] */ diff --git a/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js b/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js index 3fba89e99..2064768b2 100644 --- a/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js +++ b/packages/ckeditor5-dev-translations/lib/multiplelanguagetranslationservice.js @@ -15,16 +15,16 @@ import PO from 'pofile'; export default class MultipleLanguageTranslationService extends EventEmitter { // TODO maybe fix the jsdoc types /** - * @param {Object} options - * @param {String} options.mainLanguage The target language that will be bundled into the main webpack asset. - * @param {Array.} [options.additionalLanguages] Additional languages which files will be emitted. + * @param {object} options + * @param {string} options.mainLanguage The target language that will be bundled into the main webpack asset. + * @param {Array.} [options.additionalLanguages] Additional languages which files will be emitted. * When option is set to 'all', all languages found during the compilation will be added. - * @param {Boolean} [options.compileAllLanguages] When set to `true` languages will be found at runtime. - * @param {Boolean} [options.addMainLanguageTranslationsToAllAssets] When set to `true` the service will not complain + * @param {boolean} [options.compileAllLanguages] When set to `true` languages will be found at runtime. + * @param {boolean} [options.addMainLanguageTranslationsToAllAssets] When set to `true` the service will not complain * about multiple JS assets and will output translations for the main language to all found assets. - * @param {Boolean} [options.buildAllTranslationsToSeparateFiles] When set to `true` the service will output all translations + * @param {boolean} [options.buildAllTranslationsToSeparateFiles] When set to `true` the service will output all translations * to separate files. - * @param {String|Function|RegExp} [options.translationsOutputFile] An option allowing outputting all translation file + * @param {string|Function|RegExp} [options.translationsOutputFile] An option allowing outputting all translation file * to the given file. If a file specified by a path (string) does not exist, then it will be created. Otherwise, translations * will be outputted to the file. */ @@ -43,7 +43,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * Main language that should be built in to the bundle. * * @private - * @type {String} + * @type {string} */ this._mainLanguage = mainLanguage; @@ -52,7 +52,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * if the `compileAllLanguages` flag is turned on. * * @private - * @type {Set.} + * @type {Set.} */ this._languages = new Set( [ mainLanguage, ...additionalLanguages ] ); @@ -60,7 +60,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * An option indicating if the languages should be found at runtime. * * @private - * @type {Boolean} + * @type {boolean} */ this._compileAllLanguages = compileAllLanguages; @@ -69,7 +69,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * and will add translation for the main language to all of them. Useful option for manual tests, etc. * * @private - * @type {Boolean} + * @type {boolean} */ this._addMainLanguageTranslationsToAllAssets = addMainLanguageTranslationsToAllAssets; @@ -77,7 +77,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * A boolean option. When set to `true` outputs all translations to separate files. * * @private - * @type {Boolean} + * @type {boolean} */ this._buildAllTranslationsToSeparateFiles = buildAllTranslationsToSeparateFiles; @@ -85,7 +85,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * A set of handled packages that speeds up the translation process. * * @private - * @type {Set.} + * @type {Set.} */ this._handledPackages = new Set(); @@ -93,7 +93,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * A map of translation dictionaries in the `language -> messageId -> single & plural forms` format. * * @private - * @type {Object.>>} + * @type {Object.>>} */ this._translationDictionaries = {}; @@ -101,7 +101,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * Plural form rules that will be added to generated translation assets. * * @private - * @type {Object.} + * @type {Object.} */ this._pluralFormsRules = {}; @@ -110,7 +110,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * (with a single and possible plural forms) should be found for the target languages. * * @private - * @type {Set.} + * @type {Set.} */ this._foundMessageIds = new Set(); @@ -118,7 +118,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * Whether the `getPluralForm` function should be added in the bundle file. * * @private - * @type {Boolean} + * @type {boolean} */ this._skipPluralFormFunction = skipPluralFormFunction; @@ -130,9 +130,9 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * (e.g. an incorrect `t()` call). * * @fires warning - * @param {String} source Content of the source file. - * @param {String} fileName Source file name - * @returns {String} + * @param {string} source Content of the source file. + * @param {string} fileName Source file name + * @returns {string} */ translateSource( source, fileName ) { findMessages( @@ -150,7 +150,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * If the `compileAllLanguages` flag is set to `true`, then the language set will be expanded to all found languages. * * @fires warning - * @param {String} pathToPackage A path to the package containing translations. + * @param {string} pathToPackage A path to the package containing translations. */ loadPackage( pathToPackage ) { if ( this._handledPackages.has( pathToPackage ) ) { @@ -198,10 +198,10 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * * @fires warning * @fires error - * @param {Object} options - * @param {String} options.outputDirectory Output directory for the translation files relative to the output. - * @param {Array.} options.compilationAssetNames Original asset names from the compiler (e.g. Webpack). - * @returns {Array.} Returns new and modified assets that will be added to original ones. + * @param {object} options + * @param {string} options.outputDirectory Output directory for the translation files relative to the output. + * @param {Array.} options.compilationAssetNames Original asset names from the compiler (e.g. Webpack). + * @returns {Array.} Returns new and modified assets that will be added to original ones. */ getAssets( { outputDirectory, compilationAssetNames } ) { let bundledLanguage = this._mainLanguage; @@ -250,17 +250,17 @@ export default class MultipleLanguageTranslationService extends EventEmitter { /** * Adds the specified `id` to the collection which will be translated to the specified language. * - * @param {String} id + * @param {string} id */ addIdMessage( id ) { this._foundMessageIds.add( id ); } /** - * @param {Object} options - * @param {String} options.outputDirectory Output directory for the translation files relative to the output. - * @param {Array.} options.compilationAssetNames Original asset names from the compiler (e.g. Webpack). - * @returns {Array.} Returns an array with one asset that + * @param {object} options + * @param {string} options.outputDirectory Output directory for the translation files relative to the output. + * @param {Array.} options.compilationAssetNames Original asset names from the compiler (e.g. Webpack). + * @returns {Array.} Returns an array with one asset that */ _getAssetsWithTranslationsBundledToTheOutputFile( { outputDirectory, compilationAssetNames } ) { const assetName = match( this._translationsOutputFile, compilationAssetNames ); @@ -285,8 +285,8 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * Returns assets for the given directory and languages. * * @private - * @param {String} outputDirectory The output directory for assets. - * @param {Array.} languages Languages for assets. + * @param {string} outputDirectory The output directory for assets. + * @param {Array.} languages Languages for assets. */ _getTranslationAssets( outputDirectory, languages ) { // Sort the array of message ids to provide deterministic results. @@ -307,7 +307,7 @@ export default class MultipleLanguageTranslationService extends EventEmitter { // pluralForms="nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2)" // pluralForms="nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2" - /** @type {String} */ + /** @type {string} */ const pluralFormsRule = this._pluralFormsRules[ language ]; let pluralFormFunction; @@ -349,9 +349,9 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * Skips messages that lacks their translations. * * @private - * @param {String} language The target language - * @param {Array.} sortedMessageIds An array of sorted message ids. - * @returns {Object.} + * @param {string} language The target language + * @param {Array.} sortedMessageIds An array of sorted message ids. + * @returns {Object.} */ _getTranslations( language, sortedMessageIds ) { const langDictionary = this._translationDictionaries[ language ]; @@ -379,8 +379,8 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * Loads translations from the PO file if that file exists. * * @private - * @param {String} language PO file's language. - * @param {String} pathToPoFile Path to the target PO file. + * @param {string} language PO file's language. + * @param {string} pathToPoFile Path to the target PO file. */ _loadPoFile( language, pathToPoFile ) { if ( !fs.existsSync( pathToPoFile ) ) { @@ -406,8 +406,8 @@ export default class MultipleLanguageTranslationService extends EventEmitter { * Returns a path to the translation directory depending on the path to the package. * * @protected - * @param {String|null} relativePathToPackage - * @returns {String} + * @param {string|null} relativePathToPackage + * @returns {string} */ _getPathToTranslationDirectory( relativePathToPackage ) { // If the `relativePathToPackage` is not specified, translations for a single package are processed. @@ -420,9 +420,9 @@ export default class MultipleLanguageTranslationService extends EventEmitter { } /** - * @param {String|Function|RegExp} predicate - * @param {Array.} options - * @returns {String|undefined} + * @param {string|Function|RegExp} predicate + * @param {Array.} options + * @returns {string|undefined} */ function match( predicate, options ) { if ( typeof predicate === 'function' ) { diff --git a/packages/ckeditor5-dev-translations/lib/servetranslations.js b/packages/ckeditor5-dev-translations/lib/servetranslations.js index 57aef6946..d854a6d3f 100644 --- a/packages/ckeditor5-dev-translations/lib/servetranslations.js +++ b/packages/ckeditor5-dev-translations/lib/servetranslations.js @@ -21,14 +21,14 @@ const { RawSource, ConcatSource } = webpackSources; * * See https://webpack.js.org/api/compiler/#event-hooks and https://webpack.js.org/api/compilation/ for details about specific hooks. * - * @param {Object} compiler The webpack compiler. - * @param {Object} options Translation options. - * @param {String} options.outputDirectory The output directory for the emitted translation files, relative to the webpack context. - * @param {Boolean} [options.strict] An option that make this function throw when the error is found during the compilation. - * @param {Boolean} [options.verbose] An option that make this function log everything into the console. - * @param {String} [options.sourceFilesPattern] The source files pattern - * @param {String} [options.packageNamesPattern] The package names pattern. - * @param {String} [options.corePackagePattern] The core package pattern. + * @param {object} compiler The webpack compiler. + * @param {object} options Translation options. + * @param {string} options.outputDirectory The output directory for the emitted translation files, relative to the webpack context. + * @param {boolean} [options.strict] An option that make this function throw when the error is found during the compilation. + * @param {boolean} [options.verbose] An option that make this function log everything into the console. + * @param {string} [options.sourceFilesPattern] The source files pattern + * @param {string} [options.packageNamesPattern] The package names pattern. + * @param {string} [options.corePackagePattern] The core package pattern. * @param {AssetNamesFilter} [options.assetNamesFilter] A function to filter assets probably importing CKEditor 5 modules. * @param {TranslationService} translationService Translation service that will load PO files, replace translation keys and generate assets. * ckeditor5 - independent without hard-to-test logic. @@ -186,9 +186,9 @@ export default function serveTranslations( compiler, options, translationService /** * Return path to the package if the resource comes from `ckeditor5-*` package. * - * @param {String} cwd Current working directory. - * @param {String} resource Absolute path to the resource. - * @returns {String|null} + * @param {string} cwd Current working directory. + * @param {string} resource Absolute path to the resource. + * @returns {string|null} */ function getPathToPackage( cwd, resource, packageNamePattern ) { const relativePathToResource = path.relative( cwd, resource ); @@ -209,7 +209,7 @@ function getPathToPackage( cwd, resource, packageNamePattern ) { * * @param {webpack.Compiler} compiler * @param {webpack.Compilation} compilation - * @returns {Object} + * @returns {object} */ function getCompilationHooks( compiler, compilation ) { const { webpack } = compiler; @@ -227,8 +227,8 @@ function getCompilationHooks( compiler, compilation ) { /** * Returns an object with the chunk assets depending on the Webpack version. * - * @param {Object} compilation - * @returns {Object} + * @param {object} compilation + * @returns {object} */ function getChunkAssets( compilation ) { // Webpack 5 vs Webpack 4. @@ -238,7 +238,7 @@ function getChunkAssets( compilation ) { /** * Returns an array with list of loaded files depending on the Webpack version. * - * @param {Object|Array} chunks + * @param {object|Array} chunks * @returns {Array} */ function getFilesFromChunks( chunks ) { @@ -263,22 +263,22 @@ function getFilesFromChunks( chunks ) { * Load package translations. * * @method #loadPackage - * @param {String} pathToPackage Path to the package. + * @param {string} pathToPackage Path to the package. */ /** * Translate file's source to the target language. * * @method #translateSource - * @param {String} source File's source. - * @returns {String} + * @param {string} source File's source. + * @returns {string} */ /** * Get assets at the end of compilation. * * @method #getAssets - * @returns {Array.} + * @returns {Array.} */ /** diff --git a/packages/ckeditor5-dev-translations/lib/translatesourceloader.js b/packages/ckeditor5-dev-translations/lib/translatesourceloader.js index 3c92b4f5a..bcad21b96 100644 --- a/packages/ckeditor5-dev-translations/lib/translatesourceloader.js +++ b/packages/ckeditor5-dev-translations/lib/translatesourceloader.js @@ -7,8 +7,8 @@ * Very simple loader that runs the translateSource function only on the source. * translateSource is provided by the CKEditorTranslationsPlugin. * - * @param {String} source Content of the resource file - * @param {Object} map A source map consumed by the `source-map` package. + * @param {string} source Content of the resource file + * @param {object} map A source map consumed by the `source-map` package. */ export default function translateSourceLoader( source, map ) { const output = this.query.translateSource( source, this.resourcePath ); diff --git a/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js b/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js index db5aca77d..9fe5dd88e 100644 --- a/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js +++ b/packages/ckeditor5-dev-utils/lib/builds/getdllpluginwebpackconfig.js @@ -13,16 +13,16 @@ import { getIconsLoader, getStylesLoader, getTypeScriptLoader } from '../loaders * Returns a webpack configuration that creates a bundle file for the specified package. Thanks to that, plugins exported * by the package can be added to DLL builds. * - * @param {Object} webpack - * @param {Function} webpack.BannerPlugin Plugin used to add text to the top of the file. - * @param {Function} webpack.DllReferencePlugin Plugin used to import DLLs with webpack. - * @param {Object} options - * @param {String} options.themePath An absolute path to the theme package. - * @param {String} options.packagePath An absolute path to the root directory of the package. - * @param {String} options.manifestPath An absolute path to the CKEditor 5 DLL manifest file. - * @param {String} [options.tsconfigPath] An absolute path to the TypeScript configuration file. - * @param {Boolean} [options.isDevelopmentMode=false] Whether to build a dev mode of the package. - * @returns {Object} + * @param {object} webpack + * @param {function} webpack.BannerPlugin Plugin used to add text to the top of the file. + * @param {function} webpack.DllReferencePlugin Plugin used to import DLLs with webpack. + * @param {object} options + * @param {string} options.themePath An absolute path to the theme package. + * @param {string} options.packagePath An absolute path to the root directory of the package. + * @param {string} options.manifestPath An absolute path to the CKEditor 5 DLL manifest file. + * @param {string} [options.tsconfigPath] An absolute path to the TypeScript configuration file. + * @param {boolean} [options.isDevelopmentMode=false] Whether to build a dev mode of the package. + * @returns {object} */ export default async function getDllPluginWebpackConfig( webpack, options ) { // Terser requires webpack. However, it's needed in runtime. To avoid the "Cannot find module 'webpack'" error, @@ -131,8 +131,8 @@ export default async function getDllPluginWebpackConfig( webpack, options ) { * Transforms the package name (`@ckeditor/ckeditor5-foo-bar`) to the name that will be used while * exporting the library into the global scope. * - * @param {String} packageName - * @returns {String} + * @param {string} packageName + * @returns {string} */ function getGlobalKeyForPackage( packageName ) { return packageName @@ -144,7 +144,7 @@ function getGlobalKeyForPackage( packageName ) { * Extracts the main file name from the package name. * * @param packageName - * @returns {String} + * @returns {string} */ function getIndexFileName( packageName ) { return packageName.replace( /^@ckeditor\/ckeditor5?-/, '' ) + '.js'; diff --git a/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js b/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js index 6aace870c..41e28ab8a 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/ck-debug-loader.js @@ -9,7 +9,7 @@ * E.g. if the `CK_DEBUG_ENGINE` flag is set to true, then all lines starting with * `// @if CK_DEBUG_ENGINE //` will be uncommented. * - * @param {String} source + * @param {string} source * @param {any} map */ export default function ckDebugLoader( source, map ) { diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js index f42b2b1ec..e2c46f001 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/getcoverageloader.js @@ -8,9 +8,9 @@ import path from 'path'; const escapedPathSep = path.sep == '/' ? '/' : '\\\\'; /** - * @param {Object} options] - * @param {Array.} options.files - * @returns {Object} + * @param {object} options] + * @param {Array.} options.files + * @returns {object} */ export default function getCoverageLoader( { files } ) { return { @@ -39,8 +39,8 @@ export default function getCoverageLoader( { files } ) { * This loose way of matching packages for CC works with packages under various paths. * E.g., `workspace/ckeditor5-utils` and `ckeditor5/node_modules/ckeditor5-utils` and every other path. * - * @param {Array.} globs - * @returns {Array.} + * @param {Array.} globs + * @returns {Array.} */ function getPathsToIncludeForCoverage( globs ) { const values = globs diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js index d60b66081..4f514fb23 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/getdebugloader.js @@ -10,8 +10,8 @@ const __filename = fileURLToPath( import.meta.url ); const __dirname = path.dirname( __filename ); /** - * @param {Array.} debugFlags - * @returns {Object} + * @param {Array.} debugFlags + * @returns {object} */ export default function getDebugLoader( debugFlags ) { return { diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js index 253e0734e..e4c561964 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/getformattedtextloader.js @@ -4,7 +4,7 @@ */ /** - * @returns {Object} + * @returns {object} */ export default function getFormattedTextLoader() { return { diff --git a/packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js b/packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js index bf3e08875..73038923d 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/geticonsloader.js @@ -4,9 +4,9 @@ */ /** - * @param {Object} [options] - * @param {Boolean} [options.matchExtensionOnly] - * @returns {Object} + * @param {object} [options] + * @param {boolean} [options.matchExtensionOnly] + * @returns {object} */ export default function getIconsLoader( { matchExtensionOnly = false } = {} ) { return { diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js index fc36258c8..85e800575 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/getjavascriptloader.js @@ -6,9 +6,9 @@ import getDebugLoader from './getdebugloader.js'; /** - * @param {Object} options - * @param {Array.} options.debugFlags - * @returns {Object} + * @param {object} options + * @param {Array.} options.debugFlags + * @returns {object} */ export default function getJavaScriptLoader( { debugFlags } ) { return { diff --git a/packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js b/packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js index 5f44b2d75..c4e4925ba 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/getstylesloader.js @@ -7,13 +7,13 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import { getPostCssConfig } from '../styles/index.js'; /** - * @param {Object} options - * @param {String} options.themePath - * @param {Boolean} [options.minify] - * @param {Boolean} [options.sourceMap] - * @param {Boolean} [options.extractToSeparateFile] - * @param {Boolean} [options.skipPostCssLoader] - * @returns {Object} + * @param {object} options + * @param {string} options.themePath + * @param {boolean} [options.minify] + * @param {boolean} [options.sourceMap] + * @param {boolean} [options.extractToSeparateFile] + * @param {boolean} [options.skipPostCssLoader] + * @returns {object} */ export default function getStylesLoader( options ) { const { diff --git a/packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js b/packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js index 7e571dd88..c46d8b3f7 100644 --- a/packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js +++ b/packages/ckeditor5-dev-utils/lib/loaders/gettypescriptloader.js @@ -6,11 +6,11 @@ import getDebugLoader from './getdebugloader.js'; /** - * @param {Object} [options] - * @param {String} [options.configFile] - * @param {Array.} [options.debugFlags] - * @param {Boolean} [options.includeDebugLoader] - * @returns {Object} + * @param {object} [options] + * @param {string} [options.configFile] + * @param {Array.} [options.debugFlags] + * @param {boolean} [options.includeDebugLoader] + * @returns {object} */ export default function getTypeScriptLoader( options = {} ) { const { diff --git a/packages/ckeditor5-dev-utils/lib/logger/index.js b/packages/ckeditor5-dev-utils/lib/logger/index.js index e6a9ddcf5..d180cfb1d 100644 --- a/packages/ckeditor5-dev-utils/lib/logger/index.js +++ b/packages/ckeditor5-dev-utils/lib/logger/index.js @@ -45,8 +45,8 @@ levels.set( 'error', new Set( [ 'info', 'warning', 'error' ] ) ); * * Additionally, the `logger#error()` method prints the error instance if provided as the second argument. * - * @param {String} moduleVerbosity='info' Level of the verbosity for all log methods. - * @returns {Object} logger + * @param {string} moduleVerbosity='info' Level of the verbosity for all log methods. + * @returns {object} logger * @returns {Function} logger.info * @returns {Function} logger.warning * @returns {Function} logger.error @@ -56,7 +56,7 @@ export default function logger( moduleVerbosity = 'info' ) { /** * Displays a message when verbosity level is equal to 'info'. * - * @param {String} message Message to log. + * @param {string} message Message to log. */ info( message ) { this._log( 'info', message ); @@ -65,7 +65,7 @@ export default function logger( moduleVerbosity = 'info' ) { /** * Displays a warning message when verbosity level is equal to 'info' or 'warning'. * - * @param {String} message Message to log. + * @param {string} message Message to log. */ warning( message ) { this._log( 'warning', chalk.yellow( message ) ); @@ -74,7 +74,7 @@ export default function logger( moduleVerbosity = 'info' ) { /** * Displays an error message. * - * @param {String} message Message to log. + * @param {string} message Message to log. * @param {Error} [error] An error instance to log in the console. */ error( message, error ) { @@ -83,8 +83,8 @@ export default function logger( moduleVerbosity = 'info' ) { /** * @private - * @param {String} messageVerbosity Verbosity of particular message. - * @param {String} message Message to log. + * @param {string} messageVerbosity Verbosity of particular message. + * @param {string} message Message to log. * @param {Error} [error] An error instance to log in the console. */ _log( messageVerbosity, message, error ) { diff --git a/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js b/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js index 231e9617e..16a6ac71a 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js +++ b/packages/ckeditor5-dev-utils/lib/styles/getpostcssconfig.js @@ -15,12 +15,12 @@ import themeImporter from './themeimporter.js'; /** * Returns a PostCSS configuration to build the editor styles (e.g. used by postcss-loader). * - * @param {Object} options - * @param {Boolean} options.sourceMap When true, an inline source map will be built into the output CSS. - * @param {Boolean} options.minify When true, the output CSS will be minified. + * @param {object} options + * @param {boolean} options.sourceMap When true, an inline source map will be built into the output CSS. + * @param {boolean} options.minify When true, the output CSS will be minified. * @param {ThemeImporterOptions} options.themeImporter Configuration of the theme-importer PostCSS plugin. * See the plugin to learn more. - * @returns {Object} A PostCSS configuration object, e.g. to be used by the postcss-loader. + * @returns {object} A PostCSS configuration object, e.g. to be used by the postcss-loader. */ export default function getPostCssConfig( options = {} ) { const config = { diff --git a/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js b/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js index ffc9539ad..c6ea292e6 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js +++ b/packages/ckeditor5-dev-utils/lib/styles/themeimporter.js @@ -158,9 +158,9 @@ function importFile( options ) { * `/foo/bar/ckeditor5-theme-foo/ckeditor5-qux/theme/components/button.css` * * @private - * @param {String} themePath Path to the theme. - * @param {String} inputFilePath Path to the CSS file which is to be themed. - * @returns {String} + * @param {string} themePath Path to the theme. + * @param {string} inputFilePath Path to the CSS file which is to be themed. + * @returns {string} */ function getThemeFilePath( themePath, inputFilePath ) { // ckeditor5-theme-foo/theme/theme.css -> ckeditor5-theme-foo/theme @@ -202,11 +202,11 @@ function getThemeFilePath( themePath, inputFilePath ) { * ... * } * - * @member {String} [ThemeImporterOptions#themePath] + * @member {string} [ThemeImporterOptions#themePath] */ /** * When `true` it enables debug logs in the console. * - * @member {String} [ThemeImporterOptions#debug=false] + * @member {string} [ThemeImporterOptions#debug=false] */ diff --git a/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js b/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js index 87b9d1c6d..38e9596af 100644 --- a/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js +++ b/packages/ckeditor5-dev-utils/lib/styles/utils/getpackagename.js @@ -28,8 +28,8 @@ * * "ckeditor5-bar" * - * @param {String} inputFilePath A path to the file. - * @returns {String} The name of the package. + * @param {string} inputFilePath A path to the file. + * @returns {string} The name of the package. */ export default function getPackageName( inputFilePath ) { const match = inputFilePath.match( /^.+[/\\](ckeditor5-[^/\\]+)/ ); diff --git a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js index b025260e9..88c82d9e3 100644 --- a/packages/ckeditor5-dev-utils/lib/tools/createspinner.js +++ b/packages/ckeditor5-dev-utils/lib/tools/createspinner.js @@ -16,14 +16,14 @@ const INDENT_SIZE = 3; * * The spinner improves UX when processing a time-consuming task. A developer does not have to consider whether the process hanged on. * - * @param {String} title Description of the current processed task. - * @param {Object} [options={}] - * @param {Boolean} [options.isDisabled] Whether the spinner should be disabled. - * @param {String} [options.emoji='📍'] An emoji that will replace the spinner when it finishes. - * @param {Number} [options.indentLevel=1] The indent level. - * @param {Number} [options.total] If specified, the spinner contains a counter. It starts from `0`. To increase its value, + * @param {string} title Description of the current processed task. + * @param {object} [options={}] + * @param {boolean} [options.isDisabled] Whether the spinner should be disabled. + * @param {string} [options.emoji='📍'] An emoji that will replace the spinner when it finishes. + * @param {number} [options.indentLevel=1] The indent level. + * @param {number} [options.total] If specified, the spinner contains a counter. It starts from `0`. To increase its value, * call the `#increase()` method on the returned instance of the spinner. - * @param {String|CKEditor5SpinnerStatus} [options.status='[title] Status: [current]/[total].'] If a spinner is a counter, + * @param {string|CKEditor5SpinnerStatus} [options.status='[title] Status: [current]/[total].'] If a spinner is a counter, * this option allows customizing the displayed line. * @returns {CKEditor5Spinner} */ @@ -114,7 +114,7 @@ export default function createSpinner( title, options = {} ) { } /** - * @typedef {Object} CKEditor5Spinner + * @typedef {object} CKEditor5Spinner * * @property {CKEditor5SpinnerStart} start * @@ -134,17 +134,17 @@ export default function createSpinner( title, options = {} ) { /** * @callback CKEditor5SpinnerFinish * - * @param {Object} [options={}] + * @param {object} [options={}] * - * @param {String} [options.emoji] + * @param {string} [options.emoji] */ /** * @callback CKEditor5SpinnerStatus * - * @param {String} title + * @param {string} title * - * @param {Number} current + * @param {number} current * - * @param {Number} total + * @param {number} total */ diff --git a/packages/ckeditor5-dev-utils/lib/tools/getdirectories.js b/packages/ckeditor5-dev-utils/lib/tools/getdirectories.js index 0400abd7a..e863c0b57 100644 --- a/packages/ckeditor5-dev-utils/lib/tools/getdirectories.js +++ b/packages/ckeditor5-dev-utils/lib/tools/getdirectories.js @@ -9,7 +9,7 @@ import path from 'path'; /** * Returns array with all directories under specified path. * - * @param {String} directoryPath + * @param {string} directoryPath * @returns {Array} */ export default function getDirectories( directoryPath ) { diff --git a/packages/ckeditor5-dev-utils/lib/tools/shexec.js b/packages/ckeditor5-dev-utils/lib/tools/shexec.js index d11058125..8b84c221b 100644 --- a/packages/ckeditor5-dev-utils/lib/tools/shexec.js +++ b/packages/ckeditor5-dev-utils/lib/tools/shexec.js @@ -10,13 +10,13 @@ import logger from '../logger/index.js'; /** * Executes a shell command. * - * @param {String} command The command to be executed. - * @param {Object} options + * @param {string} command The command to be executed. + * @param {object} options * @param {'info'|'warning'|'error'|'silent'} [options.verbosity='info'] Level of the verbosity. If set as 'info' * both outputs (stdout and stderr) will be logged. If set as 'error', only stderr output will be logged. - * @param {String} [options.cwd=process.cwd()] - * @param {Boolean} [options.async=false] If set, the command execution is asynchronous. The execution is synchronous by default. - * @returns {String|Promise.} The command output. + * @param {string} [options.cwd=process.cwd()] + * @param {boolean} [options.async=false] If set, the command execution is asynchronous. The execution is synchronous by default. + * @returns {string|Promise.} The command output. */ export default function shExec( command, options = {} ) { const { @@ -49,13 +49,13 @@ export default function shExec( command, options = {} ) { } /** - * @param {Object} options - * @param {Number} options.code - * @param {String} options.stdout - * @param {String} options.stderr + * @param {object} options + * @param {number} options.code + * @param {string} options.stdout + * @param {string} options.stderr * @param {'info'|'warning'|'error'|'silent'} options.verbosity - * @param {String} options.command - * @returns {String} + * @param {string} options.command + * @returns {string} */ function execHandler( { code, stdout, stderr, verbosity, command } ) { const log = logger( verbosity ); diff --git a/packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js b/packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js index 152b533ba..f8a98203c 100644 --- a/packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js +++ b/packages/ckeditor5-dev-utils/lib/tools/updatejsonfile.js @@ -8,8 +8,8 @@ import fs from 'fs'; /** * Updates JSON file under specified path. * - * @param {String} filePath Path to file on disk. - * @param {Function} updateFunction Function that will be called with parsed JSON object. It should return + * @param {string} filePath Path to file on disk. + * @param {function} updateFunction Function that will be called with parsed JSON object. It should return * modified JSON object to save. */ export default function updateJSONFile( filePath, updateFunction ) { diff --git a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js index ea27f4f13..7146fa277 100644 --- a/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js +++ b/packages/ckeditor5-dev-web-crawler/lib/runcrawler.js @@ -31,15 +31,15 @@ import { * - open simultaneously (up to concurrency limit) links from the provided URL in a dedicated Puppeteer's page for each link, * - show error summary after all links have been visited. * - * @param {Object} options Parsed CLI arguments. - * @param {String} options.url The URL to start crawling. This argument is required. - * @param {Number} [options.depth=Infinity] Defines how many nested page levels should be examined. Infinity by default. - * @param {Array.} [options.exclusions=[]] An array of patterns to exclude links. Empty array by default to not exclude anything. - * @param {Number} [options.concurrency=1] Number of concurrent pages (browser tabs) to be used during crawling. One by default. - * @param {Boolean} [options.quit=false] Terminates the scan as soon as an error is found. False (off) by default. - * @param {Boolean} [options.disableBrowserSandbox=false] Whether the browser should be created with the `--no-sandbox` flag. - * @param {Boolean} [options.noSpinner=false] Whether to display the spinner with progress or a raw message with current progress. - * @param {Boolean} [options.ignoreHTTPSErrors=false] Whether the browser should ignore invalid (self-signed) certificates. + * @param {object} options Parsed CLI arguments. + * @param {string} options.url The URL to start crawling. This argument is required. + * @param {number} [options.depth=Infinity] Defines how many nested page levels should be examined. Infinity by default. + * @param {Array.} [options.exclusions=[]] An array of patterns to exclude links. Empty array by default to not exclude anything. + * @param {number} [options.concurrency=1] Number of concurrent pages (browser tabs) to be used during crawling. One by default. + * @param {boolean} [options.quit=false] Terminates the scan as soon as an error is found. False (off) by default. + * @param {boolean} [options.disableBrowserSandbox=false] Whether the browser should be created with the `--no-sandbox` flag. + * @param {boolean} [options.noSpinner=false] Whether to display the spinner with progress or a raw message with current progress. + * @param {boolean} [options.ignoreHTTPSErrors=false] Whether the browser should ignore invalid (self-signed) certificates. * @returns {Promise} Promise is resolved, when the crawler has finished the whole crawling procedure. */ export default async function runCrawler( options ) { @@ -106,11 +106,11 @@ export default async function runCrawler( options ) { /** * Creates a new browser instance and closes the default blank page. * - * @param {Object} options - * @param {Boolean} [options.disableBrowserSandbox] Whether the browser should be created with the `--no-sandbox` flag. - * @param {Boolean} [options.ignoreHTTPSErrors] Whether the browser should ignore invalid (self-signed) certificates. + * @param {object} options + * @param {boolean} [options.disableBrowserSandbox] Whether the browser should be created with the `--no-sandbox` flag. + * @param {boolean} [options.ignoreHTTPSErrors] Whether the browser should ignore invalid (self-signed) certificates. * - * @returns {Promise.} A promise, which resolves to the Puppeteer browser instance. + * @returns {Promise.} A promise, which resolves to the Puppeteer browser instance. */ async function createBrowser( options ) { const browserOptions = { @@ -180,16 +180,16 @@ function getErrorHandler( errors ) { /** * Searches and opens all found links in the document body from requested URL, recursively. * - * @param {Object} browser The headless browser instance from Puppeteer. - * @param {Object} data All data needed for crawling the links. - * @param {String} data.baseUrl The base URL from the initial page URL. + * @param {object} browser The headless browser instance from Puppeteer. + * @param {object} data All data needed for crawling the links. + * @param {string} data.baseUrl The base URL from the initial page URL. * @param {Array.} data.linksQueue An array of link to crawl. - * @param {Array.} data.foundLinks An array of all links, which have been already discovered. - * @param {Array.} data.exclusions An array of patterns to exclude links. Empty array by default to not exclude anything. - * @param {Number} data.concurrency Number of concurrent pages (browser tabs) to be used during crawling. - * @param {Boolean} data.quit Terminates the scan as soon as an error is found. - * @param {Function} data.onError Callback called ever time an error has been found. - * @param {Function} data.onProgress Callback called every time just before opening a new link. + * @param {Array.} data.foundLinks An array of all links, which have been already discovered. + * @param {Array.} data.exclusions An array of patterns to exclude links. Empty array by default to not exclude anything. + * @param {number} data.concurrency Number of concurrent pages (browser tabs) to be used during crawling. + * @param {boolean} data.quit Terminates the scan as soon as an error is found. + * @param {function} data.onError Callback called ever time an error has been found. + * @param {function} data.onProgress Callback called every time just before opening a new link. * @returns {Promise} Promise is resolved, when all links have been visited. */ async function openLinks( browser, { baseUrl, linksQueue, foundLinks, exclusions, concurrency, quit, onError, onProgress } ) { @@ -254,12 +254,12 @@ async function openLinks( browser, { baseUrl, linksQueue, foundLinks, exclusions * excluded links are also skipped. If the requested traversing depth has been reached, nested links from this URL are not collected * anymore. * - * @param {Object} browser The headless browser instance from Puppeteer. - * @param {Object} data All data needed for crawling the link. - * @param {String} data.baseUrl The base URL from the initial page URL. + * @param {object} browser The headless browser instance from Puppeteer. + * @param {object} data All data needed for crawling the link. + * @param {string} data.baseUrl The base URL from the initial page URL. * @param {Link} data.link A link to crawl. - * @param {Array.} data.foundLinks An array of all links, which have been already discovered. - * @param {Array.} data.exclusions An array of patterns to exclude links. Empty array by default to not exclude anything. + * @param {Array.} data.foundLinks An array of all links, which have been already discovered. + * @param {Array.} data.exclusions An array of patterns to exclude links. Empty array by default to not exclude anything. * @returns {Promise.} A promise, which resolves to a collection of unique errors and links. */ async function openLink( browser, { baseUrl, link, foundLinks, exclusions } ) { @@ -330,12 +330,12 @@ async function openLink( browser, { baseUrl, link, foundLinks, exclusions } ) { /** * Finds all links in opened page and filters out external, already discovered and explicitly excluded ones. * - * @param {Object} page The page instance from Puppeteer. - * @param {Object} data All data needed for crawling the link. - * @param {String} data.baseUrl The base URL from the initial page URL. - * @param {Array.} data.foundLinks An array of all links, which have been already discovered. - * @param {Array.} data.exclusions An array patterns to exclude links. Empty array by default to not exclude anything. - * @returns {Promise.>} A promise, which resolves to an array of unique links. + * @param {object} page The page instance from Puppeteer. + * @param {object} data All data needed for crawling the link. + * @param {string} data.baseUrl The base URL from the initial page URL. + * @param {Array.} data.foundLinks An array of all links, which have been already discovered. + * @param {Array.} data.exclusions An array patterns to exclude links. Empty array by default to not exclude anything. + * @returns {Promise.>} A promise, which resolves to an array of unique links. */ async function getLinksFromPage( page, { baseUrl, foundLinks, exclusions } ) { const evaluatePage = anchors => [ ...new Set( anchors @@ -367,8 +367,8 @@ async function getLinksFromPage( page, { baseUrl, foundLinks, exclusions } ) { /** * Finds all meta tags, that contain a pattern to ignore errors, and then returns a map between error type and these patterns. * - * @param {Object} page The page instance from Puppeteer. - * @returns {Promise.>>} A promise, which resolves to a map between an error type and a set of patterns. + * @param {object} page The page instance from Puppeteer. + * @returns {Promise.>>} A promise, which resolves to a map between an error type and a set of patterns. */ async function getErrorIgnorePatternsFromPage( page ) { const metaTag = await page.$( `head > meta[name=${ META_TAG_NAME }]` ); @@ -416,7 +416,7 @@ async function getErrorIgnorePatternsFromPage( page ) { * Iterates over all found errors from given link and marks errors as ignored, if their message match the ignore pattern. * * @param {Array.} errors An array of errors to check. - * @param {Map.>} errorIgnorePatterns A map between an error type and a set of patterns. + * @param {Map.>} errorIgnorePatterns A map between an error type and a set of patterns. */ function markErrorsAsIgnored( errors, errorIgnorePatterns ) { errors.forEach( error => { @@ -453,11 +453,11 @@ function markErrorsAsIgnored( errors, errorIgnorePatterns ) { /** * Creates a new page in Puppeteer's browser instance. * - * @param {Object} browser The headless browser instance from Puppeteer. - * @param {Object} data All data needed for creating a new page. + * @param {object} browser The headless browser instance from Puppeteer. + * @param {object} data All data needed for creating a new page. * @param {Link} data.link A link to crawl. - * @param {Function} data.onError Callback called every time just before opening a new link. - * @returns {Promise.} A promise, which resolves to the page instance from Puppeteer. + * @param {function} data.onError Callback called every time just before opening a new link. + * @returns {Promise.} A promise, which resolves to the page instance from Puppeteer. */ async function createPage( browser, { link, onError } ) { const page = await browser.newPage(); @@ -478,7 +478,7 @@ async function createPage( browser, { link, onError } ) { /** * Dismisses any dialogs (alert, prompt, confirm, beforeunload) that could be displayed on page load. * - * @param {Object} page The page instance from Puppeteer. + * @param {object} page The page instance from Puppeteer. */ function dismissDialogs( page ) { page.on( 'dialog', async dialog => { @@ -489,10 +489,10 @@ function dismissDialogs( page ) { /** * Registers all error handlers on given page instance. * - * @param {Object} page The page instance from Puppeteer. - * @param {Object} data All data needed for registering error handlers. + * @param {object} page The page instance from Puppeteer. + * @param {object} data All data needed for registering error handlers. * @param {Link} data.link A link to crawl associated with Puppeteer's page. - * @param {Function} data.onError Called each time an error has been found. + * @param {function} data.onError Called each time an error has been found. */ function registerErrorHandlers( page, { link, onError } ) { page.on( ERROR_TYPES.PAGE_CRASH.event, error => onError( { @@ -609,8 +609,8 @@ function registerErrorHandlers( page, { link, onError } ) { * Checks, if HTTP request was a navigation one, i.e. request that is driving frame's navigation. Requests sent from child frames * (i.e. from