Clone
action_service_spec.rb
require 'spec_helper'

describe Locomotive::Steam::ActionService do

  let(:site_hash)     { { 'name' => 'Acme Corp' } }
  let(:site)          { instance_double('Site', as_json: site_hash ) }
  let(:email_service) { instance_double('EmailService') }
  let(:entry_service) { instance_double('ContentService') }
  let(:api_service)   { instance_double('ExternalAPIService') }
  let(:redirection_service) { instance_double('PageRedirectionService') }
  let(:service)       { described_class.new(site, email_service, content_entry: entry_service, api: api_service, redirection: redirection_service) }

  describe '#run' do

    let(:script)  { 'return 1 + 1;' }
    let(:params)  { {} }
    let(:assigns) { {} }
    let(:session) { {} }
    let(:context) { ::Liquid::Context.new(assigns, {}, { session: session }) }

    subject { service.run(script, params, context) }

    it { is_expected.to eq 2.0 }

    describe 'deal with exceptions' do

      context 'wrong syntax' do

        let(:script) { 'a +/ b * var;' }

        it 'raises a meaningful exception' do
          expect { subject }.to raise_error(Locomotive::Steam::ActionError, "eof or line terminator in regexp (line 2)")
        end

      end

      context 'other error' do

        let(:script) { 'a.b' }

        it 'raises a meaningful exception' do
          expect { subject }.to raise_error(Locomotive::Steam::ActionError, "identifier 'a' undefined")
        end

      end

    end

    describe 'with params' do

      let(:params)  { { 'foo' => 'hello' } }
      let(:script)  { "return params.foo + ' world';" }

      it { is_expected.to eq 'hello world' }

      describe "messing with params" do

        let(:script)  { "params.foo += ' world!';" }

        it { is_expected.to eq nil }

        it "can't change a param value" do
          subject
          expect(params['foo']).to eq 'hello'
        end

      end

    end

    describe 'built-in functions / getters / setters' do

      describe 'site' do

        let(:script) { 'return "Name: " + site.name;' }

        it { is_expected.to eq 'Name: Acme Corp' }

      end

      describe 'getProp' do

        let(:assigns) { { 'name' => 'John' } }
        let(:script) { "return getProp('name');" }

        it { is_expected.to eq 'John' }

      end

      describe 'setProp' do

        let(:script) { "return setProp('done', true);" }

        it { subject; expect(context['done']).to eq true }

      end

      describe 'getSessionProp' do

        let(:session) { { name: 'John' } }
        let(:script) { "return getSessionProp('name');" }

        it { is_expected.to eq 'John' }

      end

      describe 'sendSessionProp' do

        let(:script) { "return setSessionProp('done', true);" }

        it { subject; expect(session[:done]).to eq true }

      end

      describe 'allEntries' do

        let(:now)     { Time.use_zone('America/Chicago') { Time.zone.local(2015, 'mar', 25, 10, 0) } }
        let(:assigns) { { 'now' => now } }
        let(:script) {
          <<-JS
            var entries = allEntries('bands', { 'created_at.lte': getProp('now'), published: true });
            var names = []

            for (var i = 0; i < entries.length; i++) {
              names.push(entries[i].name)
            }

            return names.join(', ')
          JS
        }

        before do
          expect(entry_service).to receive(:all).with('bands', { "created_at.lte" => "2015-03-25T10:00:00.000-05:00", "published" => true }, true).and_return([
            { 'name' => 'Pearl Jam' },
            { 'name' => 'Nirvana' },
            { 'name' => 'Soundgarden' }
          ])
        end

        it { is_expected.to eq('Pearl Jam, Nirvana, Soundgarden') }

      end

      describe 'findEntry' do

        let(:script) { "return findEntry('bands', '42').name;" }

        before do
          expect(entry_service).to receive(:find).with('bands', '42', true).and_return('name' => 'Pearl Jam')
        end

        it { is_expected.to eq('Pearl Jam') }

      end

      describe 'createEntry' do

        let(:script) { "return createEntry('bands', { name: 'Pearl Jam'}).name;" }

        before do
          expect(entry_service).to receive(:create).with('bands', { 'name' => 'Pearl Jam' }, true).and_return('name' => 'Pearl Jam')
        end

        it { is_expected.to eq('Pearl Jam') }

      end

      describe 'updateEntry' do

        let(:script) { "return updateEntry('bands', 'pearl-jam', { name: 'Pearl Jam'}).name;" }

        before do
          expect(entry_service).to receive(:update).with('bands', 'pearl-jam', { 'name' => 'Pearl Jam' }, true).and_return('name' => 'Pearl Jam')
        end

        it { is_expected.to eq('Pearl Jam') }

      end

      describe 'sendEmail' do

        let(:params) { { 'to' => 'estelle@locomotivecms.com' } }
        let(:script) { "sendEmail({ to: params.to, from: 'did@locomotivecms.com', subject: 'Happy Easter' })" }

        it 'forwards the action to the email service' do
          expect(email_service).to receive(:send_email).with({
            'to'      => 'estelle@locomotivecms.com',
            'from'    => 'did@locomotivecms.com',
            'subject' => 'Happy Easter' }, context)
          subject
        end

      end

      describe 'callAPI' do

        let(:script) { "callAPI('POST', 'https://api.stripe.com/v1/charges', { username: 'abcdefghij', data: { token: '123456789' } })" }

        it 'forwards the action to the external api service' do
          expect(api_service).to receive(:consume).with(
            'https://api.stripe.com/v1/charges', {
              method: 'POST',
              username: 'abcdefghij',
              data: {
                token: '123456789'
              }
            }, true
          )
          subject
        end

      end

      describe 'redirectTo' do

        let(:script) { "redirectTo('about-us');" }

        it 'stops the rendering process and redirects the user to another page' do
          expect(redirection_service).to receive(:redirect_to).with('about-us', nil).and_raise(Locomotive::Steam::RedirectionException.new('/about-us'))
          expect { subject }.to raise_exception(Locomotive::Steam::RedirectionException, 'Redirect to /about-us')
        end

      end

    end

  end

end