first cucumber features + crud for pages (95% done)
This commit is contained in:
parent
3b847b2732
commit
a0216dc75f
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ db/*.sqlite3
|
||||
log/*.log
|
||||
tmp/**/*
|
||||
.DS_Store
|
||||
rerun.txt
|
||||
|
9
Gemfile
9
Gemfile
@ -7,7 +7,8 @@ gem "rails", "3.0.0.beta3"
|
||||
gem "liquid"
|
||||
gem "bson_ext", '0.20.1'
|
||||
gem "mongo_ext"
|
||||
gem "mongoid", ">= 2.0.0.beta2"
|
||||
gem "mongoid", ">= 2.0.0.beta4"
|
||||
gem "mongoid_acts_as_tree", :git => 'git://github.com/evansagge/mongoid_acts_as_tree.git'
|
||||
gem "warden"
|
||||
gem "devise", ">= 1.1.rc0"
|
||||
gem "haml", '>= 3.0.0.beta.2', :git => 'git://github.com/nex3/haml.git'
|
||||
@ -28,5 +29,9 @@ group :test do
|
||||
gem 'rspec-rails', '>= 2.0.0.beta.5'
|
||||
gem 'factory_girl', :git => 'git://github.com/thoughtbot/factory_girl.git', :branch => 'rails3'
|
||||
gem 'capybara', :git => 'git://github.com/jnicklas/capybara.git'
|
||||
gem 'cucumber-rails', :git => 'git://github.com/aslakhellesoy/cucumber-rails.git'
|
||||
gem 'cucumber', '0.7.2'
|
||||
gem 'cucumber-rails'
|
||||
gem 'spork'
|
||||
gem 'launchy'
|
||||
gem 'mocha', :git => 'git://github.com/floehopper/mocha.git'
|
||||
end
|
@ -5,7 +5,7 @@ class Admin::LayoutsController < Admin::BaseController
|
||||
def index
|
||||
@layouts = current_site.layouts
|
||||
end
|
||||
|
||||
|
||||
def new
|
||||
@layout = current_site.layouts.build
|
||||
end
|
||||
|
10
app/controllers/admin/page_parts_controller.rb
Normal file
10
app/controllers/admin/page_parts_controller.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class Admin::PagePartsController < Admin::BaseController
|
||||
|
||||
layout nil
|
||||
|
||||
def index
|
||||
parts = current_site.layouts.find(params[:layout_id]).parts
|
||||
render :json => { :parts => parts }
|
||||
end
|
||||
|
||||
end
|
@ -8,10 +8,11 @@ class Admin::PagesController < Admin::BaseController
|
||||
|
||||
def new
|
||||
@page = current_site.pages.build
|
||||
@page.parts << PagePart.build_body_part
|
||||
end
|
||||
|
||||
def edit
|
||||
@page = current_site.pages.find(params[:id])
|
||||
@page = current_site.pages.find(params[:id])
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -6,4 +6,10 @@ class Admin::SessionsController < Devise::SessionsController
|
||||
|
||||
before_filter :require_site
|
||||
|
||||
protected
|
||||
|
||||
def after_sign_in_path_for(resource)
|
||||
admin_pages_url
|
||||
end
|
||||
|
||||
end
|
@ -3,7 +3,6 @@ module Admin::PagesHelper
|
||||
def parent_pages_options
|
||||
roots = current_site.pages.roots.where(:slug.ne => '404').and(:_id.ne => @page.id)
|
||||
|
||||
puts roots.to_a.inspect
|
||||
returning [] do |list|
|
||||
roots.each do |page|
|
||||
list = add_children_to_options(page, list)
|
||||
|
@ -1,10 +1,12 @@
|
||||
class Layout < LiquidTemplate
|
||||
|
||||
## associations ##
|
||||
has_many_related :pages
|
||||
embeds_many :parts, :class_name => 'PagePart'
|
||||
|
||||
## callbacks ##
|
||||
before_save :build_parts_from_value
|
||||
after_save :update_parts_in_pages
|
||||
|
||||
## validations ##
|
||||
validates_format_of :value, :with => Locomotive::Regexps::CONTENT_FOR_LAYOUT, :message => :missing_content_for_layout
|
||||
@ -15,23 +17,31 @@ class Layout < LiquidTemplate
|
||||
|
||||
def build_parts_from_value
|
||||
if self.value_changed? || self.new_record?
|
||||
self.parts = []
|
||||
self.parts.clear
|
||||
|
||||
(groups = self.value.scan(Locomotive::Regexps::CONTENT_FOR)).each do |part|
|
||||
body = nil
|
||||
|
||||
self.value.scan(Locomotive::Regexps::CONTENT_FOR).each do |part|
|
||||
part[1].strip!
|
||||
part[1] = nil if part[1].empty?
|
||||
|
||||
slug = part[0].strip.downcase
|
||||
|
||||
name = (if slug == 'layout'
|
||||
I18n.t('admin.shared.attributes.body')
|
||||
if slug == 'layout'
|
||||
body = PagePart.new :slug => slug, :name => I18n.t('admin.shared.attributes.body')
|
||||
else
|
||||
(part[1] || slug).gsub("\"", '')
|
||||
end)
|
||||
|
||||
self.parts.build :slug => slug, :name => name
|
||||
self.parts.build :slug => slug, :name => (part[1] || slug).gsub("\"", '')
|
||||
end
|
||||
end
|
||||
|
||||
self.parts.insert(0, body) if body
|
||||
|
||||
@_update_pages = true if self.value_changed?
|
||||
end
|
||||
end
|
||||
|
||||
def update_parts_in_pages
|
||||
self.pages.each { |p| p.send(:update_parts!, self.parts) } if @_update_pages
|
||||
end
|
||||
|
||||
end
|
@ -13,6 +13,7 @@ class Page
|
||||
|
||||
## associations ##
|
||||
belongs_to_related :site
|
||||
belongs_to_related :layout
|
||||
embeds_many :parts, :class_name => 'PagePart'
|
||||
|
||||
## callbacks ##
|
||||
@ -22,7 +23,7 @@ class Page
|
||||
before_save :change_parent
|
||||
before_create { |p| p.fix_position(false) }
|
||||
before_create :add_to_list_bottom
|
||||
before_create :add_body_part
|
||||
# before_create :add_body_part
|
||||
before_destroy :remove_from_list
|
||||
|
||||
## validations ##
|
||||
@ -34,6 +35,7 @@ class Page
|
||||
|
||||
## behaviours ##
|
||||
acts_as_tree :order => ['position', 'asc']
|
||||
# accepts_nested_attributes_for :parts, :allow_destroy => true
|
||||
|
||||
## methods ##
|
||||
|
||||
@ -45,8 +47,12 @@ class Page
|
||||
self.slug == '404' && self.depth.to_i == 0
|
||||
end
|
||||
|
||||
def parts_attributes=(attributes)
|
||||
self.update_parts(attributes.values.map { |attrs| PagePart.new(attrs) })
|
||||
end
|
||||
|
||||
def add_body_part
|
||||
self.parts.build :name => 'body', :value => '---body here---'
|
||||
self.parts.build :name => 'body', :slug => 'layout', :value => '---body here---'
|
||||
end
|
||||
|
||||
def parent=(owner) # missing in acts_as_tree
|
||||
@ -76,11 +82,35 @@ class Page
|
||||
|
||||
def ancestors
|
||||
return [] if root?
|
||||
self.class.find(self.path.clone << nil) # bug in mongoid (it does not handle array with on element)
|
||||
self.class.find(self.path.clone << nil) # bug in mongoid (it does not handle array with one element)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def update_parts(parts)
|
||||
performed = []
|
||||
|
||||
# add / update
|
||||
parts.each do |part|
|
||||
if (existing = self.parts.detect { |p| p.id == part.id || p.slug == part.slug })
|
||||
existing.attributes = part.attributes.delete_if { |k, v| %w{_id slug}.include?(k) }
|
||||
else
|
||||
self.parts << (existing = part)
|
||||
end
|
||||
performed << existing unless existing.disabled?
|
||||
end
|
||||
|
||||
# disable missing parts
|
||||
(self.parts.map(&:slug) - performed.map(&:slug)).each do |slug|
|
||||
self.parts.detect { |p| p.slug == slug }.disabled = true
|
||||
end
|
||||
end
|
||||
|
||||
def update_parts!(new_parts)
|
||||
self.update_parts(new_parts)
|
||||
self.save
|
||||
end
|
||||
|
||||
def change_parent
|
||||
if self.parent_id_changed?
|
||||
self.fix_position(false)
|
||||
|
@ -1,20 +1,36 @@
|
||||
class PagePart
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
# include Mongoid::Timestamps
|
||||
|
||||
## fields ##
|
||||
field :name, :type => String
|
||||
field :slug, :type => String
|
||||
field :value, :type => String
|
||||
field :disabled, :type => Boolean, :default => false
|
||||
field :value, :type => String
|
||||
|
||||
## associations ##
|
||||
embedded_in :page, :inverse_of => :parts
|
||||
|
||||
# attr_accessor :_delete
|
||||
|
||||
## callbacks ##
|
||||
before_validate { |p| p.slug ||= p.name.slugify if p.name.present? }
|
||||
# before_validate { |p| p.slug ||= p.name.slugify if p.name.present? }
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :name, :slug
|
||||
|
||||
## named scopes ##
|
||||
named_scope :enabled, where(:disabled => false)
|
||||
|
||||
## methods ##
|
||||
|
||||
# def _delete=(value)
|
||||
# puts "set _delete #{value.inspect}"
|
||||
# self.attributes[:_destroy] = true if %w(t 1 true).include?(value)
|
||||
# end
|
||||
|
||||
def self.build_body_part
|
||||
self.new(:name => I18n.t('admin.shared.attributes.body'), :slug => 'layout')
|
||||
end
|
||||
end
|
@ -1,7 +1,13 @@
|
||||
- content_for :head do
|
||||
= javascript_include_tag 'admin/plugins/codemirror/codemirror', 'admin/plugins/wslide', 'admin/pages', 'admin/page_parts'
|
||||
= stylesheet_link_tag 'admin/page_parts'
|
||||
|
||||
= f.foldable_inputs :name => :information do
|
||||
|
||||
= f.input :title
|
||||
|
||||
= f.input :layout_id, :as => :select, :collection => current_site.layouts.all, :input_html => { :data_url => admin_layout_page_parts_url('_id_to_replace_') }
|
||||
|
||||
- if not @page.index? and not @page.not_found?
|
||||
= f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false
|
||||
|
||||
@ -15,57 +21,17 @@
|
||||
= f.input :keywords
|
||||
= f.input :description
|
||||
|
||||
#page-parts
|
||||
.nav
|
||||
- @page.parts.each_with_index do |part, index|
|
||||
= link_to content_tag(:span, part.name), "#parts-#{index + 1}", :id => "control-part-#{part.slug}", :class => "part-#{index} #{'on' if index == 0}", :style => "#{'display: none' if part.disabled?}"
|
||||
.clear
|
||||
|
||||
|
||||
|
||||
/ <% f.foldable_inputs :name => :information do %>
|
||||
/ <%= f.input :title, :required => false %>
|
||||
/
|
||||
/ <% f.custom_input :path, :css => 'path' do %>
|
||||
/ /<%= f.text_field :path %>.html
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% f.custom_input :layout do %>
|
||||
/ <%= f.select :layout_id, current_site.layouts.all.collect { |l| [l.name, l.id] }, :include_blank => true %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <%= f.input :keywords, :required => false %>
|
||||
/ <%= f.input :description, :required => false %>
|
||||
/
|
||||
/ <% f.custom_input :cache_expires_in do %>
|
||||
/ <%= f.select :cache_expires_in, options_for_page_cache_expiration %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% f.custom_input :visible, :css => 'checkbox' do %>
|
||||
/ <%= f.check_box :visible %>
|
||||
/ <% end %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% unless f.object.new_record? %>
|
||||
/ <div id="page-parts">
|
||||
/ <div class="jcarousel-control">
|
||||
/ <% if body_part? %>
|
||||
/ <a class="part-0 on"><%= t('admin.pages.form.body') %></a><span></span><% end -%><% @page.parts.active.each_with_index do |p, index| -%><a class="part-<%= index + (body_part? ? 1 : 0) %> <%= 'on' if !body_part? && index == 0 %>"><%= p.slug.humanize %></a><span></span>
|
||||
/ <% end %>
|
||||
/ </div>
|
||||
/
|
||||
/ <div class="wrapper">
|
||||
/ <ul>
|
||||
/ <% if body_part? %>
|
||||
/ <li><code><%= f.text_area :body %></code></li>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% f.fields_for :parts do |g| %>
|
||||
/ <% unless g.object.disabled? %>
|
||||
/ <li><code><%= g.text_area :body, :class => 'big' %></code></li>
|
||||
/ <% end %>
|
||||
/ <% end %>
|
||||
/ </ul>
|
||||
/ </div>
|
||||
/ </div>
|
||||
/
|
||||
/ <% content_for :head do %>
|
||||
/ <%= javascript_include_tag 'admin/plugins/iphoneSwitch', 'admin/plugins/checkbox', 'admin/plugins/carousel', 'codemirror/codemirror', 'admin/pages' %>
|
||||
/ <%= stylesheet_link_tag 'carousel', 'admin/page_parts' %>
|
||||
/ <% end %>
|
||||
/ <% end %>
|
||||
.wrapper
|
||||
%ul{ :id => "parts" }
|
||||
= f.fields_for :parts do |g|
|
||||
%li{ :style => "#{'display: none' if g.object.disabled?}" }
|
||||
%code= g.text_area :value
|
||||
= g.hidden_field :name
|
||||
= g.hidden_field :slug
|
||||
= g.hidden_field :disabled, :class => 'disabled'
|
@ -1,8 +1,5 @@
|
||||
- title link_to(@page.title.blank? ? @page.title_was : @page.title, '#', :rel => 'page_title', :title => t('.ask_for_title'), :class => 'editable')
|
||||
|
||||
- content_for :head do
|
||||
= javascript_include_tag 'admin/pages'
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/menu/contents'
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
- title t('.title')
|
||||
|
||||
- content_for :head do
|
||||
= javascript_include_tag 'admin/pages'
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/menu/contents'
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
<%
|
||||
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
|
||||
rerun_opts = rerun.to_s.strip.empty? ? "--format progress features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
|
||||
std_opts = "#{rerun_opts} --format rerun --out rerun.txt --strict --tags ~@wip"
|
||||
rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
|
||||
std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --tags ~@wip"
|
||||
%>
|
||||
default: <%= std_opts %>
|
||||
default: <%= std_opts %> features
|
||||
wip: --tags @wip:3 --wip features
|
||||
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
|
||||
|
@ -5,7 +5,7 @@ development:
|
||||
<<: *defaults
|
||||
database: locomotive_dev
|
||||
|
||||
test:
|
||||
test: &test
|
||||
<<: *defaults
|
||||
database: locomotive_test
|
||||
|
||||
@ -15,3 +15,6 @@ production:
|
||||
username: user
|
||||
password: pass
|
||||
database: fanboy
|
||||
|
||||
cucumber:
|
||||
<<: *test
|
@ -14,8 +14,10 @@ end
|
||||
|
||||
## various patches
|
||||
|
||||
# Enabling scope in validates_uniqueness_of validation
|
||||
|
||||
module Mongoid #:nodoc:
|
||||
|
||||
# Enabling scope in validates_uniqueness_of validation
|
||||
module Validations #:nodoc:
|
||||
class UniquenessValidator < ActiveModel::EachValidator
|
||||
def validate_each(document, attribute, value, scope = nil)
|
||||
@ -26,4 +28,14 @@ module Mongoid #:nodoc:
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# FIX BUG #71 http://github.com/durran/mongoid/commit/47a97094b32448aa09965c854a24c78803c7f42e
|
||||
module Associations
|
||||
module InstanceMethods
|
||||
def update_embedded(name)
|
||||
association = send(name)
|
||||
association.to_a.each { |doc| doc.save if doc.changed? || doc.new_record? } unless association.blank?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -20,7 +20,7 @@ en:
|
||||
|
||||
messages:
|
||||
confirm: Are you sure ?
|
||||
|
||||
|
||||
shared:
|
||||
header:
|
||||
welcome: Welcome, {{name}}
|
||||
@ -48,6 +48,9 @@ en:
|
||||
title: Listing pages
|
||||
no_items: "There are no pages for now. Just click <a href=\"{{url}}\">here</a> to create the first one."
|
||||
new: new page
|
||||
messages:
|
||||
successful_create: "Page was successfully created."
|
||||
successful_update: "Page was successfully updated."
|
||||
|
||||
layouts:
|
||||
index:
|
||||
|
@ -22,7 +22,9 @@ Locomotive::Application.routes.draw do |map|
|
||||
get :get_path, :on => :collection
|
||||
end
|
||||
|
||||
resources :layouts
|
||||
resources :layouts do
|
||||
resources :page_parts, :only => :index
|
||||
end
|
||||
resources :snippets
|
||||
|
||||
# get 'login' => 'sessions#new', :as => :new_account_session
|
||||
|
13
doc/TODO
13
doc/TODO
@ -15,14 +15,19 @@ x store node closed or open in cookies
|
||||
x snippets section
|
||||
x menu items have to be translated
|
||||
x layout needs at least content_for_layout
|
||||
- validates_uniqueness_of :slug, :scope => :id
|
||||
- page parts
|
||||
- can not delete index + 404 pages
|
||||
x parts js/css:
|
||||
x codemirror
|
||||
x change bg (separator)
|
||||
x when a tab is selected, if we change layout, we should move to the first visible one
|
||||
x page parts
|
||||
x layout part should be always in first
|
||||
x pages section (CRUD)
|
||||
- slug unique within a folder
|
||||
- pages section (CRUD)
|
||||
- validates_uniqueness_of :slug, :scope => :id
|
||||
- refactoring page.rb => create module pagetree
|
||||
- my account section (part of settings)
|
||||
- layouts section
|
||||
- create 404 + index pages once a site is created
|
||||
- can not delete index + 404 pages
|
||||
- refactoring admin crud (pages + layouts + snippets)
|
||||
- remove all pages, snippets, ...etc when destroying a website
|
19
features/admin/login.feature
Normal file
19
features/admin/login.feature
Normal file
@ -0,0 +1,19 @@
|
||||
@site_up
|
||||
Feature: Login
|
||||
In order to access locomotive admin panel
|
||||
As an administrator
|
||||
I want to log in
|
||||
|
||||
Scenario: Successful authentication
|
||||
When I go to login
|
||||
And I fill in "account_email" with "admin@locomotiveapp.org"
|
||||
And I fill in "account_password" with "easyone"
|
||||
And I press "Log in"
|
||||
Then I should see "Listing pages"
|
||||
|
||||
Scenario: Failed authentication
|
||||
When I go to login
|
||||
And I fill in "account_email" with "admin@locomotiveapp.org"
|
||||
And I fill in "account_password" with ""
|
||||
And I press "Log in"
|
||||
Then I should not see "Listing pages"
|
31
features/admin/pages.feature
Normal file
31
features/admin/pages.feature
Normal file
@ -0,0 +1,31 @@
|
||||
@site_up
|
||||
@authenticated
|
||||
Feature: Manage Skills
|
||||
In order to manage pages
|
||||
As an administrator
|
||||
I want to add/edit/delete pages of my site
|
||||
|
||||
Scenario: Pages list is not accessible for non authenticated accounts
|
||||
Given I am not authenticated
|
||||
When I go to pages
|
||||
Then I should see "Login"
|
||||
|
||||
Scenario: Creating a valid page
|
||||
When I go to pages
|
||||
And I follow "new page"
|
||||
And I fill in "Title" with "Test"
|
||||
And I fill in "Slug" with "test"
|
||||
And I select "Home page" from "Parent"
|
||||
And I fill in "page_parts_attributes_0_value" with "Lorem ipsum...."
|
||||
And I press "Create"
|
||||
Then I should see "Page was successfully created."
|
||||
And I should have "Lorem ipsum...." in the test page layout
|
||||
|
||||
Scenario: Updating a valid page
|
||||
When I go to pages
|
||||
And I follow "Home page"
|
||||
And I fill in "Title" with "Home page !"
|
||||
And I fill in "page_parts_attributes_0_value" with "My new content is here"
|
||||
And I press "Update"
|
||||
Then I should see "Page was successfully updated."
|
||||
And I should have "My new content is here" in the index page layout
|
63
features/step_definitions/admin_steps.rb
Normal file
63
features/step_definitions/admin_steps.rb
Normal file
@ -0,0 +1,63 @@
|
||||
Before('@site_up') do
|
||||
create_site_and_admin_account
|
||||
create_layout_samples
|
||||
create_index_page
|
||||
end
|
||||
|
||||
Before('@authenticated') do
|
||||
Given %{I am an authenticated user}
|
||||
end
|
||||
|
||||
### Authentication
|
||||
|
||||
Given /^I am not authenticated$/ do
|
||||
visit('/admin/logout')
|
||||
end
|
||||
|
||||
|
||||
Given /^I am an authenticated user$/ do
|
||||
Given %{I go to login}
|
||||
And %{I fill in "account_email" with "admin@locomotiveapp.org"}
|
||||
And %{I fill in "account_password" with "easyone"}
|
||||
And %{I press "Log in"}
|
||||
end
|
||||
|
||||
Then /^I am redirected to "([^\"]*)"$/ do |url|
|
||||
assert [301, 302].include?(@integration_session.status), "Expected status to be 301 or 302, got #{@integration_session.status}"
|
||||
location = @integration_session.headers["Location"]
|
||||
assert_equal url, location
|
||||
visit location
|
||||
end
|
||||
|
||||
### Pages
|
||||
|
||||
|
||||
Then /^I should have "(.*)" in the (.*) page (.*)$/ do |content, page_slug, slug|
|
||||
page = @site.pages.where(:slug => page_slug).first
|
||||
part = page.parts.where(:slug => slug).first
|
||||
part.should_not be_nil
|
||||
part.value.should == content
|
||||
end
|
||||
|
||||
## Common
|
||||
|
||||
def create_site_and_admin_account
|
||||
@site = Factory(:site, :name => 'Locomotive test website', :subdomain => 'test')
|
||||
@admin = Factory(:account, { :name => 'Admin', :email => 'admin@locomotiveapp.org' })
|
||||
end
|
||||
|
||||
def create_layout_samples
|
||||
Factory(:layout, :site => @site, :name => 'One column', :value => %{<html>
|
||||
<head>
|
||||
<title>My website</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">\{\{ content_for_layout \}\}</div>
|
||||
</body>
|
||||
</html>})
|
||||
Factory(:layout, :site => @site)
|
||||
end
|
||||
|
||||
def create_index_page
|
||||
Factory(:page, :site => @site, :layout => @site.layouts.last, :parts => [PagePart.new(:slug => 'layout', :name => 'Body', :value => 'Hello world')])
|
||||
end
|
@ -6,6 +6,7 @@
|
||||
|
||||
|
||||
require 'uri'
|
||||
require 'cgi'
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
|
||||
|
||||
module WithinHelpers
|
||||
@ -147,7 +148,8 @@ end
|
||||
|
||||
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should contain "([^\"]*)"$/ do |field, selector, value|
|
||||
with_scope(selector) do
|
||||
field_value = find_field(field).value
|
||||
field = find_field(field)
|
||||
field_value = (field.tag_name == 'textarea') ? field.text : field.value
|
||||
if field_value.respond_to? :should
|
||||
field_value.should =~ /#{value}/
|
||||
else
|
||||
@ -158,11 +160,12 @@ end
|
||||
|
||||
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should not contain "([^\"]*)"$/ do |field, selector, value|
|
||||
with_scope(selector) do
|
||||
field_value = find_field(field).value
|
||||
field = find_field(field)
|
||||
field_value = (field.tag_name == 'textarea') ? field.text : field.value
|
||||
if field_value.respond_to? :should_not
|
||||
field_value.should_not =~ /#{value}/
|
||||
else
|
||||
assert_no_match(/#{value}/, field_value.value)
|
||||
assert_no_match(/#{value}/, field_value)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -199,9 +202,11 @@ Then /^(?:|I )should be on (.+)$/ do |page_name|
|
||||
end
|
||||
|
||||
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
|
||||
actual_params = CGI.parse(URI.parse(current_url).query)
|
||||
expected_params = Hash[expected_pairs.rows_hash.map{|k,v| [k,[v]]}]
|
||||
|
||||
query = URI.parse(current_url).query
|
||||
actual_params = query ? CGI.parse(query) : {}
|
||||
expected_params = {}
|
||||
expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
|
||||
|
||||
if actual_params.respond_to? :should
|
||||
actual_params.should == expected_params
|
||||
else
|
||||
|
@ -10,7 +10,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
|
||||
require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support
|
||||
require 'cucumber/rails/rspec'
|
||||
require 'cucumber/rails/world'
|
||||
require 'cucumber/rails/active_record'
|
||||
require 'cucumber/web/tableish'
|
||||
|
||||
require 'capybara/rails'
|
||||
@ -34,26 +33,27 @@ Capybara.default_selector = :css
|
||||
# of your scenarios, as this makes it hard to discover errors in your application.
|
||||
ActionController::Base.allow_rescue = false
|
||||
|
||||
# If you set this to true, each scenario will run in a database transaction.
|
||||
# You can still turn off transactions on a per-scenario basis, simply tagging
|
||||
# a feature or scenario with the @no-txn tag. If you are using Capybara,
|
||||
# tagging with @culerity or @javascript will also turn transactions off.
|
||||
#
|
||||
# If you set this to false, transactions will be off for all scenarios,
|
||||
# regardless of whether you use @no-txn or not.
|
||||
#
|
||||
# Beware that turning transactions off will leave data in your database
|
||||
# after each scenario, which can lead to hard-to-debug failures in
|
||||
# subsequent scenarios. If you do this, we recommend you create a Before
|
||||
# block that will explicitly put your database in a known state.
|
||||
Cucumber::Rails::World.use_transactional_fixtures = true
|
||||
require 'factory_girl'
|
||||
require 'spec/factories'
|
||||
|
||||
# How to clean your database when transactions are turned off. See
|
||||
# http://github.com/bmabey/database_cleaner for more info.
|
||||
if defined?(ActiveRecord::Base)
|
||||
begin
|
||||
require 'database_cleaner'
|
||||
DatabaseCleaner.strategy = :truncation
|
||||
rescue LoadError => ignore_if_database_cleaner_not_present
|
||||
end
|
||||
Before do
|
||||
Mongoid.master.collections.each(&:drop)
|
||||
end
|
||||
|
||||
Locomotive.configure do |config|
|
||||
config.default_domain = 'example.com'
|
||||
end
|
||||
|
||||
# class ActionController::Integration::Session
|
||||
# def reset_with_test_subdomain!
|
||||
# self.reset_without_test_subdomain!
|
||||
# self.host = "test.example.com"
|
||||
# end
|
||||
# alias_method_chain :reset!, :test_subdomain
|
||||
# end
|
||||
#
|
||||
# class ActionDispatch::Integration::Session
|
||||
# DEFAULT_HOST = 'test.example.com'
|
||||
# end
|
||||
|
||||
Capybara.default_host = 'test.example.com'
|
@ -7,10 +7,16 @@ module NavigationHelpers
|
||||
#
|
||||
def path_to(page_name)
|
||||
case page_name
|
||||
|
||||
|
||||
when /the home\s?page/
|
||||
'/'
|
||||
|
||||
when /login/
|
||||
new_account_session_path
|
||||
when /logout/
|
||||
destroy_account_session_path
|
||||
when /pages/
|
||||
admin_pages_path
|
||||
|
||||
# Add more mappings here.
|
||||
# Here is an example that pulls values out of the Regexp:
|
||||
#
|
||||
@ -18,8 +24,14 @@ module NavigationHelpers
|
||||
# user_profile_path(User.find_by_login($1))
|
||||
|
||||
else
|
||||
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
||||
"Now, go and add a mapping in #{__FILE__}"
|
||||
begin
|
||||
page_name =~ /the (.*) page/
|
||||
path_components = $1.split(/\s+/)
|
||||
self.send(path_components.push('path').join('_').to_sym)
|
||||
rescue Object => e
|
||||
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
||||
"Now, go and add a mapping in #{__FILE__}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -14,18 +14,24 @@ begin
|
||||
require 'cucumber/rake/task'
|
||||
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
|
||||
Cucumber::Rake::Task.new(:ok, 'Run features that should pass') do |t|
|
||||
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'default'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
|
||||
Cucumber::Rake::Task.new(:wip, 'Run features that are being worked on') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'wip'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new(:rerun, 'Record failing features and run only them if any exist') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'rerun'
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip]
|
||||
end
|
||||
|
BIN
public/images/admin/form/header-left-on.png
Normal file
BIN
public/images/admin/form/header-left-on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 235 B |
BIN
public/images/admin/form/header-right-on.png
Normal file
BIN
public/images/admin/form/header-right-on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 311 B |
@ -24,7 +24,7 @@ var addCodeMirrorEditor = function(type, el, parser) {
|
||||
if (type == 'liquid') type = 'xml';
|
||||
|
||||
var editor = CodeMirror.fromTextArea(el.attr('id'), {
|
||||
height: "330px",
|
||||
height: "400px",
|
||||
parserfile: parserfile,
|
||||
stylesheet: ["/stylesheets/admin/plugins/codemirror/" + type + "colors.css", "/stylesheets/admin/plugins/codemirror/liquidcolors.css"],
|
||||
path: "/javascripts/admin/plugins/codemirror/",
|
||||
|
83
public/javascripts/admin/page_parts.js
Normal file
83
public/javascripts/admin/page_parts.js
Normal file
@ -0,0 +1,83 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
// slider
|
||||
var resetSlider = function() {
|
||||
$('#page-parts .wrapper ul').wslide({
|
||||
width: 880,
|
||||
height: 400,
|
||||
autolink: false,
|
||||
duration: 300,
|
||||
horiz: true
|
||||
});
|
||||
}
|
||||
|
||||
resetSlider();
|
||||
|
||||
// codemirror
|
||||
$('#parts code textarea').each(function() { addCodeMirrorEditor('liquid', $(this)); });
|
||||
|
||||
var refreshParts = function(parts) {
|
||||
console.log('refreshParts');
|
||||
$('#page-parts .nav a').removeClass('enabled');
|
||||
|
||||
$(parts).each(function() {
|
||||
console.log("iterating..." + this.slug);
|
||||
var control = $('#control-part-' + this.slug);
|
||||
|
||||
// adding missing part
|
||||
if (control.size() == 0) {
|
||||
console.log('adding part');
|
||||
var nbParts = $('#page-parts .nav a').size();
|
||||
$('#page-parts .nav .clear').before('<a id="control-part-' + this.slug + '" class="enabled part-' + nbParts + '" href="#parts-' + (nbParts + 1) + '"><span>' + this.name + '</span></a>');
|
||||
|
||||
var textareaInput = '<textarea rows="20" name="page[parts_attributes][' + nbParts + '][value]" id="page_parts_attributes_' + nbParts + '_value"/>';
|
||||
var hiddenInputs = '<input type="hidden" name="page[parts_attributes][' + nbParts + '][name]" value="' + this.name + '" />'
|
||||
hiddenInputs += '<input type="hidden" name="page[parts_attributes][' + nbParts + '][slug]" value="' + this.slug + '" />'
|
||||
$('#page-parts .wrapper ul').append('<li id="part-' + nbParts + '" class="new"><code>' + textareaInput + '</code>' + hiddenInputs + '</li>');
|
||||
|
||||
resetSlider();
|
||||
$('#parts li:last code textarea').each(function() { addCodeMirrorEditor('liquid', $(this)); });
|
||||
} else {
|
||||
var index = parseInt(control.attr('class').match(/part-(.+)/)[1]) + 1;
|
||||
var wrapper = $('#parts-' + index);
|
||||
|
||||
// updating part
|
||||
control.html('<span>' + this.name + '</span>').addClass('enabled').show();
|
||||
wrapper.find('input.disabled').val('false');
|
||||
wrapper.show();
|
||||
}
|
||||
});
|
||||
|
||||
// removing or hiding parts
|
||||
$('#page-parts .nav a:not(.enabled)').each(function() {
|
||||
var index = parseInt($(this).attr('class').match(/part-(.+)/)[1]) + 1;
|
||||
var wrapper = $('#parts-' + index);
|
||||
if (wrapper.hasClass('new')) {
|
||||
$(this).remove(); wrapper.remove();
|
||||
} else {
|
||||
wrapper.find('input.disabled').val('true');
|
||||
$(this).hide(); wrapper.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// go to the first one if we hid the selected wrapper
|
||||
var selectedNav = $('#page-parts .nav a.on');
|
||||
if (selectedNav.size() == 1) {
|
||||
var index = parseInt(selectedNav.attr('class').match(/part-(.+)/)[1]) + 1;
|
||||
if ($('#parts-' + index + ':visible').size() == 0)
|
||||
$('#page-parts .nav a:first').click();
|
||||
} else
|
||||
$('#page-parts .nav a:first').click();
|
||||
}
|
||||
|
||||
var loadPartsFromLayout = function() {
|
||||
if ($('#page_layout_id').val() == '')
|
||||
return ;
|
||||
|
||||
var url = $('#page_layout_id').attr('data_url').replace('_id_to_replace_', $('#page_layout_id').val());
|
||||
$.get(url, '', function(data) { refreshParts(data.parts) }, 'json');
|
||||
}
|
||||
|
||||
$('#page_layout_id').change(loadPartsFromLayout);
|
||||
|
||||
});
|
125
public/javascripts/admin/plugins/wslide.js
Normal file
125
public/javascripts/admin/plugins/wslide.js
Normal file
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* wSlide 0.1 - http://www.webinventif.fr/wslide-plugin/
|
||||
*
|
||||
* Rendez vos sites glissant !
|
||||
*
|
||||
* Copyright (c) 2008 Julien Chauvin (webinventif.fr)
|
||||
* Licensed under the Creative Commons License:
|
||||
* http://creativecommons.org/licenses/by/3.0/
|
||||
*
|
||||
* Date: 2008-01-27
|
||||
*/
|
||||
(function($){
|
||||
$.fn.wslide = function(h) {
|
||||
h = jQuery.extend({
|
||||
width: 150,
|
||||
height: 150,
|
||||
pos: 1,
|
||||
col: 1,
|
||||
effect: 'swing',
|
||||
fade: false,
|
||||
horiz: false,
|
||||
autolink: true,
|
||||
duration: 1500
|
||||
}, h);
|
||||
function gogogo(g){
|
||||
g.each(function(i){
|
||||
var a = $(this);
|
||||
var uniqid = a.attr('id');
|
||||
if(uniqid == undefined){
|
||||
uniqid = 'wslide'+i;
|
||||
}
|
||||
|
||||
if ($(this).parent().hasClass('wslide-wrap'))
|
||||
a = $(this).parent();
|
||||
else {
|
||||
$(this).wrap('<div class="wslide-wrap" id="'+uniqid+'-wrap"></div>');
|
||||
a = $('#'+uniqid+'-wrap');
|
||||
}
|
||||
|
||||
var b = a.find('ul li');
|
||||
var effets = h.effect;
|
||||
if(jQuery.easing.easeInQuad == undefined && (effets!='swing' || effets!='normal')){
|
||||
effets = 'swing';
|
||||
}
|
||||
var typex = h.width;
|
||||
var typey = h.height;
|
||||
function resultante(prop){
|
||||
var tempcalc = prop;
|
||||
tempcalc = tempcalc.split('px');
|
||||
tempcalc = tempcalc[0];
|
||||
return Number(tempcalc);
|
||||
}
|
||||
var litypex = typex-(resultante(b.css('padding-left'))+resultante(b.css('padding-right')));
|
||||
var litypey = typey-(resultante(b.css('padding-top'))+resultante(b.css('padding-bottom')));
|
||||
var col = h.col;
|
||||
if(h.horiz){
|
||||
col = Number(b.length+1);
|
||||
}
|
||||
var manip = '';
|
||||
var ligne = Math.ceil(Number(b.length)/col);
|
||||
a.css('overflow','hidden').css('position','relative').css('text-align','left').css('height',typey+'px').css('width',typex+'px').css('margin','0').css('padding','0');
|
||||
a.find('ul').css('position','absolute').css('margin','0').css('padding','0').css('width',Number((col+0)*typex)+'px').css('height',Number(ligne*typey)+'px');
|
||||
b.css('display','block').css('overflow','hidden').css('float','left').css('height',litypey+'px').css('width',litypex+'px');
|
||||
b.each(function (i) {
|
||||
var offset = a.offset();
|
||||
var thisoffset = $(this).offset();
|
||||
$(this).attr('id',uniqid+'-'+Number(i+1)).attr('rel', Number(thisoffset.left-offset.left)+':'+Number(thisoffset.top-offset.top));
|
||||
manip += ' <a href="#'+uniqid+'-'+Number(i+1)+'">'+Number(i+1)+'</a>';
|
||||
});
|
||||
|
||||
if(typeof h.autolink == 'boolean'){
|
||||
if(h.autolink){
|
||||
a.after('<div class="wslide-menu" id="'+uniqid+'-menu">'+manip+'</div>');
|
||||
}
|
||||
}else if (typeof h.autolink == 'string'){
|
||||
if($('#'+h.autolink).length){
|
||||
$('#'+h.autolink).html(manip);
|
||||
}else{
|
||||
a.after('<div id="#'+h.autolink+'">'+manip+'</div>');
|
||||
}
|
||||
}
|
||||
var start = '#'+uniqid+'-';
|
||||
var stoccurent = "";
|
||||
$('a[href*="'+start+'"]').unbind('click').click(function () {
|
||||
$('a[href*="'+stoccurent+'"]').removeClass("on");
|
||||
$(this).addClass("on");
|
||||
var tri = $(this).attr('href');
|
||||
tri=tri.split('#');
|
||||
tri='#'+tri[1];
|
||||
stoccurent = tri;
|
||||
var decal = $(tri).attr('rel');
|
||||
decal = decal.split(':');
|
||||
var decal2 = decal[1];
|
||||
decal2 = -decal2;
|
||||
decal = decal[0];
|
||||
decal = -decal;
|
||||
if(h.fade){
|
||||
a.find('ul').animate({ opacity: 0 }, h.duration/2, effets, function(){$(this).css('top',decal2+'px').css('left',decal+'px');$(this).animate({ opacity: 1 }, h.duration/2, effets)} );
|
||||
}else{
|
||||
a.find('ul').animate({ top: decal2+'px',left: decal+'px' }, h.duration, effets );
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(h.pos <= 0){
|
||||
h.pos = 1;
|
||||
}
|
||||
$('a[href$="'+start+h.pos+'"]').addClass("on");
|
||||
var tri = $('a[href*="'+start+'"]:eq('+Number(h.pos-1)+')').attr('href');
|
||||
tri=tri.split('#');
|
||||
tri='#'+tri[1];
|
||||
stoccurent = tri;
|
||||
var decal = $(tri).attr('rel');
|
||||
decal = decal.split(':');
|
||||
var decal2 = decal[1];
|
||||
decal2 = -decal2;
|
||||
decal = decal[0];
|
||||
decal = -decal;
|
||||
a.find('ul').css('top',decal2+'px').css('left',decal+'px');
|
||||
|
||||
})
|
||||
}
|
||||
gogogo(this);
|
||||
return this;
|
||||
}
|
||||
})(jQuery);
|
60
public/stylesheets/admin/page_parts.css
Normal file
60
public/stylesheets/admin/page_parts.css
Normal file
@ -0,0 +1,60 @@
|
||||
#page-parts .wrapper {
|
||||
background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
|
||||
width: 880px;
|
||||
padding: 20px 20px;
|
||||
}
|
||||
|
||||
#page-parts {
|
||||
background: transparent url(/images/admin/form/header.png) no-repeat 0 0;
|
||||
}
|
||||
|
||||
#page-parts .control {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#page-parts .nav a {
|
||||
float: left;
|
||||
display: block;
|
||||
height: 30px;
|
||||
padding: 0px 0px 0 11px;
|
||||
color: #8b8d9a;
|
||||
text-decoration: none;
|
||||
font-size: 0.8em;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#page-parts .nav a span {
|
||||
display: inline-block;
|
||||
height: 26px;
|
||||
padding: 4px 11px 0 0px;
|
||||
}
|
||||
|
||||
#page-parts .nav a:first-child { padding-left: 22px; }
|
||||
#page-parts .nav a:first-child span { padding-right: 13px; }
|
||||
|
||||
#page-parts .nav a.on {
|
||||
color: #1e1f26;
|
||||
font-weight: bold;
|
||||
background: transparent url(/images/admin/form/header-left-on.png) no-repeat 0 0;
|
||||
}
|
||||
|
||||
#page-parts .nav a.on span {
|
||||
background: transparent url(/images/admin/form/header-right-on.png) no-repeat right 0;
|
||||
}
|
||||
|
||||
#page-parts .nav a:first-child.on {
|
||||
background: transparent url(/images/admin/form/header-first-on.png) no-repeat 0 0;
|
||||
}
|
||||
|
||||
#page-parts .wslide-wrap {
|
||||
border: 1px solid #a6a8b8;
|
||||
}
|
||||
|
||||
#page-parts code { display: block; background: white; height: 400px; }
|
||||
#page-parts code textarea {
|
||||
width: 880px;
|
||||
height: 400px;
|
||||
background: transparent url(../../images/admin/form/field.png) repeat-x 0 0;
|
||||
border: 0px;
|
||||
}
|
@ -35,11 +35,11 @@ Factory.define :layout do |l|
|
||||
l.name '1 main column + sidebar'
|
||||
l.value %{<html>
|
||||
<head>
|
||||
<title>Hello world !</title>
|
||||
<title>My website</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">\{\{ content_for_layout \}\}</div>
|
||||
<div id="sidebar">\{\{ content_for_sidebar "Left Sidebar"\}\}</div>
|
||||
<div id="main">\{\{ content_for_layout \}\}</div>
|
||||
</body>
|
||||
</html>}
|
||||
end
|
||||
|
@ -14,13 +14,14 @@ describe Layout do
|
||||
layout.errors[:value].should == ["should contain 'content_for_layout' liquid tag"]
|
||||
end
|
||||
|
||||
describe 'once created' do
|
||||
describe 'page parts' do
|
||||
|
||||
before(:each) do
|
||||
@layout = Factory(:layout)
|
||||
@layout = Factory.build(:layout)
|
||||
end
|
||||
|
||||
it 'should have 2 parts' do
|
||||
@layout.send(:build_parts_from_value)
|
||||
@layout.parts.count.should == 2
|
||||
|
||||
@layout.parts.first.name.should == 'Body'
|
||||
@ -30,6 +31,22 @@ describe Layout do
|
||||
@layout.parts.last.slug.should == 'sidebar'
|
||||
end
|
||||
|
||||
it 'should not add parts to pages if layout does not change' do
|
||||
@layout.stubs(:value_changed?).returns(false)
|
||||
page = Factory.build(:page, :layout => @layout, :site => nil)
|
||||
page.expects(:update_parts!).never
|
||||
@layout.pages << page
|
||||
@layout.save
|
||||
end
|
||||
|
||||
it 'should add parts to pages if layout changes' do
|
||||
@layout.value = @layout.value + "..."
|
||||
page = Factory.build(:page, :layout => @layout, :site => nil)
|
||||
page.expects(:update_parts!)
|
||||
@layout.pages << page
|
||||
@layout.save
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -48,13 +48,7 @@ describe Page do
|
||||
Factory.build(:page, :slug => 'index', :site => nil).index?.should be_true
|
||||
Factory.build(:page, :slug => 'index', :depth => 1, :site => nil).index?.should be_false
|
||||
end
|
||||
|
||||
it 'should add the body part' do
|
||||
page = Factory(:page)
|
||||
page.parts.should_not be_empty
|
||||
page.parts.first.name.should == 'body'
|
||||
end
|
||||
|
||||
|
||||
it 'should have normalized slug' do
|
||||
page = Factory.build(:page, :slug => ' Valid ité.html ')
|
||||
page.valid?
|
||||
@ -67,6 +61,120 @@ describe Page do
|
||||
|
||||
end
|
||||
|
||||
describe 'accepts_nested_attributes_for used for parts' do
|
||||
|
||||
before(:each) do
|
||||
@page = Factory.build(:page)
|
||||
@page.parts.build(:name => 'Main content', :slug => 'layout')
|
||||
@page.parts.build(:name => 'Left column', :slug => 'left_sidebar')
|
||||
@page.parts.build(:name => 'Right column', :slug => 'right_sidebar')
|
||||
end
|
||||
|
||||
it 'should add parts' do
|
||||
attributes = { '0' => { 'slug' => 'footer', 'name' => 'A custom footer', 'value' => 'End of page' } }
|
||||
@page.parts_attributes = attributes
|
||||
@page.parts.size.should == 4
|
||||
@page.parts.last.slug.should == 'footer'
|
||||
@page.parts.last.disabled.should == false
|
||||
end
|
||||
|
||||
it 'should update parts' do
|
||||
attributes = { '0' => { 'slug' => 'layout', 'name' => 'A new name', 'value' => 'Hello world' } }
|
||||
@page.parts_attributes = attributes
|
||||
@page.parts.size.should == 3
|
||||
@page.parts.first.slug.should == 'layout'
|
||||
@page.parts.first.name.should == 'A new name'
|
||||
@page.parts.first.value.should == 'Hello world'
|
||||
end
|
||||
|
||||
it 'should disable parts' do
|
||||
attributes = { '0' => { 'slug' => 'left_sidebar', 'disabled' => 'true' } }
|
||||
@page.parts_attributes = attributes
|
||||
@page.parts.size.should == 3
|
||||
@page.parts.first.disabled.should == true
|
||||
@page.parts[1].disabled.should == true
|
||||
@page.parts[2].disabled.should == true
|
||||
end
|
||||
|
||||
it 'should add/update/disable parts at the same time' do
|
||||
@page.parts.size.should == 3
|
||||
|
||||
attributes = {
|
||||
'0' => { 'slug' => 'layout', 'name' => 'Body', 'value' => 'Hello world' },
|
||||
'1' => { 'slug' => 'left_sidebar', 'disabled' => 'true' },
|
||||
'2' => { 'id' => @page.parts[2].id, 'value' => 'Content from right sidebar', 'disabled' => 'false' }
|
||||
}
|
||||
@page.parts_attributes = attributes
|
||||
@page.parts.size.should == 3
|
||||
|
||||
@page.parts[0].value.should == 'Hello world'
|
||||
@page.parts[1].disabled.should == true
|
||||
@page.parts[2].disabled.should == false
|
||||
end
|
||||
|
||||
it 'should update it with success (mongoid bug #71)' do
|
||||
@page.save
|
||||
@page = Page.first
|
||||
|
||||
@page.parts.size.should == 3
|
||||
@page.parts_attributes = { '0' => { 'slug' => 'header', 'name' => 'A custom header', 'value' => 'Head of page' } }
|
||||
@page.parts.size.should == 4
|
||||
|
||||
@page.save
|
||||
@page = Page.first
|
||||
|
||||
@page.parts.size.should == 4
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'dealing with page parts' do # DUPLICATED ?
|
||||
|
||||
before(:each) do
|
||||
@page = Factory.build(:page)
|
||||
@parts = [
|
||||
PagePart.new(:name => 'Main content', :slug => 'layout'),
|
||||
PagePart.new(:name => 'Left column', :slug => 'left_sidebar'),
|
||||
PagePart.new(:name => 'Right column', :slug => 'right_sidebar')
|
||||
]
|
||||
@page.send(:update_parts, @parts)
|
||||
end
|
||||
|
||||
it 'should add new parts from an array of parts' do
|
||||
@page.parts.size.should == 3
|
||||
@page.parts.shift.name.should == 'Main content'
|
||||
@page.parts.shift.name.should == 'Left column'
|
||||
@page.parts.shift.name.should == 'Right column'
|
||||
end
|
||||
|
||||
it 'should update parts' do
|
||||
@parts[1].name = 'Very left column'
|
||||
@page.send(:update_parts, @parts)
|
||||
@page.parts.size.should == 3
|
||||
@page.parts[1].name.should == 'Very left column'
|
||||
@page.parts[1].slug.should == 'left_sidebar'
|
||||
end
|
||||
|
||||
it 'should disable parts' do
|
||||
@parts = [@parts.shift, @parts.pop]
|
||||
@page.send(:update_parts, @parts)
|
||||
@page.parts.size.should == 3
|
||||
@page.parts[1].name.should == 'Left column'
|
||||
@page.parts[1].disabled.should be_true
|
||||
end
|
||||
|
||||
it 'should enable parts previously disabled' do
|
||||
parts_at_first = @parts.clone
|
||||
@parts = [@parts.shift, @parts.pop]
|
||||
@page.send(:update_parts, @parts)
|
||||
@page.send(:update_parts, parts_at_first)
|
||||
@page.parts.size.should == 3
|
||||
@page.parts[1].name.should == 'Left column'
|
||||
@page.parts[1].disabled.should be_true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'acts as tree' do
|
||||
|
||||
before(:each) do
|
||||
@ -125,22 +233,21 @@ describe Page do
|
||||
|
||||
describe 'acts as list' do
|
||||
|
||||
before(:each) do
|
||||
@home = Factory(:page)
|
||||
@child_1 = Factory(:page, :title => 'Subpage 1', :slug => 'foo', :parent => @home, :site => @home.site)
|
||||
@child_2 = Factory(:page, :title => 'Subpage 2', :slug => 'bar', :parent => @home, :site => @home.site)
|
||||
@child_3 = Factory(:page, :title => 'Subpage 3', :slug => 'acme', :parent => @home, :site => @home.site)
|
||||
end
|
||||
before(:each) do
|
||||
@home = Factory(:page)
|
||||
@child_1 = Factory(:page, :title => 'Subpage 1', :slug => 'foo', :parent => @home, :site => @home.site)
|
||||
@child_2 = Factory(:page, :title => 'Subpage 2', :slug => 'bar', :parent => @home, :site => @home.site)
|
||||
@child_3 = Factory(:page, :title => 'Subpage 3', :slug => 'acme', :parent => @home, :site => @home.site)
|
||||
end
|
||||
|
||||
it 'should be at the bottom of the folder once created' do
|
||||
[@child_1, @child_2, @child_3].each_with_index { |c, i| c.position.should == i + 1 }
|
||||
end
|
||||
it 'should be at the bottom of the folder once created' do
|
||||
[@child_1, @child_2, @child_3].each_with_index { |c, i| c.position.should == i + 1 }
|
||||
end
|
||||
|
||||
it 'should have its position updated if a sibling is removed' do
|
||||
@child_2.destroy
|
||||
[@child_1, @child_3.reload].each_with_index { |c, i| c.position.should == i + 1 }
|
||||
end
|
||||
|
||||
it 'should have its position updated if a sibling is removed' do
|
||||
@child_2.destroy
|
||||
[@child_1, @child_3.reload].each_with_index { |c, i| c.position.should == i + 1 }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -9,7 +9,7 @@ require 'rspec/rails'
|
||||
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
||||
|
||||
Rspec.configure do |config|
|
||||
config.mock_with :rspec
|
||||
config.mock_with :mocha
|
||||
|
||||
config.before(:each) do
|
||||
Mongoid.master.collections.each(&:drop)
|
||||
|
Loading…
Reference in New Issue
Block a user