Files
Sebastian Poxhofer 05e1271376 test: use expect().resolves instead of awaiting inside expect (#45672)
Enables the vitest/prefer-expect-resolves oxlint rule and applies its
autofix across the spec suite.
2026-09-04 10:40:46 +00:00

261 lines
8.9 KiB
TypeScript

import { codeBlock } from 'common-tags';
import { Fixtures } from '~test/fixtures.ts';
import * as httpMock from '~test/http-mock.ts';
import { clone } from '../../../../../util/clone.ts';
import { defaultRegistryUrl } from '../../../../datasource/npm/common.ts';
import type { UpdateLockedConfig } from '../../../types.ts';
import { updateLockedDependency } from '../../index.ts';
const packageFileContent = Fixtures.get('package.json', './package-lock');
const lockFileContent = Fixtures.get('package-lock-v1.json', './package-lock');
const lockFileV2Content = Fixtures.get(
'package-lock-v2.json',
'./package-lock',
);
const acceptsJson = Fixtures.getJson('accepts.json', './package-lock');
const expressJson = Fixtures.get('express.json', './common');
const mimeJson = Fixtures.get('mime.json', './package-lock');
const serveStaticJson = Fixtures.get('serve-static.json', './package-lock');
const sendJson = Fixtures.get('send.json', './package-lock');
const typeIsJson = Fixtures.getJson('type-is.json', './package-lock');
describe('modules/manager/npm/update/locked-dependency/index', () => {
describe('updateLockedDependency()', () => {
let config: UpdateLockedConfig;
beforeEach(() => {
config = {
packageFile: 'package.json',
packageFileContent,
lockFile: 'package-lock.json',
lockFileContent,
depName: 'some-dep',
currentVersion: '1.0.0',
newVersion: '1.0.1',
};
});
it('validates filename', async () => {
await expect(
updateLockedDependency({ ...config, lockFile: 'yarn.lock' }),
).resolves.toMatchObject({});
await expect(
updateLockedDependency({ ...config, lockFile: 'yarn.lock2' }),
).resolves.toMatchObject({});
});
it('validates versions', async () => {
await expect(
updateLockedDependency({
...config,
newVersion: '^2.0.0',
}),
).resolves.toMatchObject({});
});
it('returns null for unparseable files', async () => {
await expect(
updateLockedDependency({
...config,
lockFileContent: 'not json',
}),
).resolves.toMatchObject({});
});
it('rejects lockFileVersion 2', async () => {
await expect(
updateLockedDependency({
...config,
lockFileContent: lockFileContent.replace(': 1,', ': 2,'),
}),
).resolves.toMatchObject({});
});
it('returns null if no locked deps', async () => {
await expect(updateLockedDependency(config)).resolves.toMatchObject({});
});
it('rejects null if no constraint found', async () => {
await expect(
updateLockedDependency({
...config,
lockFileContent: lockFileContent.replace('1.0.0', '10.0.0'),
depName: 'accepts',
currentVersion: '10.0.0',
newVersion: '11.0.0',
}),
).resolves.toMatchObject({});
});
it('remediates in-range', async () => {
const res = await updateLockedDependency({
...config,
depName: 'mime',
currentVersion: '1.2.11',
newVersion: '1.2.12',
});
expect(
JSON.parse(res.files!['package-lock.json']).dependencies.mime.version,
).toBe('1.2.12');
});
it('rejects in-range remediation if lockfile v2+', async () => {
const res = await updateLockedDependency({
...config,
lockFileContent: lockFileV2Content,
depName: 'mime',
currentVersion: '1.2.11',
newVersion: '1.2.12',
});
expect(res.status).toBe('unsupported');
});
it('fails to remediate if parent dep cannot support', async () => {
const acceptsModified = clone(acceptsJson);
acceptsModified.versions['2.0.0'] = {};
httpMock
.scope(defaultRegistryUrl)
.get('/accepts')
.reply(200, acceptsModified);
httpMock
.scope(defaultRegistryUrl)
.get('/express')
.reply(200, expressJson);
const res = await updateLockedDependency({
...config,
depName: 'accepts',
currentVersion: '1.0.0',
newVersion: '2.0.0',
});
expect(res).toMatchObject({});
});
it('remediates express', async () => {
config.depName = 'express';
config.currentVersion = '4.0.0';
config.newVersion = '4.1.0';
const res = await updateLockedDependency(config);
expect(res.files!['package.json']).toContain('"express": "4.1.0"');
const packageLock = JSON.parse(res.files!['package-lock.json']);
expect(packageLock.dependencies.express.version).toBe('4.1.0');
});
it('remediates lock file v2 express', async () => {
config.depName = 'express';
config.currentVersion = '4.0.0';
config.newVersion = '4.1.0';
config.lockFileContent = lockFileV2Content;
const res = await updateLockedDependency(config);
expect(res.files!['package.json']).toContain('"express": "4.1.0"');
const packageLock = JSON.parse(res.files!['package-lock.json']);
expect(packageLock.dependencies.express.version).toBe('4.1.0');
});
it('returns already-updated if already remediated exactly', async () => {
config.depName = 'mime';
config.currentVersion = '1.2.10';
config.newVersion = '1.2.11';
const res = await updateLockedDependency(config);
expect(res.status).toBe('already-updated');
});
it('returns already-updated if already v2 remediated exactly', async () => {
config.depName = 'mime';
config.currentVersion = '1.2.10';
config.newVersion = '1.2.11';
config.lockFileContent = lockFileV2Content;
const res = await updateLockedDependency(config);
expect(res.status).toBe('already-updated');
});
it('returns already-updated if already remediated higher', async () => {
config.depName = 'mime';
config.currentVersion = '1.2.9';
config.newVersion = '1.2.10';
config.allowHigherOrRemoved = true;
const res = await updateLockedDependency(config);
expect(res.status).toBe('already-updated');
});
it('returns already-updated if not found', async () => {
config.depName = 'notfound';
config.currentVersion = '1.2.9';
config.newVersion = '1.2.10';
config.allowHigherOrRemoved = true;
const res = await updateLockedDependency(config);
expect(res.status).toBe('already-updated');
});
it('returns update-failed if other, lower version found', async () => {
config.depName = 'mime';
config.currentVersion = '1.2.5';
config.newVersion = '1.2.15';
config.allowHigherOrRemoved = true;
const res = await updateLockedDependency(config);
expect(res.status).toBe('update-failed');
});
it('remediates mime', async () => {
config.depName = 'mime';
config.currentVersion = '1.2.11';
config.newVersion = '1.4.1';
httpMock.scope(defaultRegistryUrl).get('/mime').reply(200, mimeJson);
httpMock.scope(defaultRegistryUrl).get('/send').reply(200, sendJson);
httpMock
.scope(defaultRegistryUrl)
.get('/serve-static')
.reply(200, serveStaticJson);
httpMock.scope(defaultRegistryUrl).get('/type-is').reply(200, typeIsJson);
const res = await updateLockedDependency(config);
const packageLock = JSON.parse(res.files!['package-lock.json']);
expect(packageLock.dependencies.mime.version).toBe('1.4.1');
expect(packageLock.dependencies.express.version).toBe('4.16.0');
});
it('fails remediation if cannot update parent', async () => {
config.depName = 'mime';
config.currentVersion = '1.2.11';
config.newVersion = '1.4.1';
config.allowParentUpdates = false;
const res = await updateLockedDependency(config);
expect(res.status).toBe('update-failed');
});
it('fails remediation if bundled', async () => {
const bundledPackageJson = codeBlock`
{
"name": "repro-renovate-13977",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"semantic-release": "19.0.2"
}
}
`;
const bundledPackageLockJson = Fixtures.get(
'bundled.package-lock.json',
'./package-lock',
);
config.depName = 'ansi-regex';
config.currentVersion = '3.0.0';
config.newVersion = '5.0.1';
config.packageFileContent = bundledPackageJson;
config.lockFileContent = bundledPackageLockJson;
const res = await updateLockedDependency(config);
expect(res.status).toBe('update-failed');
});
it('rejects in-range remediation if pnpm', async () => {
config.lockFile = 'pnpm-lock.yaml';
const res = await updateLockedDependency(config);
expect(res.status).toBe('unsupported');
});
});
});