247 lines
6.5 KiB
Ruby
247 lines
6.5 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
require 'spec_helper'
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::Identifier do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'evaluates a selector' do
|
||
|
data = { 'number' => 10 }
|
||
|
|
||
|
expect(described_class.new('number').evaluate(state, data)).to eq(10)
|
||
|
end
|
||
|
|
||
|
it 'returns nil if the key is not set' do
|
||
|
expect(described_class.new('number').evaluate(state, {})).to be_nil
|
||
|
end
|
||
|
|
||
|
it 'returns nil if the input is not a Hash' do
|
||
|
expect(described_class.new('number').evaluate(state, 45)).to be_nil
|
||
|
end
|
||
|
|
||
|
it 'returns the current data when using the special identifier "it"' do
|
||
|
expect(described_class.new('it').evaluate(state, 45)).to eq(45)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::Integer do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'evaluates a selector' do
|
||
|
expect(described_class.new(0).evaluate(state, [10])).to eq(10)
|
||
|
end
|
||
|
|
||
|
it 'returns nil if the index is not set' do
|
||
|
expect(described_class.new(1).evaluate(state, [10])).to be_nil
|
||
|
end
|
||
|
|
||
|
it 'returns nil if the input is not an Array' do
|
||
|
expect(described_class.new(0).evaluate(state, {})).to be_nil
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::Selector do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
let(:data) { { 'numbers' => [10] } }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'evaluates a selector' do
|
||
|
ident = Gitlab::Changelog::AST::Identifier.new('numbers')
|
||
|
int = Gitlab::Changelog::AST::Integer.new(0)
|
||
|
|
||
|
expect(described_class.new([ident, int]).evaluate(state, data)).to eq(10)
|
||
|
end
|
||
|
|
||
|
it 'evaluates a selector that returns nil' do
|
||
|
int = Gitlab::Changelog::AST::Integer.new(0)
|
||
|
|
||
|
expect(described_class.new([int]).evaluate(state, data)).to be_nil
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::Variable do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
let(:data) { { 'numbers' => [10] } }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'evaluates a variable' do
|
||
|
node = Gitlab::Changelog::Parser
|
||
|
.new
|
||
|
.parse_and_transform('{{numbers.0}}')
|
||
|
.nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, data)).to eq('10')
|
||
|
end
|
||
|
|
||
|
it 'evaluates an undefined variable' do
|
||
|
node =
|
||
|
Gitlab::Changelog::Parser.new.parse_and_transform('{{foobar}}').nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, data)).to eq('')
|
||
|
end
|
||
|
|
||
|
it 'evaluates the special variable "it"' do
|
||
|
node =
|
||
|
Gitlab::Changelog::Parser.new.parse_and_transform('{{it}}').nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, data)).to eq(data.to_s)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::Expressions do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'evaluates all expressions' do
|
||
|
node = Gitlab::Changelog::Parser
|
||
|
.new
|
||
|
.parse_and_transform('{{number}}foo')
|
||
|
|
||
|
expect(node.evaluate(state, { 'number' => 10 })).to eq('10foo')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::Text do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'returns the text' do
|
||
|
expect(described_class.new('foo').evaluate(state, {})).to eq('foo')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::If do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'evaluates a truthy if expression without an else clause' do
|
||
|
node = Gitlab::Changelog::Parser
|
||
|
.new
|
||
|
.parse_and_transform('{% if thing %}foo{% end %}')
|
||
|
.nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, { 'thing' => true })).to eq('foo')
|
||
|
end
|
||
|
|
||
|
it 'evaluates a falsy if expression without an else clause' do
|
||
|
node = Gitlab::Changelog::Parser
|
||
|
.new
|
||
|
.parse_and_transform('{% if thing %}foo{% end %}')
|
||
|
.nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, { 'thing' => false })).to eq('')
|
||
|
end
|
||
|
|
||
|
it 'evaluates a falsy if expression with an else clause' do
|
||
|
node = Gitlab::Changelog::Parser
|
||
|
.new
|
||
|
.parse_and_transform('{% if thing %}foo{% else %}bar{% end %}')
|
||
|
.nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, { 'thing' => false })).to eq('bar')
|
||
|
end
|
||
|
end
|
||
|
|
||
|
describe '#truthy?' do
|
||
|
it 'returns true for a non-empty String' do
|
||
|
expect(described_class.new.truthy?('foo')).to eq(true)
|
||
|
end
|
||
|
|
||
|
it 'returns true for a non-empty Array' do
|
||
|
expect(described_class.new.truthy?([10])).to eq(true)
|
||
|
end
|
||
|
|
||
|
it 'returns true for a Boolean true' do
|
||
|
expect(described_class.new.truthy?(true)).to eq(true)
|
||
|
end
|
||
|
|
||
|
it 'returns false for an empty String' do
|
||
|
expect(described_class.new.truthy?('')).to eq(false)
|
||
|
end
|
||
|
|
||
|
it 'returns true for an empty Array' do
|
||
|
expect(described_class.new.truthy?([])).to eq(false)
|
||
|
end
|
||
|
|
||
|
it 'returns false for a Boolean false' do
|
||
|
expect(described_class.new.truthy?(false)).to eq(false)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
RSpec.describe Gitlab::Changelog::AST::Each do
|
||
|
let(:state) { Gitlab::Changelog::EvalState.new }
|
||
|
|
||
|
describe '#evaluate' do
|
||
|
it 'evaluates the expression' do
|
||
|
data = { 'animals' => [{ 'name' => 'Cat' }, { 'name' => 'Dog' }] }
|
||
|
node = Gitlab::Changelog::Parser
|
||
|
.new
|
||
|
.parse_and_transform('{% each animals %}{{name}}{% end %}')
|
||
|
.nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, data)).to eq('CatDog')
|
||
|
end
|
||
|
|
||
|
it 'returns an empty string when the input is not a collection' do
|
||
|
data = { 'animals' => 10 }
|
||
|
node = Gitlab::Changelog::Parser
|
||
|
.new
|
||
|
.parse_and_transform('{% each animals %}{{name}}{% end %}')
|
||
|
.nodes[0]
|
||
|
|
||
|
expect(node.evaluate(state, data)).to eq('')
|
||
|
end
|
||
|
|
||
|
it 'disallows too many nested loops' do
|
||
|
data = {
|
||
|
'foo' => [
|
||
|
{
|
||
|
'bar' => [
|
||
|
{
|
||
|
'baz' => [
|
||
|
{
|
||
|
'quix' => [
|
||
|
{
|
||
|
'foo' => [{ 'name' => 'Alice' }]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
|
||
|
template = <<~TPL
|
||
|
{% each foo %}
|
||
|
{% each bar %}
|
||
|
{% each baz %}
|
||
|
{% each quix %}
|
||
|
{% each foo %}
|
||
|
{{name}}
|
||
|
{% end %}
|
||
|
{% end %}
|
||
|
{% end %}
|
||
|
{% end %}
|
||
|
{% end %}
|
||
|
TPL
|
||
|
|
||
|
node =
|
||
|
Gitlab::Changelog::Parser.new.parse_and_transform(template).nodes[0]
|
||
|
|
||
|
expect { node.evaluate(state, data) }
|
||
|
.to raise_error(Gitlab::Changelog::Error)
|
||
|
end
|
||
|
end
|
||
|
end
|