gitlab-org--gitlab-foss/spec/lib/gitlab/template_parser/ast_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

247 lines
6.6 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::TemplateParser::AST::Identifier do
let(:state) { Gitlab::TemplateParser::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::TemplateParser::AST::Integer do
let(:state) { Gitlab::TemplateParser::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::TemplateParser::AST::Selector do
let(:state) { Gitlab::TemplateParser::EvalState.new }
let(:data) { { 'numbers' => [10] } }
describe '#evaluate' do
it 'evaluates a selector' do
ident = Gitlab::TemplateParser::AST::Identifier.new('numbers')
int = Gitlab::TemplateParser::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::TemplateParser::AST::Integer.new(0)
expect(described_class.new([int]).evaluate(state, data)).to be_nil
end
end
end
RSpec.describe Gitlab::TemplateParser::AST::Variable do
let(:state) { Gitlab::TemplateParser::EvalState.new }
let(:data) { { 'numbers' => [10] } }
describe '#evaluate' do
it 'evaluates a variable' do
node = Gitlab::TemplateParser::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::TemplateParser::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::TemplateParser::Parser.new.parse_and_transform('{{it}}').nodes[0]
expect(node.evaluate(state, data)).to eq(data.to_s)
end
end
end
RSpec.describe Gitlab::TemplateParser::AST::Expressions do
let(:state) { Gitlab::TemplateParser::EvalState.new }
describe '#evaluate' do
it 'evaluates all expressions' do
node = Gitlab::TemplateParser::Parser
.new
.parse_and_transform('{{number}}foo')
expect(node.evaluate(state, { 'number' => 10 })).to eq('10foo')
end
end
end
RSpec.describe Gitlab::TemplateParser::AST::Text do
let(:state) { Gitlab::TemplateParser::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::TemplateParser::AST::If do
let(:state) { Gitlab::TemplateParser::EvalState.new }
describe '#evaluate' do
it 'evaluates a truthy if expression without an else clause' do
node = Gitlab::TemplateParser::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::TemplateParser::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::TemplateParser::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::TemplateParser::AST::Each do
let(:state) { Gitlab::TemplateParser::EvalState.new }
describe '#evaluate' do
it 'evaluates the expression' do
data = { 'animals' => [{ 'name' => 'Cat' }, { 'name' => 'Dog' }] }
node = Gitlab::TemplateParser::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::TemplateParser::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::TemplateParser::Parser.new.parse_and_transform(template).nodes[0]
expect { node.evaluate(state, data) }
.to raise_error(Gitlab::TemplateParser::Error)
end
end
end