Skip to content

Commit

Permalink
test: add catalog protocol tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gluxon committed Apr 21, 2024
1 parent e19bc96 commit f159d89
Showing 1 changed file with 326 additions and 0 deletions.
326 changes: 326 additions & 0 deletions pkg-manager/plugin-commands-installation/test/catalog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { type Lockfile } from '@pnpm/lockfile-types'
import { type ProjectManifest } from '@pnpm/types'
import { readProjects } from '@pnpm/filter-workspace-packages'
import { preparePackages } from '@pnpm/prepare'
import { install } from '@pnpm/plugin-commands-installation'
import { DEFAULT_OPTS } from './utils'
import path from 'path'
import readYamlFile from 'read-yaml-file'
import writeYamlFile from 'write-yaml-file'
import fs from 'fs/promises'

// Polyfilling Symbol.asyncDispose for Jest.
//
// Copied with a few changes from https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/#using-declarations-and-explicit-resource-management
if (Symbol.asyncDispose === undefined) {
(Symbol as { asyncDispose?: symbol }).asyncDispose = Symbol('Symbol.asyncDispose')
}

/**
* A utility to make writing catalog tests easier by reducing boilerplate.
*/
class CatalogTestsController implements AsyncDisposable {
private readonly workspaceDir: string

constructor (readonly pkgs: Array<{ location: string, package: ProjectManifest }>) {
preparePackages(pkgs)
this.workspaceDir = process.cwd()
}

async writeWorkspaceYml (data: unknown) {
await writeYamlFile(path.join(this.workspaceDir, 'pnpm-workspace.yaml'), data)
}

async install (opts?: { filter?: readonly string[] }) {
const { allProjects, selectedProjectsGraph } = await readProjects(this.workspaceDir, opts?.filter?.map(parentDir => ({ parentDir })) ?? [])
await install.handler({
...DEFAULT_OPTS,
allProjects,
dir: this.workspaceDir,
lockfileDir: this.workspaceDir,
recursive: true,
selectedProjectsGraph,
workspaceDir: this.workspaceDir,
})
}

async lockfile (): Promise<Lockfile> {
return readYamlFile(path.join(this.workspaceDir, WANTED_LOCKFILE))
}

async updateProjectManifest (location: string, manifest: ProjectManifest): Promise<void> {
const { selectedProjectsGraph } = await readProjects(this.workspaceDir, [{ parentDir: location }])
await selectedProjectsGraph[path.join(this.workspaceDir, location)].package.writeProjectManifest(manifest)
}

async [Symbol.asyncDispose] (): Promise<void> {
await fs.rmdir(this.workspaceDir, { recursive: true })
}
}

test('installing with "catalog:" should work', async () => {
await using ctrl = new CatalogTestsController([
{
location: 'packages/project1',
package: {
dependencies: {
'is-positive': 'catalog:',
},
},
},
// Empty second project to create a multi-package workspace.
{
location: 'packages/project2',
package: {},
},
])

await ctrl.writeWorkspaceYml({
packages: ['packages/*'],
catalog: { 'is-positive': '1.0.0' },
})
await ctrl.install()
const lockfile = await ctrl.lockfile()

expect(lockfile.importers['packages/project1']).toEqual({
dependencies: {
'is-positive': {
specifier: 'catalog:',
version: '1.0.0',
},
},
})
})

test('importer to importer dependency with "catalog:" should work', async () => {
await using ctrl = new CatalogTestsController([
{
location: 'packages/project1',
package: {
name: 'project1',
dependencies: {
project2: 'workspace:*',
},
},
},
{
location: 'packages/project2',
package: {
name: 'project2',
dependencies: {
'is-positive': 'catalog:',
},
},
},
])

await ctrl.writeWorkspaceYml({
packages: ['packages/*'],
catalog: { 'is-positive': '1.0.0' },
})

await ctrl.install()
const lockfile = await ctrl.lockfile()

expect(lockfile.importers['packages/project2']).toEqual({
dependencies: {
'is-positive': {
specifier: 'catalog:',
version: '1.0.0',
},
},
})
})

