landable 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +56 -0
- data/Rakefile +22 -0
- data/app/controllers/concerns/landable/variables_concern.rb +38 -0
- data/app/controllers/landable/api/access_tokens_controller.rb +48 -0
- data/app/controllers/landable/api/assets_controller.rb +55 -0
- data/app/controllers/landable/api/categories_controller.rb +13 -0
- data/app/controllers/landable/api/directories_controller.rb +19 -0
- data/app/controllers/landable/api/page_revisions_controller.rb +38 -0
- data/app/controllers/landable/api/pages_controller.rb +96 -0
- data/app/controllers/landable/api/templates_controller.rb +33 -0
- data/app/controllers/landable/api/themes_controller.rb +57 -0
- data/app/controllers/landable/api_controller.rb +75 -0
- data/app/controllers/landable/application_controller.rb +4 -0
- data/app/controllers/landable/public/pages_controller.rb +25 -0
- data/app/controllers/landable/public/preview/page_revisions_controller.rb +16 -0
- data/app/controllers/landable/public/preview/pages_controller.rb +16 -0
- data/app/controllers/landable/public/sitemap_controller.rb +18 -0
- data/app/decorators/landable/null_page_decorator.rb +26 -0
- data/app/decorators/landable/page_decorator.rb +40 -0
- data/app/helpers/landable/application_helper.rb +13 -0
- data/app/helpers/landable/pages_helper.rb +20 -0
- data/app/models/concerns/landable/has_assets.rb +74 -0
- data/app/models/concerns/landable/table_name.rb +12 -0
- data/app/models/concerns/landable/traffic/table_name.rb +14 -0
- data/app/models/landable/access_token.rb +20 -0
- data/app/models/landable/asset.rb +63 -0
- data/app/models/landable/asset_search_engine.rb +20 -0
- data/app/models/landable/author.rb +12 -0
- data/app/models/landable/category.rb +27 -0
- data/app/models/landable/directory.rb +23 -0
- data/app/models/landable/page.rb +259 -0
- data/app/models/landable/page_revision.rb +74 -0
- data/app/models/landable/page_search_engine.rb +20 -0
- data/app/models/landable/search_engine.rb +39 -0
- data/app/models/landable/template.rb +24 -0
- data/app/models/landable/theme.rb +20 -0
- data/app/models/landable/traffic/access.rb +11 -0
- data/app/models/landable/traffic/ad_group.rb +11 -0
- data/app/models/landable/traffic/ad_type.rb +11 -0
- data/app/models/landable/traffic/attribution.rb +38 -0
- data/app/models/landable/traffic/bid_match_type.rb +11 -0
- data/app/models/landable/traffic/browser.rb +11 -0
- data/app/models/landable/traffic/campaign.rb +11 -0
- data/app/models/landable/traffic/city.rb +11 -0
- data/app/models/landable/traffic/content.rb +11 -0
- data/app/models/landable/traffic/cookie.rb +16 -0
- data/app/models/landable/traffic/country.rb +11 -0
- data/app/models/landable/traffic/creative.rb +11 -0
- data/app/models/landable/traffic/device.rb +11 -0
- data/app/models/landable/traffic/device_type.rb +11 -0
- data/app/models/landable/traffic/domain.rb +12 -0
- data/app/models/landable/traffic/event.rb +12 -0
- data/app/models/landable/traffic/event_type.rb +11 -0
- data/app/models/landable/traffic/experiment.rb +11 -0
- data/app/models/landable/traffic/http_method.rb +11 -0
- data/app/models/landable/traffic/ip_address.rb +11 -0
- data/app/models/landable/traffic/ip_lookup.rb +12 -0
- data/app/models/landable/traffic/keyword.rb +11 -0
- data/app/models/landable/traffic/location.rb +11 -0
- data/app/models/landable/traffic/match_type.rb +11 -0
- data/app/models/landable/traffic/medium.rb +11 -0
- data/app/models/landable/traffic/mime_type.rb +11 -0
- data/app/models/landable/traffic/network.rb +11 -0
- data/app/models/landable/traffic/owner.rb +10 -0
- data/app/models/landable/traffic/ownership.rb +10 -0
- data/app/models/landable/traffic/page_view.rb +24 -0
- data/app/models/landable/traffic/path.rb +13 -0
- data/app/models/landable/traffic/placement.rb +11 -0
- data/app/models/landable/traffic/platform.rb +11 -0
- data/app/models/landable/traffic/position.rb +11 -0
- data/app/models/landable/traffic/query_string.rb +12 -0
- data/app/models/landable/traffic/referer.rb +11 -0
- data/app/models/landable/traffic/region.rb +11 -0
- data/app/models/landable/traffic/search_term.rb +11 -0
- data/app/models/landable/traffic/source.rb +11 -0
- data/app/models/landable/traffic/target.rb +11 -0
- data/app/models/landable/traffic/user_agent.rb +28 -0
- data/app/models/landable/traffic/user_agent_type.rb +11 -0
- data/app/models/landable/traffic/visit.rb +15 -0
- data/app/models/landable/traffic/visitor.rb +13 -0
- data/app/responders/landable/api_responder.rb +76 -0
- data/app/responders/landable/page_render_responder.rb +15 -0
- data/app/serializers/landable/access_token_serializer.rb +6 -0
- data/app/serializers/landable/asset_serializer.rb +14 -0
- data/app/serializers/landable/author_serializer.rb +5 -0
- data/app/serializers/landable/category_serializer.rb +5 -0
- data/app/serializers/landable/directory_serializer.rb +8 -0
- data/app/serializers/landable/page_revision_serializer.rb +15 -0
- data/app/serializers/landable/page_serializer.rb +31 -0
- data/app/serializers/landable/template_serializer.rb +5 -0
- data/app/serializers/landable/theme_serializer.rb +7 -0
- data/app/services/landable/authentication_service.rb +44 -0
- data/app/services/landable/registration_service.rb +13 -0
- data/app/services/landable/render_service.rb +86 -0
- data/app/services/landable/tidy_service.rb +155 -0
- data/app/uploaders/landable/asset_uploader.rb +9 -0
- data/app/validators/path_validator.rb +12 -0
- data/app/validators/url_validator.rb +13 -0
- data/app/views/templates/preview.liquid +122 -0
- data/bin/rails +8 -0
- data/bin/redb +7 -0
- data/config.ru +7 -0
- data/config/cucumber.yml +5 -0
- data/config/routes.rb +62 -0
- data/db/migrate/20130510221424_create_landable_schema.rb +338 -0
- data/db/migrate/20130909182713_landable_pages__add_updated_by.rb +11 -0
- data/db/migrate/20130909182715_landable_page_revisions__break_out_snapshot.rb +72 -0
- data/db/migrate/20130909191153_landable_pages__add_lock_version.rb +5 -0
- data/db/migrate/20131002220041_file_based_themes.rb +12 -0
- data/db/migrate/20131008164204_create_head_tag_on_page.rb +19 -0
- data/db/migrate/20131008193544_drop_status_codes_model.rb +44 -0
- data/db/migrate/20131028145652_add_traffic_schema.rb +276 -0
- data/db/migrate/20131101213623_add_dnt_column_to_visits.rb +7 -0
- data/db/migrate/20131104224120_add_meta_on_events.rb +7 -0
- data/db/migrate/20131106185946_add_index_on_page_revisions_path.rb +6 -0
- data/db/migrate/20131106193021_page_revisisons__path_status_code_index.rb +9 -0
- data/db/migrate/20131108212501_traffic_owner_ids_are_serials.rb +20 -0
- data/db/migrate/20131121150902_add_attribution_id_to_unique_index.rb +10 -0
- data/db/migrate/20131216214027_drop_browser_screenshot_tables.rb +6 -0
- data/db/migrate/20140128170659_file_backed_templates.rb +8 -0
- data/db/migrate/20140205193757_fix_status_codes.rb +24 -0
- data/db/migrate/20140206211322_add_response_time_to_traffic_page_views.rb +7 -0
- data/db/migrate/20140220170324_add_slug_to_categories.rb +14 -0
- data/db/migrate/20140220174630_add_abstract_and_hero_asset_to_pages_and_page_revisions.rb +8 -0
- data/db/migrate/20140224205516_rename_traffic_schema.rb +40 -0
- data/db/pgtap/pgtap.sql +9034 -0
- data/db/test/landable.access_tokens.sql +13 -0
- data/db/test/landable.assets.sql +16 -0
- data/db/test/landable.authors.sql +22 -0
- data/db/test/landable.categories.sql +9 -0
- data/db/test/landable.page_revisions.sql +41 -0
- data/db/test/landable.pages.sql +19 -0
- data/db/test/landable.templates.sql +15 -0
- data/db/test/landable.themes.sql +25 -0
- data/doc/schema/access_token.json +22 -0
- data/doc/schema/asset.json +65 -0
- data/doc/schema/author.json +30 -0
- data/doc/schema/directory.json +24 -0
- data/doc/schema/page.json +95 -0
- data/doc/schema/page_revision.json +70 -0
- data/doc/schema/theme.json +37 -0
- data/doc/schema/uuid.json +6 -0
- data/features/api/access_tokens.feature +84 -0
- data/features/api/assets.feature +46 -0
- data/features/api/cors.feature +25 -0
- data/features/api/pages.feature +42 -0
- data/features/api/preview.feature +16 -0
- data/features/api/templates.feature +33 -0
- data/features/api/themes.feature +33 -0
- data/features/liquid/body.feature +35 -0
- data/features/liquid/drops/categories.feature +54 -0
- data/features/liquid/tags.feature +168 -0
- data/features/public/content_types.feature +17 -0
- data/features/public/publishing.feature +45 -0
- data/features/public/status_codes.feature +25 -0
- data/features/public/views.feature +17 -0
- data/features/step_definitions/asset_steps.rb +60 -0
- data/features/step_definitions/core_api_steps.rb +139 -0
- data/features/step_definitions/debug_steps.rb +3 -0
- data/features/step_definitions/factory_steps.rb +124 -0
- data/features/step_definitions/html_steps.rb +9 -0
- data/features/step_definitions/liquid_steps.rb +79 -0
- data/features/step_definitions/revision_steps.rb +5 -0
- data/features/step_definitions/theme_steps.rb +43 -0
- data/features/support/env.rb +66 -0
- data/features/support/usefulness.rb +13 -0
- data/landable.gemspec +54 -0
- data/lib/generators/landable/collection/collection_generator.rb +0 -0
- data/lib/generators/landable/collection/templates/stylesheets/landable/%file_name%.less +0 -0
- data/lib/generators/landable/collection/templates/stylesheets/landable/%file_name%/mixins.less +0 -0
- data/lib/generators/landable/collection/templates/stylesheets/landable/%file_name%/variables.less +0 -0
- data/lib/generators/landable/component/component_generator.rb +0 -0
- data/lib/generators/landable/component/templates/javascripts/landable/%file_name%.less +0 -0
- data/lib/generators/landable/component/templates/stylesheets/landable/%file_name%.less +0 -0
- data/lib/generators/landable/install_generator.rb +19 -0
- data/lib/generators/landable/landable_generator.rb +22 -0
- data/lib/generators/templates/landable.rb +34 -0
- data/lib/landable.rb +30 -0
- data/lib/landable/configuration.rb +115 -0
- data/lib/landable/core_ext/ipaddr.rb +18 -0
- data/lib/landable/engine.rb +69 -0
- data/lib/landable/error.rb +16 -0
- data/lib/landable/inflections.rb +4 -0
- data/lib/landable/layout.rb +60 -0
- data/lib/landable/liquid.rb +27 -0
- data/lib/landable/liquid/asset_tags.rb +76 -0
- data/lib/landable/liquid/drops.rb +46 -0
- data/lib/landable/liquid/filters.rb +11 -0
- data/lib/landable/liquid/tags.rb +91 -0
- data/lib/landable/migration.rb +40 -0
- data/lib/landable/mime_types.rb +15 -0
- data/lib/landable/partial.rb +46 -0
- data/lib/landable/seeds.rb +36 -0
- data/lib/landable/traffic.rb +34 -0
- data/lib/landable/traffic/crawl_tracker.rb +9 -0
- data/lib/landable/traffic/noop_tracker.rb +8 -0
- data/lib/landable/traffic/ping_tracker.rb +9 -0
- data/lib/landable/traffic/scan_tracker.rb +9 -0
- data/lib/landable/traffic/scrape_tracker.rb +9 -0
- data/lib/landable/traffic/tracker.rb +283 -0
- data/lib/landable/traffic/user_tracker.rb +65 -0
- data/lib/landable/version.rb +10 -0
- data/lib/tasks/landable/cucumber.rake +67 -0
- data/lib/tasks/landable/data.rake +166 -0
- data/lib/tasks/landable/pgtap.rake +26 -0
- data/lib/tasks/landable/rdoc.rake +11 -0
- data/lib/tasks/landable/seed.rake +16 -0
- data/lib/tasks/landable/spec.rake +15 -0
- data/script/cucumber +10 -0
- data/spec/concerns/landable/has_assets_spec.rb +75 -0
- data/spec/concerns/landable/table_name_spec.rb +15 -0
- data/spec/concerns/landable/traffic/table_name_spec.rb +16 -0
- data/spec/controllers/concerns/landable/variables_concern_spec.rb +66 -0
- data/spec/controllers/landable/api/assets_controller_spec.rb +24 -0
- data/spec/controllers/landable/api/categories_controller_spec.rb +45 -0
- data/spec/controllers/landable/api/directories_controller_spec.rb +56 -0
- data/spec/controllers/landable/api/page_revisions_controller_spec.rb +29 -0
- data/spec/controllers/landable/api/pages_controller_spec.rb +271 -0
- data/spec/controllers/landable/api_controller_spec.rb +189 -0
- data/spec/controllers/public/preview/page_revisions_controller_spec.rb +41 -0
- data/spec/controllers/public/preview/pages_controller_spec.rb +36 -0
- data/spec/controllers/public/sitemap_controller_spec.rb +25 -0
- data/spec/decorators/page_decorator_spec.rb +90 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/foo.jpg +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +10 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/controllers/priority_controller.rb +7 -0
- data/spec/dummy/app/helpers/application_helper.rb +5 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/layouts/priority.html.erb +18 -0
- data/spec/dummy/app/views/partials/_foobazz.html +1 -0
- data/spec/dummy/app/views/priority/show.html.erb +11 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +21 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +60 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/landable.rb +22 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/db/structure.sql +3736 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/asset.rb +29 -0
- data/spec/factories/authors.rb +12 -0
- data/spec/factories/category.rb +6 -0
- data/spec/factories/page_revision.rb +6 -0
- data/spec/factories/pages.rb +33 -0
- data/spec/factories/template.rb +13 -0
- data/spec/factories/theme.rb +14 -0
- data/spec/fixtures/assets/cthulhu.jpg +0 -0
- data/spec/fixtures/assets/panda.png +0 -0
- data/spec/fixtures/assets/sloth.png +0 -0
- data/spec/fixtures/assets/small.pdf +0 -0
- data/spec/helpers/pages_helper_spec.rb +35 -0
- data/spec/lib/landable/configuration_spec.rb +20 -0
- data/spec/lib/landable/layout_spec.rb +25 -0
- data/spec/lib/landable/liquid_spec.rb +24 -0
- data/spec/lib/landable/migration_spec.rb +51 -0
- data/spec/lib/landable/partial_spec.rb +84 -0
- data/spec/lib/landable/tracking_spec.rb +62 -0
- data/spec/lib/landable/traffic_spec.rb +45 -0
- data/spec/models/landable/access_token_spec.rb +13 -0
- data/spec/models/landable/asset_spec.rb +48 -0
- data/spec/models/landable/directory_spec.rb +36 -0
- data/spec/models/landable/page/errors_spec.rb +30 -0
- data/spec/models/landable/page_revision_spec.rb +75 -0
- data/spec/models/landable/page_spec.rb +377 -0
- data/spec/models/landable/template_spec.rb +47 -0
- data/spec/models/landable/theme_spec.rb +8 -0
- data/spec/responders/page_render_responder_spec.rb +43 -0
- data/spec/routing/public_page_route_spec.rb +36 -0
- data/spec/services/landable/authentication_service_spec.rb +61 -0
- data/spec/services/landable/render_service_spec.rb +103 -0
- data/spec/services/landable/tidy_service_spec.rb +157 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/behaviors.rb +107 -0
- data/spec/support/carrier_wave.rb +17 -0
- data/spec/support/categories.yml +2 -0
- data/spec/support/helpers.rb +22 -0
- metadata +795 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Feature: Content types by extension
|
|
2
|
+
As a content author
|
|
3
|
+
I want Landable to deliver the right content type for my content
|
|
4
|
+
|
|
5
|
+
Scenario Outline:
|
|
6
|
+
Given a published page "/foo.<ext>"
|
|
7
|
+
When I GET "/foo.<ext>"
|
|
8
|
+
Then the response header "Content-Type" should be "<content_type>"
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
| ext | content_type |
|
|
12
|
+
| xml | application/xml |
|
|
13
|
+
| json | application/json |
|
|
14
|
+
| txt | text/plain |
|
|
15
|
+
| html | text/html |
|
|
16
|
+
| htm | text/html |
|
|
17
|
+
| foo | text/plain |
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Feature: Only published revisions are publicly available
|
|
2
|
+
|
|
3
|
+
@allow-rescue
|
|
4
|
+
Scenario: Never-published page
|
|
5
|
+
Given a page "/unpub"
|
|
6
|
+
When I GET "/unpub"
|
|
7
|
+
Then the response status should be 404
|
|
8
|
+
When I publish the page "/unpub"
|
|
9
|
+
And I GET "/unpub"
|
|
10
|
+
Then the response status should be 200
|
|
11
|
+
|
|
12
|
+
Scenario: Currently published revision
|
|
13
|
+
Given a published page "/pubbed"
|
|
14
|
+
When I GET "/pubbed"
|
|
15
|
+
Then the response status should be 200
|
|
16
|
+
And the response body should include the body of page "/pubbed"
|
|
17
|
+
|
|
18
|
+
Scenario: Unpublished change from one theme to another
|
|
19
|
+
Given a published page "/pubbed" with a theme containing "foo"
|
|
20
|
+
When I choose another theme containing "bar"
|
|
21
|
+
And I GET "/pubbed"
|
|
22
|
+
Then I should see "foo"
|
|
23
|
+
When I publish the page with another theme
|
|
24
|
+
And I GET "/pubbed"
|
|
25
|
+
Then I should see "bar"
|
|
26
|
+
|
|
27
|
+
Scenario: Changes to the theme itself do not need to be published (for now?)
|
|
28
|
+
Given a published page "/pubbed" with a theme containing "foo"
|
|
29
|
+
When I change the theme to contain "bar"
|
|
30
|
+
And I GET "/pubbed"
|
|
31
|
+
Then I should see "bar"
|
|
32
|
+
|
|
33
|
+
@allow-rescue
|
|
34
|
+
Scenario: Unpublished status change
|
|
35
|
+
Given a published page "/pubbed"
|
|
36
|
+
When I change the page to a 410
|
|
37
|
+
And I GET "/pubbed"
|
|
38
|
+
Then the response status should be 200
|
|
39
|
+
When I publish the page
|
|
40
|
+
And I GET "/pubbed"
|
|
41
|
+
Then the response status should be 410
|
|
42
|
+
When I revert to the previous revision
|
|
43
|
+
And I publish the page
|
|
44
|
+
And I GET "/pubbed"
|
|
45
|
+
Then the response status should be 200
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Feature: Page status codes
|
|
2
|
+
|
|
3
|
+
@allow-rescue
|
|
4
|
+
Scenario Outline: HTTP response status
|
|
5
|
+
Given a published page "/foo" with status <code>
|
|
6
|
+
When I GET "/foo"
|
|
7
|
+
Then the response status should be <code>
|
|
8
|
+
|
|
9
|
+
Examples:
|
|
10
|
+
| code |
|
|
11
|
+
| 200 |
|
|
12
|
+
| 301 |
|
|
13
|
+
| 302 |
|
|
14
|
+
| 410 |
|
|
15
|
+
|
|
16
|
+
Scenario Outline: Redirects
|
|
17
|
+
Given page "/foo" redirects to "http://google.com" with status <code>
|
|
18
|
+
When I GET "/foo"
|
|
19
|
+
Then the response status should be <code>
|
|
20
|
+
And I should have been redirected to "http://google.com"
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
| code |
|
|
24
|
+
| 301 |
|
|
25
|
+
| 302 |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Feature: Helpers are provided for Rails views that Landable didn't serve
|
|
2
|
+
The dummy app defines a few routes before mounting
|
|
3
|
+
Landable; we want to ensure those routes still function
|
|
4
|
+
from both the application and Landable's perspective.
|
|
5
|
+
|
|
6
|
+
Scenario: Application view references landable helpers
|
|
7
|
+
Given a page "/priority" with title "Landable4Life"
|
|
8
|
+
And the robots meta tag of "/priority" is "noindex,nofollow"
|
|
9
|
+
And the body of page "/priority" is:
|
|
10
|
+
"""
|
|
11
|
+
<h1>Inline HTML!</h1>
|
|
12
|
+
"""
|
|
13
|
+
When I GET "/priority"
|
|
14
|
+
Then the response status should be 200
|
|
15
|
+
And the element "title" should have inner text "Home: Landable4Life"
|
|
16
|
+
And the element "#path" should have inner text "/priority"
|
|
17
|
+
And the element "meta[name='robots'][content='noindex,nofollow']" should exist
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# a bit specific eh ...
|
|
2
|
+
Given '2 assets named "panda" and "disclaimer"' do
|
|
3
|
+
@assets = [create(:asset, name: 'panda', fixture: 'panda.png'),
|
|
4
|
+
create(:asset, name: 'disclaimer', fixture: 'small.pdf')]
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
When 'I POST an asset to "$path"' do |path|
|
|
8
|
+
post path, asset: attributes_for(:asset, fixture: 'panda.png')
|
|
9
|
+
last_request.should be_form_data
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
When 'I POST that asset to "$path" again' do |path|
|
|
13
|
+
post path, asset: attributes_for(:asset, fixture: 'panda.png')
|
|
14
|
+
last_request.should be_form_data
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
When 'I POST an asset to "$path" with both $assoc IDs' do |path, assoc|
|
|
18
|
+
ary = instance_variable_get("@#{assoc}s")
|
|
19
|
+
key = :"#{assoc}_ids"
|
|
20
|
+
|
|
21
|
+
post path, asset: attributes_for(:asset).merge({
|
|
22
|
+
key => ary.map(&:id)
|
|
23
|
+
})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Then 'the response should contain an "asset"' do
|
|
27
|
+
last_json.should have_key('asset')
|
|
28
|
+
last_json['asset']['mime_type'].should == 'image/png'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Then 'the response should contain the $version "asset"' do |version|
|
|
32
|
+
# version is intentionally ignored; it just reads better in the steps.
|
|
33
|
+
|
|
34
|
+
@asset ||= Landable::Asset.order('created_at DESC').first
|
|
35
|
+
at_json('asset/id').should == @asset.id
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Then /^the asset ID should( not)? be in the array at "([^"]+)"$/ do |negative, json_path|
|
|
39
|
+
array = at_json json_path
|
|
40
|
+
if negative
|
|
41
|
+
array.should_not include(@asset.id)
|
|
42
|
+
else
|
|
43
|
+
array.should include(@asset.id)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Then 'both $assoc IDs should be in the array at "$json_path"' do |assoc, json_path|
|
|
48
|
+
ids = instance_variable_get("@#{assoc}s").map &:id
|
|
49
|
+
at_json(json_path).should include(*ids)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
Then 'the rendered body should be the correct assets' do
|
|
53
|
+
<<-eos
|
|
54
|
+
<link href="/assets/application-/#{/./}.css" media="screen" rel="stylesheet" />
|
|
55
|
+
<script src="/assets/application-#{/./}.js"></script>
|
|
56
|
+
<img alt="Foo" src="/assets/foo-#{/./}.jpg" />
|
|
57
|
+
<img alt="Foo" src="/assets/foo-#{/./}.jpg" />
|
|
58
|
+
<img alt="Baz!" src="https://landable.dev/_assets//uploads/panda.png" />
|
|
59
|
+
eos
|
|
60
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Lame file name, but I don't know what else to call it atm.
|
|
2
|
+
|
|
3
|
+
expand_mustache = lambda do |context, str|
|
|
4
|
+
if str.respond_to?(:gsub)
|
|
5
|
+
str.gsub(/\{\{([^ ].+?)\}\}/) { context.eval($1) }
|
|
6
|
+
else
|
|
7
|
+
str
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Landable::FeatureHelper
|
|
12
|
+
def basic_authorize!(author = current_author, token = current_access_token)
|
|
13
|
+
basic_authorize author.username, token.id
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def current_author
|
|
17
|
+
@current_author ||= create :author
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def current_access_token
|
|
21
|
+
@current_access_token ||= create :access_token, author: @current_author
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def last_json(body = last_response.body)
|
|
25
|
+
JSON.parse body
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def latest(model)
|
|
29
|
+
model = model.to_s.classify
|
|
30
|
+
klass = "Landable::#{model}".constantize
|
|
31
|
+
klass.order('created_at DESC').first!
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
World(Landable::FeatureHelper)
|
|
36
|
+
|
|
37
|
+
Before '@api' do
|
|
38
|
+
header 'Accept', 'application/json'
|
|
39
|
+
header 'Content-Type', 'application/json'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Before '@api', '~@no-api-auth' do
|
|
43
|
+
basic_authorize!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Given 'I accept HTML' do
|
|
47
|
+
header 'Accept', 'text/html'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Given 'I accept JSON' do
|
|
51
|
+
header 'Accept', 'application/json'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Given 'my API requests include a valid access token' do
|
|
55
|
+
basic_authorize!
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
Given 'I repeat the request with a valid access token' do
|
|
59
|
+
basic_authorize!
|
|
60
|
+
request last_request.url, last_request.env
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
Given 'my access token will expire in 2 minutes' do
|
|
64
|
+
current_access_token.update_attributes!(expires_at: 2.minutes.from_now)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
Given 'my access token expired 2 minutes ago' do
|
|
68
|
+
current_access_token.update_attributes!(expires_at: 2.minutes.ago)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
When /^I (HEAD|GET|POST|PUT|PATCH|DELETE|OPTIONS)(?: to)? "(.+?)"$/ do |http_method, path|
|
|
72
|
+
request expand_mustache[binding, path], method: http_method
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
When /^I (POST|PUT|PATCH|DELETE|OPTIONS)(?: to)? "(.+?)"(?: with)?:$/ do |http_method, path, body|
|
|
76
|
+
request expand_mustache[binding, path], method: http_method, params: body
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
When 'I request CORS from "$path" with:' do |path, table|
|
|
80
|
+
options = table.hashes.first
|
|
81
|
+
|
|
82
|
+
header 'Origin', options['origin']
|
|
83
|
+
header 'Access-Control-Request-Method', options['method']
|
|
84
|
+
request path, method: 'OPTIONS'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
When 'I follow the "Location" header' do
|
|
88
|
+
get last_response.headers['Location']
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
Then 'my access token should not expire for at least 2 hours' do
|
|
92
|
+
token = current_access_token.reload
|
|
93
|
+
token.expires_at.should be >= 2.hours.from_now
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
Then /^the response(?: status)? should(?: (not))? be (\d{3})(?: "[A-Za-z ]+")?$/ do |negate, code|
|
|
97
|
+
code = Integer(code)
|
|
98
|
+
if negate == 'not'
|
|
99
|
+
last_response.status.should_not eq(code)
|
|
100
|
+
else
|
|
101
|
+
last_response.status.should eq(code)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Then /^the response should contain (?:a|an) "([^"]+)"$/ do |model|
|
|
106
|
+
last_json.should have_key(model)
|
|
107
|
+
last_json[model].should have_key('id')
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
Then /^the response should contain (\d+) "([^"]+)"$/ do |count, kind|
|
|
111
|
+
last_json[kind].length.should == Integer(count)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
Then 'the response body should be empty' do
|
|
115
|
+
last_response.body.should be_blank
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
Then 'the response body should be "$body"' do |body|
|
|
119
|
+
last_response.body.should == body
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
Then 'I should have been redirected to "$url"' do |url|
|
|
123
|
+
last_response.headers['Location'].should == url
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
Then 'the response headers should include:' do |table|
|
|
127
|
+
expected = table.hashes.reduce({}) do |acc, row|
|
|
128
|
+
acc.merge row['header'] => row['value']
|
|
129
|
+
end
|
|
130
|
+
last_response.headers.should include(expected)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
Then 'the JSON at "$path" should be "$value"' do |json_path, value|
|
|
134
|
+
at_json(json_path).should == value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
Then 'the response header "$header" should be "$content_type"' do |header, content_type|
|
|
138
|
+
last_response.headers[header].should include(content_type)
|
|
139
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Given /^(\d+) ([\w\s]+)$/ do |count, kind|
|
|
2
|
+
klass = kind.gsub(/\s+/, '_').singularize.classify.gsub(/^/, 'Landable::').constantize
|
|
3
|
+
klass.destroy_all
|
|
4
|
+
|
|
5
|
+
result = Integer(count).times.map do
|
|
6
|
+
create kind.gsub(/\s+/, '_').singularize.to_sym
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
instance_variable_set :"@#{kind}", result
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Given 'there are no authors in the database' do
|
|
13
|
+
Landable::Author.delete_all
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Given /^an author "([^"]+)"$/ do |username|
|
|
17
|
+
create :author, username: username
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Given '"$username" has an unexpired access token' do |username|
|
|
21
|
+
create :access_token, author: Landable::Author.where(username: username).first!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Given 'I also have an older, expired access token' do
|
|
25
|
+
@expired_access_token = create :access_token, author: @current_author, expires_at: 2.minutes.ago
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Given "there is another author's access token in the database" do
|
|
29
|
+
@foreign_access_token = create :access_token, author: create(:author)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Given /^an? (page|theme|asset|template)$/ do |model|
|
|
33
|
+
instance_variable_set :"@#{model}", create(model.to_sym)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Given /^a (page|theme) with an asset attached$/ do |model|
|
|
37
|
+
record = create model.to_sym
|
|
38
|
+
@asset = create :asset
|
|
39
|
+
record.assets.push @asset
|
|
40
|
+
instance_variable_set :"@#{model}", record
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Given /^a page "([^"]+)"$/ do |path|
|
|
44
|
+
create :page, path: path, body: "<HTML>BODY</HTML>"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Given /^a page "([^"]+)" with title "(.+)"$/ do |path, title|
|
|
48
|
+
create :page, path: path, title: title, body: "<HTML>BODY</HTML>"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Given /^a "(\w+)" page with title "(.+)" and category "(.+)"$/ do |published, title, category|
|
|
52
|
+
category_obj = Landable::Category.where('lower(name) = ?', category.downcase).first
|
|
53
|
+
@page = create :page, title: title, category: category_obj
|
|
54
|
+
@page.publish! author: create(:author) if published == 'published'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Given 'page "$path" redirects to "$url" with status $code' do |path, url, code|
|
|
58
|
+
page = create :page, :redirect, path: path, redirect_url: url, status_code: code, body: "BODY"
|
|
59
|
+
page.publish! author: create(:author)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Given 'a published page "$path"' do |path|
|
|
63
|
+
@page = create :page, path: path
|
|
64
|
+
@page.publish! author: create(:author)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
Given 'a published page "$path" with status $code' do |path, code|
|
|
68
|
+
code = Integer(code)
|
|
69
|
+
page = case code
|
|
70
|
+
when 301, 302 then create :page, :redirect, path: path, status_code: code, body: "BODY"
|
|
71
|
+
when 410 then create :page, :gone, path: path, body: "BODY"
|
|
72
|
+
else create :page, path: path, body: "BODY"
|
|
73
|
+
end
|
|
74
|
+
page.publish! author: create(:author)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
Given 'a published page "$path" titled "$title" with theme "$name"' do |path, title, theme|
|
|
78
|
+
page = create :page, path: path, title: title, theme: create(:theme, name: theme)
|
|
79
|
+
page.publish! author: create(:author)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
Given 'the body of theme "$name" is "$body"' do |name, body|
|
|
83
|
+
theme = Landable::Theme.where(name: name).first!
|
|
84
|
+
theme.update_attributes! body: body
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
Given 'the $tag meta tag of "$path" is "$value"' do |tag, path, value|
|
|
88
|
+
page = Landable::Page.by_path!(path)
|
|
89
|
+
page.meta_tags ||= {}
|
|
90
|
+
page.meta_tags.merge! tag => value
|
|
91
|
+
page.save!
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
Given 'the body of page "$path" is:' do |path, body|
|
|
95
|
+
Landable::Page.by_path!(path).update_attributes(body: body)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
When 'I publish the page "$path"' do |path|
|
|
99
|
+
Landable::Page.by_path!(path).publish! author: create(:author)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
When(/^I change the page to a (\d+)$/) do |code|
|
|
103
|
+
@page.status_code = code
|
|
104
|
+
@page.save!
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
Then /^there should be (\d+) ([\w\s]+) in the database$/ do |count, kind|
|
|
108
|
+
name = kind.gsub(/\s+/, '_').classify
|
|
109
|
+
klass = "Landable::#{name}".constantize
|
|
110
|
+
klass.count.should eql(Integer(count))
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
Then 'an author "$username" should exist' do |username|
|
|
114
|
+
Landable::Author.where(username: username).first!
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
Then /^the author "(.+?)" should have (\d+) access tokens?$/ do |username, n|
|
|
118
|
+
author = Landable::Author.where(username: username).first!
|
|
119
|
+
author.access_tokens.count.should == Integer(n)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
Then 'the response body should include the body of page "$path"' do |path|
|
|
123
|
+
last_response.body.should include(Landable::Page.by_path!(path).body)
|
|
124
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Then 'the element "$css" should exist' do |css|
|
|
2
|
+
doc = Nokogiri::HTML(last_response.body)
|
|
3
|
+
doc.at(css).should be_present
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
Then 'the element "$css" should have inner text "$text"' do |css, text|
|
|
7
|
+
doc = Nokogiri::HTML(last_response.body)
|
|
8
|
+
doc.at(css).text.should == text
|
|
9
|
+
end
|