mirror of https://github.com/renovatebot/renovate
535 lines
15 KiB
TypeScript
535 lines
15 KiB
TypeScript
import { mocked } from '../../../test/util';
|
|
import { getOptions } from '../../config/options';
|
|
import * as _execUtils from '../exec/utils';
|
|
import * as template from '.';
|
|
|
|
jest.mock('../exec/utils');
|
|
|
|
const execUtils = mocked(_execUtils);
|
|
|
|
describe('util/template/index', () => {
|
|
beforeEach(() => {
|
|
execUtils.getChildEnv.mockReturnValue({
|
|
CUSTOM_FOO: 'foo',
|
|
HOME: '/root',
|
|
});
|
|
});
|
|
|
|
it('returns empty string if cannot compile', () => {
|
|
expect(template.safeCompile('{{abc', {})).toBe('');
|
|
});
|
|
|
|
it('has valid exposed config options', () => {
|
|
const allOptions = getOptions().map((option) => option.name);
|
|
const missingOptions = template.exposedConfigOptions.filter(
|
|
(option) => !allOptions.includes(option),
|
|
);
|
|
expect(missingOptions).toEqual([]);
|
|
});
|
|
|
|
it('filters out disallowed fields', () => {
|
|
const userTemplate =
|
|
'{{#if isFoo}}foo{{/if}}{{platform}} token = "{{token}}"';
|
|
const input = {
|
|
isFoo: true,
|
|
platform: 'github',
|
|
token: '123test ',
|
|
releases: [{ token: '123test' }],
|
|
logJSON: { token: '123test' },
|
|
};
|
|
const output = template.compile(userTemplate, input);
|
|
expect(output).toBe('github token = ""');
|
|
});
|
|
|
|
it('containsString', () => {
|
|
const userTemplate =
|
|
"{{#if (containsString platform 'git')}}True{{else}}False{{/if}}";
|
|
const input = { platform: 'github' };
|
|
const output = template.compile(userTemplate, input, false);
|
|
expect(output).toContain('True');
|
|
});
|
|
|
|
it('not containsString', () => {
|
|
const userTemplate =
|
|
"{{#if (containsString platform 'hub')}}True{{else}}False{{/if}}";
|
|
const input = { platform: 'gitlab' };
|
|
const output = template.compile(userTemplate, input);
|
|
expect(output).toContain('False');
|
|
});
|
|
|
|
it('and returns true when all parameters are true', () => {
|
|
const userTemplate =
|
|
'{{#if (and isMajor isSingleVersion isReplacement)}}True{{else}}False{{/if}}';
|
|
const input = { isMajor: true, isSingleVersion: true, isReplacement: true };
|
|
const output = template.compile(userTemplate, input);
|
|
expect(output).toContain('True');
|
|
});
|
|
|
|
it('and returns false when at least one parameter is false', () => {
|
|
const userTemplate =
|
|
'{{#if (and isMajor isPatch isGithub)}}True{{else}}False{{/if}}';
|
|
const input = { isMajor: true, isPatch: false, isReplacement: true };
|
|
const output = template.compile(userTemplate, input);
|
|
expect(output).toContain('False');
|
|
});
|
|
|
|
it('or returns true when at least one is true', () => {
|
|
const userTemplate =
|
|
'{{#if (or isMajor isPatch isReplacement)}}True{{else}}False{{/if}}';
|
|
const input = { isMajor: false, isPatch: true, isReplacement: false };
|
|
const output = template.compile(userTemplate, input);
|
|
expect(output).toContain('True');
|
|
});
|
|
|
|
it('or returns false when all are false', () => {
|
|
const userTemplate =
|
|
'{{#if (or isMajor isPatch isReplacement)}}True{{else}}False{{/if}}';
|
|
const input = { isMajor: false, isPatch: false, isReplacement: false };
|
|
const output = template.compile(userTemplate, input);
|
|
expect(output).toContain('False');
|
|
});
|
|
|
|
it('string to pretty JSON ', () => {
|
|
const userTemplate =
|
|
'{{{ stringToPrettyJSON \'{"some":{"fancy":"json"}}\'}}}';
|
|
const output = template.compile(userTemplate, undefined as never);
|
|
expect(output).toMatchSnapshot();
|
|
});
|
|
|
|
it('to JSON', () => {
|
|
const userTemplate = '{{{ toJSON upgrades }}}';
|
|
const input = {
|
|
upgrades: [
|
|
{
|
|
depName: 'foo-lib',
|
|
currentVersion: '1.0.0',
|
|
newVersion: '1.0.1',
|
|
},
|
|
],
|
|
};
|
|
const output = template.compile(userTemplate, input);
|
|
expect(JSON.parse(output)).toEqual(input.upgrades);
|
|
});
|
|
|
|
it('to JSON empty array', () => {
|
|
const userTemplate = '{{{ toJSON (toArray) }}}';
|
|
const output = template.compile(userTemplate, {});
|
|
expect(JSON.parse(output)).toEqual([]);
|
|
});
|
|
|
|
it('to JSON empty object', () => {
|
|
const userTemplate = '{{{ toJSON (toObject) }}}';
|
|
const output = template.compile(userTemplate, {});
|
|
expect(JSON.parse(output)).toEqual({});
|
|
});
|
|
|
|
it('to Object passing illegal number of elements', () => {
|
|
const userTemplate = "{{{ toJSON (toObject 'foo') }}}";
|
|
const outputFunc = () => template.compile(userTemplate, {});
|
|
expect(outputFunc).toThrow();
|
|
});
|
|
|
|
it('build complex json', () => {
|
|
const userTemplate =
|
|
"{{{ toJSON (toObject 'upgrades' upgrades 'array' (toArray platform isMajor 'foo')) }}}";
|
|
const input = {
|
|
platform: 'github',
|
|
isMajor: true,
|
|
upgrades: [
|
|
{
|
|
depName: 'foo-lib',
|
|
},
|
|
],
|
|
};
|
|
const output = template.compile(userTemplate, input);
|
|
expect(JSON.parse(output)).toEqual({
|
|
upgrades: input.upgrades,
|
|
array: [input.platform, input.isMajor, 'foo'],
|
|
});
|
|
});
|
|
|
|
it('lowercase', () => {
|
|
const userTemplate = "{{{ lowercase 'FOO'}}}";
|
|
const output = template.compile(userTemplate, undefined as never);
|
|
expect(output).toBe('foo');
|
|
});
|
|
|
|
it('has access to basic environment variables (basicEnvVars)', () => {
|
|
const userTemplate = 'HOME is {{env.HOME}}';
|
|
const output = template.compile(userTemplate, {});
|
|
expect(output).toBe('HOME is /root');
|
|
});
|
|
|
|
it('and has access to custom variables (customEnvVariables)', () => {
|
|
const userTemplate = 'CUSTOM_FOO is {{env.CUSTOM_FOO}}';
|
|
const output = template.compile(userTemplate, {});
|
|
expect(output).toBe('CUSTOM_FOO is foo');
|
|
});
|
|
|
|
it('and has access to prBodyDefinitions', () => {
|
|
const userTemplate =
|
|
'Issues: {{#each upgrades}}{{{prBodyDefinitions.Issue}}} {{/each}}';
|
|
const config = {
|
|
upgrades: [
|
|
{
|
|
prBodyDefinitions: {
|
|
Issue: '1234',
|
|
},
|
|
},
|
|
],
|
|
};
|
|
const output = template.compile(userTemplate, config);
|
|
expect(output).toBe('Issues: 1234 ');
|
|
});
|
|
|
|
it('replace', () => {
|
|
const userTemplate =
|
|
"{{ replace '[a-z]+\\.github\\.com' 'ghc' depName }}{{ replace 'some' 'other' depType }}";
|
|
const output = template.compile(userTemplate, {
|
|
depName: 'some.github.com/dep',
|
|
});
|
|
expect(output).toBe('ghc/dep');
|
|
});
|
|
|
|
describe('proxyCompileInput', () => {
|
|
const allowedField = 'body';
|
|
const allowedArrayField = 'prBodyNotes';
|
|
const forbiddenField = 'foobar';
|
|
|
|
type TestCompileInput = Record<
|
|
typeof allowedField | typeof allowedArrayField | typeof forbiddenField,
|
|
unknown
|
|
>;
|
|
|
|
const compileInput: TestCompileInput = {
|
|
[allowedField]: 'allowed',
|
|
[allowedArrayField]: ['allowed'],
|
|
[forbiddenField]: 'forbidden',
|
|
};
|
|
|
|
it('accessing allowed fields', () => {
|
|
const warnVariables = new Set<string>();
|
|
const p = template.proxyCompileInput(compileInput, warnVariables);
|
|
|
|
expect(p[allowedField]).toBe('allowed');
|
|
expect(p[allowedArrayField]).toStrictEqual(['allowed']);
|
|
expect(warnVariables).toBeEmpty();
|
|
expect(p[forbiddenField]).toBeUndefined();
|
|
expect(warnVariables).toEqual(new Set<string>([forbiddenField]));
|
|
});
|
|
|
|
it('supports object nesting', () => {
|
|
const warnVariables = new Set<string>();
|
|
const proxy = template.proxyCompileInput(
|
|
{
|
|
[allowedField]: compileInput,
|
|
},
|
|
warnVariables,
|
|
);
|
|
|
|
const obj = proxy[allowedField] as TestCompileInput;
|
|
expect(obj[allowedField]).toBe('allowed');
|
|
expect(warnVariables).toBeEmpty();
|
|
expect(obj[forbiddenField]).toBeUndefined();
|
|
expect(warnVariables).toEqual(new Set<string>([forbiddenField]));
|
|
});
|
|
|
|
it('supports array nesting', () => {
|
|
const warnVariables = new Set<string>();
|
|
const proxy = template.proxyCompileInput(
|
|
{
|
|
[allowedField]: [compileInput],
|
|
},
|
|
warnVariables,
|
|
);
|
|
|
|
const arr = proxy[allowedField] as TestCompileInput[];
|
|
const obj = arr[0];
|
|
expect(obj[allowedField]).toBe('allowed');
|
|
expect(obj[allowedArrayField]).toStrictEqual(['allowed']);
|
|
expect(warnVariables).toBeEmpty();
|
|
expect(obj[forbiddenField]).toBeUndefined();
|
|
expect(warnVariables).toEqual(new Set<string>([forbiddenField]));
|
|
});
|
|
});
|
|
|
|
describe('percent encoding', () => {
|
|
it('encodes values', () => {
|
|
const output = template.compile(
|
|
'{{{encodeURIComponent "@fsouza/prettierd"}}}',
|
|
undefined as never,
|
|
);
|
|
expect(output).toBe('%40fsouza%2Fprettierd');
|
|
});
|
|
|
|
it('decodes values', () => {
|
|
const output = template.compile(
|
|
'{{{decodeURIComponent "%40fsouza/prettierd"}}}',
|
|
undefined as never,
|
|
);
|
|
expect(output).toBe('@fsouza/prettierd');
|
|
});
|
|
});
|
|
|
|
describe('base64 encoding', () => {
|
|
it('encodes values', () => {
|
|
const output = template.compile(
|
|
'{{{encodeBase64 "@fsouza/prettierd"}}}',
|
|
undefined as never,
|
|
);
|
|
expect(output).toBe('QGZzb3V6YS9wcmV0dGllcmQ=');
|
|
});
|
|
|
|
it('handles null values gracefully', () => {
|
|
const output = template.compile('{{{encodeBase64 packageName}}}', {
|
|
packageName: null,
|
|
});
|
|
expect(output).toBe('');
|
|
});
|
|
|
|
it('handles undefined values gracefully', () => {
|
|
const output = template.compile('{{{encodeBase64 packageName}}}', {
|
|
packageName: undefined,
|
|
});
|
|
expect(output).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('equals', () => {
|
|
it('equals', () => {
|
|
const output = template.compile(
|
|
'{{#if (equals datasource "git-refs")}}https://github.com/{{packageName}}{{else}}{{packageName}}{{/if}}',
|
|
{
|
|
datasource: 'git-refs',
|
|
packageName: 'renovatebot/renovate',
|
|
},
|
|
);
|
|
expect(output).toBe('https://github.com/renovatebot/renovate');
|
|
});
|
|
|
|
it('not equals', () => {
|
|
const output = template.compile(
|
|
'{{#if (equals datasource "git-refs")}}https://github.com/{{packageName}}{{else}}{{packageName}}{{/if}}',
|
|
{
|
|
datasource: 'github-releases',
|
|
packageName: 'renovatebot/renovate',
|
|
},
|
|
);
|
|
expect(output).toBe('renovatebot/renovate');
|
|
});
|
|
|
|
it('not strict equals', () => {
|
|
const output = template.compile(
|
|
'{{#if (equals newMajor "3")}}equals{{else}}not equals{{/if}}',
|
|
{
|
|
newMajor: 3,
|
|
},
|
|
);
|
|
expect(output).toBe('not equals');
|
|
});
|
|
});
|
|
|
|
describe('includes', () => {
|
|
it('includes is true', () => {
|
|
const output = template.compile(
|
|
'{{#if (includes labels "dependencies")}}production{{else}}notProduction{{/if}}',
|
|
{
|
|
labels: ['dependencies'],
|
|
},
|
|
);
|
|
|
|
expect(output).toBe('production');
|
|
});
|
|
|
|
it('includes is false', () => {
|
|
const output = template.compile(
|
|
'{{#if (includes labels "dependencies")}}production{{else}}notProduction{{/if}}',
|
|
{
|
|
labels: ['devDependencies'],
|
|
},
|
|
);
|
|
|
|
expect(output).toBe('notProduction');
|
|
});
|
|
|
|
it('includes with incorrect type first argument', () => {
|
|
const output = template.compile(
|
|
'{{#if (includes labels "dependencies")}}production{{else}}notProduction{{/if}}',
|
|
{
|
|
labels: 'devDependencies',
|
|
},
|
|
);
|
|
|
|
expect(output).toBe('notProduction');
|
|
});
|
|
|
|
it('includes with incorrect type second argument', () => {
|
|
const output = template.compile(
|
|
'{{#if (includes labels 555)}}production{{else}}notProduction{{/if}}',
|
|
{
|
|
labels: ['devDependencies'],
|
|
},
|
|
);
|
|
|
|
expect(output).toBe('notProduction');
|
|
});
|
|
});
|
|
|
|
describe('split', () => {
|
|
it('should return empty array on non string input', () => {
|
|
const output = template.compile("test {{ split labels '-' }}", {
|
|
labels: 123,
|
|
});
|
|
expect(output).toBe('test ');
|
|
});
|
|
|
|
it('should return empty array on missing parameter', () => {
|
|
const output = template.compile('test {{ split labels }}', {
|
|
labels: 'foo-bar',
|
|
});
|
|
expect(output).toBe('test ');
|
|
});
|
|
|
|
it('should return array on success', () => {
|
|
const output = template.compile("{{ split labels '-' }}", {
|
|
labels: 'foo-bar',
|
|
});
|
|
expect(output).toBe('foo,bar');
|
|
});
|
|
|
|
it('should return array element', () => {
|
|
const output = template.compile(
|
|
"{{ lookup (split packageName '-') 1 }}",
|
|
{
|
|
packageName: 'foo-bar-test',
|
|
},
|
|
);
|
|
expect(output).toBe('bar');
|
|
});
|
|
});
|
|
|
|
describe('lookupArray', () => {
|
|
it('performs lookup for every array element', () => {
|
|
const output = template.compile(
|
|
'{{#each (lookupArray upgrades "prBodyDefinitions")}} {{{Issue}}}{{/each}}',
|
|
{
|
|
upgrades: [
|
|
{
|
|
prBodyDefinitions: {
|
|
Issue: 'ABC-123',
|
|
},
|
|
},
|
|
{},
|
|
{
|
|
prBodyDefinitions: {
|
|
Issue: 'DEF-456',
|
|
},
|
|
},
|
|
null,
|
|
undefined,
|
|
],
|
|
},
|
|
);
|
|
|
|
expect(output).toBe(' ABC-123 DEF-456');
|
|
});
|
|
|
|
it('handles null input array', () => {
|
|
const output = template.compile(
|
|
'{{#each (lookupArray testArray "prBodyDefinitions")}} {{{Issue}}}{{/each}}',
|
|
{
|
|
testArray: null,
|
|
},
|
|
false,
|
|
);
|
|
|
|
expect(output).toBe('');
|
|
});
|
|
|
|
it('handles empty string key', () => {
|
|
const output = template.compile(
|
|
'{{#each (lookupArray testArray "")}} {{{.}}}{{/each}}',
|
|
{
|
|
testArray: [
|
|
{
|
|
'': 'ABC-123',
|
|
},
|
|
],
|
|
},
|
|
false,
|
|
);
|
|
|
|
expect(output).toBe(' ABC-123');
|
|
});
|
|
|
|
it('handles null key', () => {
|
|
const output = template.compile(
|
|
'{{#each (lookupArray testArray null)}} {{{.}}}{{/each}}',
|
|
{
|
|
testArray: [
|
|
{
|
|
null: 'ABC-123',
|
|
},
|
|
],
|
|
},
|
|
false,
|
|
);
|
|
|
|
expect(output).toBe(' ABC-123');
|
|
});
|
|
});
|
|
|
|
describe('distinct', () => {
|
|
it('skips duplicate values', () => {
|
|
const output = template.compile(
|
|
'{{#each (distinct (lookupArray (lookupArray upgrades "prBodyDefinitions") "Issue"))}} {{{.}}}{{/each}}',
|
|
{
|
|
upgrades: [
|
|
{
|
|
prBodyDefinitions: {
|
|
Issue: 'ABC-123',
|
|
},
|
|
},
|
|
{
|
|
prBodyDefinitions: {
|
|
Issue: 'DEF-456',
|
|
},
|
|
},
|
|
{
|
|
prBodyDefinitions: {
|
|
Issue: 'ABC-123',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
);
|
|
|
|
expect(output).toBe(' ABC-123 DEF-456');
|
|
});
|
|
|
|
it('handles null elements', () => {
|
|
const output = template.compile(
|
|
'{{#each (distinct input)}}{{{.}}}{{/each}}',
|
|
{
|
|
input: [null, null],
|
|
},
|
|
false,
|
|
);
|
|
|
|
expect(output).toBe('');
|
|
});
|
|
|
|
it('handles null input', () => {
|
|
const output = template.compile(
|
|
'{{#each (distinct input)}}{{{.}}}{{/each}}',
|
|
{
|
|
input: null,
|
|
},
|
|
false,
|
|
);
|
|
|
|
expect(output).toBe('');
|
|
});
|
|
});
|
|
});
|