test('lockfile contains catalog snapshots', async () => {
await using ctrl = new CatalogTestsController([
{
location: 'packages/project1',
package: {
dependencies: {
'is-positive': 'catalog:',
},
},
},
{
location: 'packages/project2',
package: {
dependencies: {
'is-negative': 'catalog:',
},
},
},
])

await ctrl.writeWorkspaceYml({
packages: ['packages/*'],
catalog: {
'is-positive': '^1.0.0',
'is-negative': '^1.0.0',
},
})

await ctrl.install()
const lockfile = await ctrl.lockfile()

expect(lockfile.catalogs).toStrictEqual({
default: {
'is-positive': { specifier: '^1.0.0', version: '1.0.0' },
'is-negative': { specifier: '^1.0.0', version: '1.0.0' },
},
})
})

test('lockfile is updated if catalog config changes', async () => {
await using ctrl = new CatalogTestsController([
{
location: 'packages/project1',
package: {
dependencies: {
'is-positive': 'catalog:',
},
},
},
])

await ctrl.writeWorkspaceYml({
packages: ['packages/*'],
catalog: { 'is-positive': '=1.0.0' },
})
await ctrl.install()

expect((await ctrl.lockfile()).importers['packages/project1']).toEqual({
dependencies: {
'is-positive': {
specifier: 'catalog:',
version: '1.0.0',
},
},
})

await ctrl.writeWorkspaceYml({
packages: ['packages/*'],
catalog: {
'is-positive': '=3.1.0',
},
})
await ctrl.install()

expect((await ctrl.lockfile()).importers['packages/project1']).toEqual({
dependencies: {
'is-positive': {
specifier: 'catalog:',
version: '3.1.0',
},
},
})
})

test('lockfile catalog snapshots retain existing entries on --filter', async () => {
await using ctrl = new CatalogTestsController([
{
location: 'packages/project1',
package: {
dependencies: {
'is-negative': 'catalog:',
},
},
},
{
location: 'packages/project2',
package: {
dependencies: {
'is-positive': 'catalog:',
},
},
},
])

await ctrl.writeWorkspaceYml({
packages: ['packages/*'],
catalog: {
'is-positive': '^1.0.0',
'is-negative': '^1.0.0',
},
})

await ctrl.install()

expect((await ctrl.lockfile()).catalogs).toStrictEqual({
default: {
'is-negative': { specifier: '^1.0.0', version: '1.0.0' },
'is-positive': { specifier: '^1.0.0', version: '1.0.0' },
},
})

// Update catalog definitions so pnpm triggers a rerun.
await ctrl.writeWorkspaceYml({
packages: ['packages/*'],
catalog: {
'is-positive': '=3.1.0',
'is-negative': '^1.0.0',
},
})
await ctrl.install({ filter: ['packages/project2'] })

expect((await ctrl.lockfile()).catalogs).toStrictEqual({
default: {
// The is-negative snapshot should be carried from the previous install,
// despite the current filtered install not using it.
'is-negative': { specifier: '^1.0.0', version: '1.0.0' },

'is-positive': { specifier: '=3.1.0', version: '3.1.0' },
},
})
})

test('lockfile catalog snapshots should keep unused entries', async () => {
await using ctrl = new CatalogTestsController([
{
location: 'packages/project1',
package: {
dependencies: {
'is-positive': 'catalog:',
},
},
},
])

await writeYamlFile('pnpm-workspace.yaml', {
packages: ['packages/*'],
catalog: {
'is-positive': '=1.0.0',
},
})

{
await ctrl.install()
const lockfile = await ctrl.lockfile()
expect(lockfile.importers['packages/project1']?.dependencies?.['is-positive']).toEqual({
specifier: 'catalog:',
version: '1.0.0',
})
expect(lockfile.catalogs?.default).toStrictEqual({
'is-positive': { specifier: '=1.0.0', version: '1.0.0' },
})
}

await ctrl.updateProjectManifest('packages/project1', { dependencies: { 'is-positive': '=1.0.0' } })
await ctrl.install()

{
const lockfile = await ctrl.lockfile()
expect(lockfile.importers['packages/project1']).toEqual({
dependencies: {
'is-positive': {
specifier: '=1.0.0',
version: '1.0.0',
},
},
})
expect(lockfile.catalogs?.default).toStrictEqual({
'is-positive': { specifier: '=1.0.0', version: '1.0.0' },
})
}
})

0 comments on commit f159d89

Please sign in to comment.