Files
renovate/lib/modules/manager/deno/extract.spec.ts
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

522 lines
16 KiB
TypeScript

import { codeBlock } from 'common-tags';
import { findPackages } from 'find-packages';
import { fs } from '../../../../test/util.ts';
import { GlobalConfig } from '../../../config/global.ts';
import * as compat from './compat.ts';
import {
extractAllPackageFiles,
getLockFiles,
processDenoExtract,
processImportMap,
} from './extract.ts';
vi.mock('../../../util/fs/index.ts');
vi.mock('./compat.ts');
// used in detectNodeCompatWorkspaces()
vi.mock('find-packages', () => ({
findPackages: vi.fn(),
}));
describe('modules/manager/deno/extract', () => {
describe('getLockFiles()', () => {
it('found lock file', async () => {
fs.getSiblingFileName.mockReturnValueOnce('deno.lock');
fs.localPathIsFile.mockResolvedValue(true);
const result = await getLockFiles('deno.lock', 'deno.json');
expect(result).toEqual(['deno.lock']);
});
it('not found lock file', async () => {
fs.getSiblingFileName.mockReturnValueOnce('package-lock.json');
const result = await getLockFiles(false, 'deno.json');
expect(result).toEqual([]);
});
});
describe('processImportMap()', () => {
it('importMap', async () => {
fs.readLocalFile.mockResolvedValueOnce(
JSON.stringify({
imports: {
dep1: 'jsr:@scope/name@3.0.0',
},
scopes: {
'https://deno.land/x/name/mod.ts': {
'@scope/name': 'jsr:@scope/name@0.2.0',
},
},
}),
);
const result = await processImportMap('import_map.json', 'deno.json', [
'deno.lock',
]);
expect(result).toStrictEqual({
deps: [
{
currentRawValue: 'jsr:@scope/name@3.0.0',
currentValue: '3.0.0',
datasource: 'jsr',
depName: '@scope/name',
depType: 'imports',
versioning: 'deno',
},
{
currentRawValue: 'jsr:@scope/name@0.2.0',
currentValue: '0.2.0',
datasource: 'jsr',
depName: '@scope/name',
depType: 'scopes',
versioning: 'deno',
},
],
lockFiles: ['deno.lock'],
managerData: {
importMapReferrer: 'deno.json',
},
packageFile: 'import_map.json',
});
});
it('remote importMap', async () => {
const result = await processImportMap(
'https://deno.land/x/import_map.json',
'demo.json',
[],
);
expect(result).toBeNull();
});
it('importMap path specified but not exists', async () => {
fs.readLocalFile.mockResolvedValueOnce(null);
const result = await processImportMap('import_map.json', 'demo.json', []);
expect(result).toBeNull();
});
it('invalid importMap file', async () => {
fs.readLocalFile.mockResolvedValueOnce('invalid');
const result = await processImportMap('import_map.json', 'demo.json', []);
expect(result).toBeNull();
});
});
describe('processDenoExtract()', () => {
it('importMap', async () => {
const result = await processDenoExtract({
content: {
lock: undefined,
dependencies: [],
importMap: 'import_map.json',
managerData: {
workspaces: undefined,
},
},
fileName: 'deno.json',
});
expect(result).toStrictEqual([
{
deps: [],
lockFiles: [],
managerData: {
workspaces: undefined,
},
packageFile: 'deno.json',
},
]);
});
});
describe('extractAllPackageFiles()', () => {
it('invalid deno.json file', async () => {
fs.getSiblingFileName.mockReturnValueOnce('deno.json');
fs.readLocalFile.mockResolvedValueOnce('invalid');
await expect(extractAllPackageFiles({}, ['deno.json'])).resolves.toEqual(
[],
);
});
it('multiple matched files with deno.json only', async () => {
// Mock for deno.json - returns valid package file
fs.readLocalFile.mockResolvedValueOnce(
JSON.stringify({
imports: {
dep1: 'npm:dep1@1.0.0',
},
}),
);
fs.getSiblingFileName.mockReturnValueOnce('deno.lock');
fs.localPathIsFile.mockResolvedValueOnce(false);
const result = await extractAllPackageFiles({}, ['deno.json']);
expect(result).toHaveLength(1);
expect(result[0].packageFile).toBe('deno.json');
expect(result[0].deps).toHaveLength(1);
});
it('deno.lock without package.json', async () => {
fs.getSiblingFileName.mockReturnValueOnce('deno.lock');
fs.localPathIsFile.mockResolvedValue(false);
await expect(extractAllPackageFiles({}, ['deno.lock'])).resolves.toEqual(
[],
);
});
it('deno.lock when collectPackageJson returns null', async () => {
vi.mocked(compat.collectPackageJson).mockResolvedValueOnce(null);
await expect(extractAllPackageFiles({}, ['deno.lock'])).resolves.toEqual(
[],
);
});
it('deno.lock when collectPackageJson returns empty array', async () => {
vi.mocked(compat.collectPackageJson).mockResolvedValueOnce([]);
await expect(extractAllPackageFiles({}, ['deno.lock'])).resolves.toEqual(
[],
);
});
it('complex config with imports, scopes, tasks and lint', async () => {
fs.readLocalFile.mockResolvedValueOnce(
JSON.stringify({
imports: {
dep1: 'npm:dep1@1.0.0',
dep10: 'https://deno.land/x/dep10@v0.1.12/mod.ts',
},
scopes: {
'https://deno.land/x/': {
dep1: 'npm:dep1@^2.0.0',
},
},
tasks: {
init: 'deno run -RWE jsr:@scope/dep4@latest',
dev: {
command: 'deno run -A --node-modules-dir=auto npm:dep5@6.0.0',
dependencies: ['init'],
},
build: 'deno run -A --node-modules-dir=auto npm:dep6 build',
changelog: {
description: 'description',
command:
'deno run --allow-read --allow-write https://deno.land/x/dep@v1.0.1/bin.ts',
},
joke: 'deno npm:dep7',
// these should be ignored
'not-contain-a-datasource': 'deno task npm:example',
'npm:example': 'deno task jsr:@scope/task',
'jsr:@scope/task': 'curl http://example.com',
},
compilerOptions: {
types: ['npm:@types/dep1@1.0.0'],
jsxImportSource: 'npm:dep2@1.0.0',
jsxImportSourceTypes: 'npm:@types/dep2@1.0.0',
},
lint: {
plugins: [
'npm:dep3@1.0.0/mod.ts',
'./local_dep.js', // should be ignored
],
},
}),
);
fs.getSiblingFileName.mockReturnValueOnce('deno.lock');
fs.localPathIsFile.mockResolvedValue(true);
fs.readLocalFile.mockResolvedValueOnce(
JSON.stringify({
version: '5',
}),
);
await expect(
extractAllPackageFiles({}, ['deno.jsonc', 'deno.lock']),
).resolves.toStrictEqual([
{
deps: [
{
currentRawValue: 'npm:dep1@1.0.0',
currentValue: '1.0.0',
datasource: 'npm',
depName: 'dep1',
depType: 'imports',
versioning: 'deno',
},
{
currentRawValue: 'https://deno.land/x/dep10@v0.1.12/mod.ts',
currentValue: 'v0.1.12',
datasource: 'deno',
depName: 'https://deno.land/x/dep10',
depType: 'imports',
},
{
currentRawValue: 'npm:dep1@^2.0.0',
currentValue: '^2.0.0',
datasource: 'npm',
depName: 'dep1',
depType: 'scopes',
versioning: 'deno',
},
{
currentRawValue: 'jsr:@scope/dep4@latest',
currentValue: 'latest',
datasource: 'jsr',
depName: '@scope/dep4',
depType: 'tasks',
versioning: 'deno',
},
{
currentRawValue: 'npm:dep5@6.0.0',
currentValue: '6.0.0',
datasource: 'npm',
depName: 'dep5',
depType: 'tasks.command',
versioning: 'deno',
},
{
currentRawValue: 'npm:dep6',
currentValue: undefined,
datasource: 'npm',
depName: 'dep6',
depType: 'tasks',
versioning: 'deno',
},
{
currentRawValue: 'https://deno.land/x/dep@v1.0.1/bin.ts',
currentValue: 'v1.0.1',
datasource: 'deno',
depName: 'https://deno.land/x/dep',
depType: 'tasks.command',
},
{
currentRawValue: 'npm:dep7',
currentValue: undefined,
datasource: 'npm',
depName: 'dep7',
depType: 'tasks',
versioning: 'deno',
},
{
depName: 'deno task npm:example',
depType: 'tasks',
skipReason: 'unsupported',
skipStage: 'extract',
},
{
depName: 'deno task jsr:@scope/task',
depType: 'tasks',
skipReason: 'unsupported',
skipStage: 'extract',
},
{
depName: 'curl http://example.com',
depType: 'tasks',
skipReason: 'unsupported',
skipStage: 'extract',
},
{
currentRawValue: 'npm:@types/dep1@1.0.0',
currentValue: '1.0.0',
datasource: 'npm',
depName: '@types/dep1',
depType: 'compilerOptions.types',
versioning: 'deno',
},
{
currentRawValue: 'npm:dep2@1.0.0',
currentValue: '1.0.0',
datasource: 'npm',
depName: 'dep2',
depType: 'compilerOptions.jsxImportSource',
versioning: 'deno',
},
{
currentRawValue: 'npm:@types/dep2@1.0.0',
currentValue: '1.0.0',
datasource: 'npm',
depName: '@types/dep2',
depType: 'compilerOptions.jsxImportSourceTypes',
versioning: 'deno',
},
{
currentRawValue: 'npm:dep3@1.0.0/',
currentValue: '1.0.0',
datasource: 'npm',
depName: 'dep3',
depType: 'lint.plugins',
versioning: 'deno',
},
{
depName: './local_dep.js',
depType: 'lint.plugins',
skipReason: 'unsupported',
skipStage: 'extract',
},
],
lockFiles: ['deno.lock'],
managerData: {
workspaces: undefined,
},
packageFile: 'deno.jsonc',
},
]);
});
// workspace tests
describe('workspaces', () => {
beforeAll(() => {
GlobalConfig.set({ localDir: '' });
});
it('npm workspace compatible', async () => {
vi.mocked(findPackages).mockResolvedValue([
{
dir: 'sub',
manifest: { name: 'sub' },
writeProjectManifest: Promise.resolve,
},
]);
// Mock collectPackageJson to return the npm workspace package files
vi.mocked(compat.collectPackageJson).mockImplementation((file) => {
if (file === 'deno.lock') {
return Promise.resolve([
{
deps: [
{
currentValue: '1.0.0',
datasource: 'npm',
depName: 'dep1',
depType: 'dependencies',
prettyDepType: 'dependency',
lockedVersion: '1.0.0',
},
],
managerData: {
packageName: 'root',
workspaces: ['sub'],
},
extractedConstraints: {},
lockFiles: ['deno.lock'],
packageFile: 'package.json',
packageFileVersion: '0.0.1',
},
{
deps: [
{
currentValue: '2.0.0',
datasource: 'npm',
depName: 'dep2',
depType: 'dependencies',
lockedVersion: '2.0.0',
prettyDepType: 'dependency',
},
],
managerData: {
packageName: 'sub',
workspaces: undefined,
},
extractedConstraints: {},
lockFiles: ['deno.lock'],
packageFile: 'sub/package.json',
packageFileVersion: '0.0.2',
},
] as any);
}
return Promise.resolve(null);
});
fs.getSiblingFileName.mockReturnValue('package.json');
fs.readLocalFile.mockImplementation((fileName) => {
if (fileName === 'package.json') {
return Promise.resolve(codeBlock`
{
"name": "root",
"version": "0.0.1",
"dependencies": {
"dep1": "1.0.0"
},
"workspaces": [
"sub"
]
}
`);
}
if (fileName === 'sub/package.json') {
return Promise.resolve(codeBlock`
{
"name": "sub",
"version": "0.0.2",
"dependencies": {
"dep2": "2.0.0"
}
}
`);
}
if (fileName === 'deno.lock') {
return Promise.resolve(codeBlock`
{
"version": "5",
"specifiers": {
"npm:dep1@1.0.0": "1.0.0",
"npm:dep2@2.0.0": "2.0.0"
}
}
`);
}
return Promise.resolve(null);
});
fs.localPathIsFile.mockImplementation((fileName) => {
return Promise.resolve(
fileName === 'package.json' ||
fileName === 'sub/package.json' ||
fileName === 'deno.lock',
);
});
await expect(
extractAllPackageFiles({}, ['deno.lock']),
).resolves.toStrictEqual([
{
deps: [
{
currentValue: '1.0.0',
datasource: 'npm',
depName: 'dep1',
depType: 'dependencies',
prettyDepType: 'dependency',
lockedVersion: '1.0.0',
},
],
managerData: {
packageName: 'root',
workspaces: ['sub'],
},
extractedConstraints: {},
lockFiles: ['deno.lock'],
packageFile: 'package.json',
packageFileVersion: '0.0.1',
},
{
deps: [
{
currentValue: '2.0.0',
datasource: 'npm',
depName: 'dep2',
depType: 'dependencies',
lockedVersion: '2.0.0',
prettyDepType: 'dependency',
},
],
managerData: {
packageName: 'sub',
workspaces: undefined,
},
extractedConstraints: {},
lockFiles: ['deno.lock'],
packageFile: 'sub/package.json',
packageFileVersion: '0.0.2',
},
]);
});
});
});
});