Repository: tabbyz/chibineko Branch: master Commit: 334d18690a05 Files: 206 Total size: 229.6 KB Directory structure: gitextract_t1ykuj_k/ ├── .gitignore ├── .rspec ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── app/ │ ├── assets/ │ │ ├── images/ │ │ │ └── .keep │ │ ├── javascripts/ │ │ │ ├── application.coffee │ │ │ ├── dashboards.coffee │ │ │ ├── data_table.coffee │ │ │ ├── move_tests.coffee │ │ │ ├── projects.coffee │ │ │ ├── static_pages.coffee │ │ │ ├── team_users.coffee │ │ │ ├── teams.coffee │ │ │ ├── test_preview.coffee │ │ │ ├── tests.coffee │ │ │ └── validation.coffee │ │ └── stylesheets/ │ │ ├── application.scss │ │ ├── bootstrap_overrides.scss │ │ ├── color.scss │ │ ├── common.scss │ │ ├── data_table.scss │ │ ├── layouts/ │ │ │ ├── navbar.scss │ │ │ ├── sidebar.scss │ │ │ └── style.scss │ │ ├── static_pages/ │ │ │ ├── terms.scss │ │ │ └── top.scss │ │ ├── teams/ │ │ │ └── team_users.scss │ │ ├── tests/ │ │ │ ├── edit_result_label.scss │ │ │ ├── form.scss │ │ │ ├── move.scss │ │ │ ├── show.scss │ │ │ └── testcase.scss │ │ ├── toastr_overrides.scss │ │ └── vendor/ │ │ ├── best_in_place.scss │ │ ├── bootgrid.scss │ │ └── ladda.scss │ ├── controllers/ │ │ ├── application_controller.rb │ │ ├── concerns/ │ │ │ └── .keep │ │ ├── dashboard_controller.rb │ │ ├── projects_controller.rb │ │ ├── static_pages_controller.rb │ │ ├── team_users_controller.rb │ │ ├── teams_controller.rb │ │ ├── testcases_controller.rb │ │ ├── tests_controller.rb │ │ ├── user_settings_controller.rb │ │ └── users/ │ │ └── registrations_controller.rb │ ├── helpers/ │ │ ├── application_helper.rb │ │ ├── dashboard_helper.rb │ │ ├── projects_helper.rb │ │ ├── static_pages_helper.rb │ │ ├── team_users_helper.rb │ │ ├── teams_helper.rb │ │ └── tests_helper.rb │ ├── mailers/ │ │ └── .keep │ ├── models/ │ │ ├── .keep │ │ ├── concerns/ │ │ │ └── .keep │ │ ├── project.rb │ │ ├── team.rb │ │ ├── team_user.rb │ │ ├── test.rb │ │ ├── testcase.rb │ │ └── user.rb │ └── views/ │ ├── dashboard/ │ │ ├── _navbar.html.slim │ │ └── index.html.slim │ ├── devise/ │ │ ├── confirmations/ │ │ │ └── new.html.slim │ │ ├── mailer/ │ │ │ ├── confirmation_instructions.en.html.slim │ │ │ ├── confirmation_instructions.ja.html.slim │ │ │ ├── password_change.html.erb │ │ │ ├── reset_password_instructions.en.html.slim │ │ │ └── reset_password_instructions.ja.html.slim │ │ ├── passwords/ │ │ │ └── new.html.slim │ │ ├── registrations/ │ │ │ └── new.html.slim │ │ ├── sessions/ │ │ │ └── new.html.slim │ │ └── shared/ │ │ └── _links.html.erb │ ├── layouts/ │ │ ├── _dashboard_sidebar.html.slim │ │ ├── _logo.html.slim │ │ ├── _main_menu.html.slim │ │ ├── _navbar.html.slim │ │ ├── _root_sidebar.html.slim │ │ ├── _sign_in_link.html.slim │ │ ├── _sign_out_link.html.slim │ │ ├── _sign_up_link.html.slim │ │ ├── _team_sidebar.html.slim │ │ └── application.html.slim │ ├── projects/ │ │ ├── _navbar.html.slim │ │ ├── _new.html.slim │ │ ├── _save.js.erb │ │ ├── _settings.html.slim │ │ ├── create.js.erb │ │ ├── new.js.erb │ │ ├── settings.js.erb │ │ ├── show.html.slim │ │ └── update.js.erb │ ├── static_pages/ │ │ ├── terms.html.slim │ │ └── top.html.slim │ ├── team_users/ │ │ ├── _index.html.slim │ │ ├── _save.js.erb │ │ ├── create.js.erb │ │ ├── destroy.js.erb │ │ └── index.js.erb │ ├── teams/ │ │ ├── _new.html.slim │ │ ├── _save.js.erb │ │ ├── _settings.html.slim │ │ ├── create.js.erb │ │ ├── new.js.erb │ │ ├── settings.js.erb │ │ ├── show.html.slim │ │ └── update.js.erb │ ├── tests/ │ │ ├── _alert.html.slim │ │ ├── _cheatsheet.en.html.slim │ │ ├── _cheatsheet.ja.html.slim │ │ ├── _description.html.slim │ │ ├── _form.html.slim │ │ ├── _move.html.slim │ │ ├── _navbar.html.slim │ │ ├── _result_label.html.slim │ │ ├── _table_toolbar.html.slim │ │ ├── _testcase.html.slim │ │ ├── bulk_destroy.js.erb │ │ ├── bulk_move.js.erb │ │ ├── create.js.erb │ │ ├── description.js.erb │ │ ├── edit.html.slim │ │ ├── move.js.erb │ │ ├── new.html.slim │ │ ├── result_label.js.erb │ │ ├── show.csv.ruby │ │ ├── show.html.slim │ │ ├── update.js.erb │ │ └── update_result_label.js.erb │ └── user_settings/ │ ├── _modal.html.slim │ ├── edit.js.erb │ └── update.js.erb ├── bin/ │ ├── bundle │ ├── rails │ ├── rake │ └── setup ├── circle.yml ├── config/ │ ├── application.rb │ ├── boot.rb │ ├── database.yml │ ├── environment.rb │ ├── environments/ │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── i18n-js.yml │ ├── initializers/ │ │ ├── action_mailer.rb │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── cookies_serializer.rb │ │ ├── devise.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── session_store.rb │ │ ├── time_formats.rb │ │ └── wrap_parameters.rb │ ├── locales/ │ │ ├── devise.en.yml │ │ ├── devise.ja.yml │ │ ├── devise_view.en.yml │ │ ├── devise_view.ja.yml │ │ ├── en.yml │ │ ├── ja.yml │ │ ├── js.en.yml │ │ ├── js.ja.yml │ │ ├── terms.en.yml │ │ └── terms.ja.yml │ ├── mailer.yml.example │ ├── routes.rb │ └── secrets.yml ├── config.ru ├── db/ │ ├── migrate/ │ │ ├── 20151224022612_devise_create_users.rb │ │ ├── 20151224030646_create_teams.rb │ │ ├── 20151225045046_create_projects.rb │ │ ├── 20151225141534_create_tests.rb │ │ ├── 20151226051709_create_testcases.rb │ │ ├── 20160103121144_add_column_to_user.rb │ │ ├── 20160109162956_create_team_users.rb │ │ └── 20160319072222_add_created_at_index_to_test.rb │ ├── schema.rb │ └── seeds.rb ├── lib/ │ ├── assets/ │ │ └── .keep │ └── tasks/ │ └── .keep ├── log/ │ └── .keep ├── public/ │ ├── 404.en.html │ ├── 404.ja.html │ ├── 422.en.html │ ├── 422.ja.html │ ├── 500.en.html │ ├── 500.ja.html │ ├── error.css │ ├── javascripts/ │ │ └── translations.js │ ├── maintenance.html │ └── robots.txt ├── spec/ │ ├── factories/ │ │ ├── testcases.rb │ │ └── tests.rb │ ├── models/ │ │ ├── test_spec.rb │ │ └── testcase_spec.rb │ ├── rails_helper.rb │ └── spec_helper.rb └── vendor/ └── assets/ ├── javascripts/ │ ├── .keep │ └── jquery.readyselector.js └── stylesheets/ └── .keep ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Ignore bundler config. /.bundle /vendor/bundle # Ignore mailer config. /config/mailer.yml # Ignore the default SQLite database. /db/*.sqlite3 /db/*.sqlite3-journal # Ignore all logfiles and tempfiles. /log/*.log /tmp # Ignore other unneeded files. doc/ *.swp *~ .project .DS_Store .idea .secret ================================================ FILE: .rspec ================================================ --color --require spec_helper ================================================ FILE: Gemfile ================================================ source 'https://rubygems.org' ruby '2.3.0' gem 'rails', '4.2.4' gem 'slim-rails', '~> 3.0.0' gem 'sass-rails', '~> 5.0' # JavaScript gem 'i18n-js', '~> 2.1.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.1', '>= 4.1.1' gem 'therubyracer', platforms: :ruby gem 'jquery-rails' gem 'jbuilder', '~> 2.4', '>= 2.4.1' gem 'gon', '~> 6.0', '>= 6.0.1' # Other gem 'bootstrap-sass', '~> 3.3', '>= 3.3.6' gem 'font-awesome-rails', '~> 4.5', '>= 4.5.0.1' gem 'bcrypt', '~> 3.1', '>= 3.1.11' gem 'devise', '~> 3.5', '>= 3.5.6' gem 'best_in_place', '~> 3.1' gem 'rails_autolink', '~> 1.1', '>= 1.1.6' gem 'http_accept_language', '~> 2.0', '>= 2.0.5' gem 'browser-timezone-rails', github: 'kbaum/browser-timezone-rails' gem 'turnout', '~> 2.2', '>= 2.2.1' gem 'actionview-encoded_mail_to', '~> 1.0', '>= 1.0.7' gem 'momentjs-rails', '~> 2.11' gem 'kaminari', '~> 0.16.3' group :development, :test do gem 'byebug' gem 'web-console', '~> 3.0' gem 'spring' gem 'bullet' gem 'rspec-rails' gem 'factory_girl_rails' gem 'shoulda-matchers' end group :development do gem 'sqlite3' gem "letter_opener_web" end group :production do gem 'rails_12factor' gem 'pg' end ================================================ FILE: LICENSE ================================================ Copyright 2016 SHIFT, Inc. All rights reserved. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016 SHIFT, Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # Chibineko [![CircleCI](https://circleci.com/gh/tabbyz/chibineko.svg?style=shield)](https://circleci.com/gh/tabbyz/chibineko) [![License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) **Chibineko** is a simple test supporting tool specializing in the management of manual tests. It is hosted on [https://chibineko.jp](https://chibineko.jp). ## Screenshot ### Top page ![screenshot_top](https://cloud.githubusercontent.com/assets/15026812/13838533/3930fb0a-ec58-11e5-87d2-07a22a808347.png) ### Execute a test ![screenshot_execute_test](https://cloud.githubusercontent.com/assets/15026812/13838537/3ac1d7fa-ec58-11e5-8d04-5e0a50cfb08e.png) ### Create a test ![screenshot_create_test](https://cloud.githubusercontent.com/assets/15026812/13838540/3cb37fd2-ec58-11e5-92b3-1aa43d3cb41e.png) ## Quick Start ### Install Chibineko on Heroku Clone the repo ```console $ git clone git@github.com:tabbyz/chibineko.git $ cd chibineko ``` Create a app at Heroku ```console $ heroku create NAME_FOR_YOUR_APP ``` Push an app to Heroku ```console $ git push heroku master ``` Initialization of database ```console $ heroku run rake db:migrate $ heroku run rake db:seed ``` Set the environment variable ```console $ heroku config:add SECRET_KEY_BASE=`rake secret` ``` Open your Chibineko and sign in with your credentials ```console $ heroku open ``` Your username is `test@example.com` and your password is `test` as well. ### Configure Email You must have email settings to the user registration. Create a configuration file ```console $ cp config/mailer.yml.example config/mailer.yml ``` ```yaml # For example, if you want to use Gmail as the SMTP server. production: default_url_options: host: "example.com" delivery_method: :smtp smtp_settings: enable_starttls_auto: true address: "smtp.gmail.com" port: 587 domain: "example.com" authentication: "plain" user_name: "@gmail.com" password: "" ``` Remove it from `.gitignore` ```yaml config/mailer.yml # Remove ``` To commit the changes ```console $ git add . $ git commit -m "Configure Email" ``` Push an app to Heroku ```console $ git push heroku master ``` ## Contributing 1. Fork it 1. Create your feature branch (`git checkout -b my-new-feature`) 1. Commit your changes (`git commit -am 'Add some feature'`) 1. Push to the branch (`git push origin my-new-feature`) 1. Create new Pull Request ## License See [LICENSE](LICENSE). © SHIFT, Inc. All Rights Reserved. ================================================ FILE: Rakefile ================================================ # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require File.expand_path('../config/application', __FILE__) Rails.application.load_tasks ================================================ FILE: app/assets/images/.keep ================================================ ================================================ FILE: app/assets/javascripts/application.coffee ================================================ #= require jquery #= require jquery_ujs #= require jquery.cookie #= require jstz #= require browser_timezone_rails/set_time_zone #= require bootstrap-sprockets #= require best_in_place #= require Sortable.min #= require toastr.min #= require ladda/spin.min #= require ladda/ladda.min #= require ladda/ladda.jquery.min #= require jquery.overlay.min #= require jquery.bootgrid.min #= require perfect-scrollbar.jquery.min #= require moment #= require moment/ja #= require jquery.readyselector #= require i18n #= require i18n/translations #= require_tree . $(document).ready -> $("[data-toggle='popover']").popover({ container: "body" }) $("[data-toggle='tooltip']").tooltip({ container: "body" }) $(".dropdown-toggle").dropdown() $(".best_in_place").best_in_place() $(".carousel").carousel() $(".hover-scroll").perfectScrollbar() $(document).on "shown.bs.modal", (e) -> $("[autofocus]", e.target).focus() $(document).on "click", ".collapse-btn", (e) -> if $(this).hasClass("collapsed") $(this).text(I18n.t("js.common.collapse.expand")) else $(this).text(I18n.t("js.common.collapse.close")) ================================================ FILE: app/assets/javascripts/dashboards.coffee ================================================ ================================================ FILE: app/assets/javascripts/data_table.coffee ================================================ $(document).ready -> # Bootgrid settings $(".data-table").on("initialized.rs.jquery.bootgrid", (e) -> $(".table-toolbar").hide().appendTo(".actionBar") ).on("loaded.rs.jquery.bootgrid", (e) -> $(".table-toolbar").hide() ).on("selected.rs.jquery.bootgrid deselected.rs.jquery.bootgrid", (e, rows) -> count = $(".bootgrid-table tr.active").length if count == 0 $(".table-toolbar").hide() else $(".table-toolbar").show() $(".table-toolbar .selected-count").text("#{count} #{I18n.t('js.tests.toolbar.selected')}") ).bootgrid columnSelection: false, rowCount: -1, selection: true, multiSelect: true, converters: { date: { from: (value) -> return moment(value) to: (value) -> return moment(value).locale(I18n.locale).format("ll") }, }, formatters: { title: (column, row) -> return "#{row.title}" }, labels: { loading: I18n.t("js.bootgrid.loading"), noResults: I18n.t("js.bootgrid.no_results"), search: I18n.t("js.bootgrid.search"), }, templates: { infos: "", select: "" } # Bulk destroy action $(document).on "click", ".bulk-destroy-btn", -> if !confirm(I18n.t("js.common.messages.delete_confirm")) return false tests = [] for e in $(".bootgrid-table tr.active") tests.push $(e).data("row-id") $.ajax url: "/t/bulk_destroy" type: 'PATCH' dataType: 'script' data: $.param({ tests: tests }) # Bulk move action $(document).on "click", ".bulk-move-btn", -> project = $(this).data("project-id") tests = [] for e in $(".bootgrid-table tr.active") tests.push $(e).data("row-id") $.ajax url: "/t/bulk_move" type: 'PATCH' dataType: 'script' data: $.param({ tests: tests, project: project }) ================================================ FILE: app/assets/javascripts/move_tests.coffee ================================================ $(".tests.show").ready -> $(document).on "click", ".move-test-modal .clickable", -> $(".project-list .clickable.active").removeClass("active") $(this).addClass("active") project_id = $(this).attr("data-project-id") $(".selected-id-field").val(project_id) ================================================ FILE: app/assets/javascripts/projects.coffee ================================================ ================================================ FILE: app/assets/javascripts/static_pages.coffee ================================================ # Place all the behaviors and hooks related to the matching controller here. # All this logic will automatically be available in application.js. # You can use CoffeeScript in this file: http://coffeescript.org/ ================================================ FILE: app/assets/javascripts/team_users.coffee ================================================ $(".projects, .teams, .tests").ready -> # ================================================== # Function # ================================================== preSearchUser = null searchUser = (team, email) -> clearTimeout(preSearchUser) preSearchUser = setTimeout (-> ajaxSearchUser(team, email) ), 500 ajaxSearchUser = (team, email) -> btn = $(".ladda-button").ladda() label = btn.find(".ladda-label") $.ajax url: "/teams/#{team}/team_users/ajax_search_user" type: 'GET' dataType: 'json' data: $.param({ email: email }) beforeSend: (jqXHR, settings) -> label.text(I18n.t("js.team_sidebar.edit_members.search_user")) btn.ladda("start") error: (jqXHR, textStatus, errorThrown) -> console.log "AJAX Error: #{textStatus}" btn.ladda("stop") label.text(I18n.t("js.team_sidebar.edit_members.unknown_error")) btn.prop("disabled", true) success: (data, textStatus, jqXHR) -> if data label.text(I18n.t("js.team_sidebar.edit_members.add_user")) btn.ladda("stop") btn.prop("disabled", false) else label.text(I18n.t("js.team_sidebar.edit_members.not_found")) btn.ladda("stop") btn.prop("disabled", true) # ================================================== # Event # ================================================== $(document).on "keyup", "#new_team_user #email", -> email = $(this).val() team = $("#team_name").val() searchUser(team, email) ================================================ FILE: app/assets/javascripts/teams.coffee ================================================ ================================================ FILE: app/assets/javascripts/test_preview.coffee ================================================ $(".tests.new, .tests.edit").ready -> # ================================================== # Function # ================================================== caseTag = (markdown) -> tag = "" array = markdown.split(/\r\n|\r|\n/) for line in array line = escapeHtml(line) switch caseType(line) when "heading" lv = caseHeadingLevel(line) body = caseBody(line) if tag == "" tag += "
" else if lv == 1 tag += "
" if lv == 1 tag += """
#{body}
""" when "testcase" tag += "
" if tag == "" body = caseBody(line) tag += """
#{body}
""" tag = "
" + tag + "
" caseHeadingLevel = (text) -> return -1 if text == "" text = text.trim() if text.substr(0, 1) == "#" text.match(/^#*/)[0].length else 0 caseType = (text) -> switch caseHeadingLevel(text) when -1 then "blank" when 0 then "testcase" else "heading" caseBody = (text) -> switch caseType(text) when "heading" text.match(/^(#*)(.*)/)[2] when "testcase" r = text.match(/(.*)(,\s?\[.*\])/) if r then r[1] else text preUpdatePreview = null updatePreview = () -> clearTimeout(preUpdatePreview) preUpdatePreview = setTimeout (-> e = $("#test_markdown") if e.length if markdown = e.val() tag = caseTag(markdown) $(".test-preview-content").empty() $(".test-preview-content").append(tag) $(".test-preview-content").show() $(".test-cheatsheet").hide() else $(".test-preview-content").hide() $(".test-cheatsheet").show() ), 200 escapeHtml = (text) -> text.replace(/&/g, "&").replace(/"/g, """).replace(//g, ">") # ================================================== # Event # ================================================== updatePreview() $(document).on "keyup", "#test_markdown", -> updatePreview() # $(document).on "scroll", "#test_markdown", -> # TODO: Doesn't work $("#test_markdown").scroll -> syncScroll($(this)) $("#test_markdown").overlay( { match: /^#.*|\n#.*/g, css: {"background-color": "#DDD"} }) # Leave this page $("#test_markdown").change -> $(window).on "beforeunload", -> return I18n.t("js.tests.new.messages.unsaved_changes") $("input[type=submit]").click -> $(window).off("beforeunload") # TODO: To local methods @syncScroll = (e) -> source = e target = $(".test-preview-content") sourceScrollMax = source.get(0).scrollHeight - source.get(0).offsetHeight targetScrollMax = target.get(0).scrollHeight - target.get(0).offsetHeight scrollRatio = source.scrollTop()/ sourceScrollMax target.scrollTop(targetScrollMax * scrollRatio) ================================================ FILE: app/assets/javascripts/tests.coffee ================================================ $(".tests.show").ready -> # ================================================== # Function # ================================================== countAll = () -> $(".result-btn").size() countLabel = (label) -> count = 0 for e in $(".result-btn") count += 1 if $(e).text() == label count updateProgressBar = () -> transition = "width 0.5s ease" count = countAll() for text in gon.resultLabelTexts color = gon.resultLabels[text] ratio = countLabel(text) / count * 100 $(".progress-animation .progress-bar[data-result-text='#{text}']").css({ width: "#{ratio}%", WebkitTransition: transition, MozTransition: transition, MsTransition: transition, OTransition: transition, transition: transition }) updateProgressCount = () -> tag = """ """ total = countAll() for text in gon.resultLabelTexts count = countLabel(text) ratio = (count / total * 100).toFixed(1) color = gon.resultLabels[text] tag += """ """ tag += """
#{text} #{count} (#{ratio}%)
#{I18n.t("js.tests.navbar.total")} #{total}
""" $(".progress-count").attr("data-content", tag) removeColorClass = (element) -> element.removeClass (idx, css) -> return (css.match(/\bcolor-\S+/g) || []).join(' ') setResult = (btn, label) -> dropdownBtn = btn.next() removeColorClass(btn) removeColorClass(dropdownBtn) btn.addClass("color-#{gon.resultLabels[label]}") dropdownBtn.addClass("color-#{gon.resultLabels[label]}") btn.text(label) nextResultLabel = (currentLabel) -> idx = $.inArray(currentLabel, gon.resultLabelTexts) if idx == gon.resultLabelTexts.length - 1 gon.resultLabelTexts[0] else gon.resultLabelTexts[idx + 1] prePostResult = null postResult = (caseId, result) -> clearTimeout(prePostResult) prePostResult = setTimeout (-> ajaxPostResult(caseId, result) ), 300 ajaxPostResult = (caseId, result) -> $.ajax url: "#{gon.test.slug}/cases/#{caseId}" type: 'PATCH' dataType: 'json' data: $.param({ testcase: { result: result } }) error: (jqXHR, textStatus, errorThrown) -> toastr.warning(I18n.t("js.tests.show.errors.conflict"), I18n.t("js.tests.show.errors.please_reload")) # console.log "Ajax Error: #{textStatus}" success: (data, textStatus, jqXHR) -> # console.log "Ajax Successful: #{data}" # ================================================== # Event # ================================================== updateProgressBar() updateProgressCount() # Color picker $(document).on "click", ".color-picker .color-item", -> radio = $(this).prev() color = radio.val() popover = $(this).parents(".popover") triggerElement = popover.prev() removeColorClass(triggerElement) triggerElement.addClass("color-#{color}") triggerElement.val(color) # Use default label $(document).on "click", ".user-default-result-label", -> modalBody = $(this).parents(".modal-body") table = modalBody.find("table") tableFooter = modalBody.find(".table-footer") if $(this).is(":checked") table.hide() tableFooter.hide() else table.show() tableFooter.show() # Remove result label $(document).on "click", ".delete-result-label-icon", -> # Check item count tbody = $(this).parents("tbody") if tbody.find("tr").size() == 1 alert(I18n.t("js.tests.edit_result_label.errors.too_short")) return # Remove item tr = $(this).parents("tr") tr.remove() # Add result label $(document).on "click", ".add-result-label-icon", -> table = $(this).parent(".table-footer").prev("table") tbody = table.children("tbody") tr = tbody.find("tr:first").clone() # Check item count if tbody.find("tr").size() == 9 alert(I18n.t("js.tests.edit_result_label.errors.too_long")) return # Reset value textInput = tr.find(".result-label-text > input") textInput.val("") colorInput = tr.find(".result-label-color > input") removeColorClass(colorInput) colorInput.addClass("color-white") # Add Item tbody.append(tr) $(".select-color").popover({ html : true, content: $(".popover-content-template").html() }) $(document).on "click", ".result-btn", -> label = nextResultLabel($(this).text().trim()) btn = $(this) setResult(btn, label) postResult(btn.data("case-id"), label) updateProgressBar() updateProgressCount() $(document).on "click", ".result-item", -> groupEle = $(this).closest(".result-btn-group")[0] btn = $(groupEle).children(".result-btn") label = $(this).text().trim() setResult(btn, label) postResult(btn.data("case-id"), label) updateProgressBar() updateProgressCount() $(document).on "blur", ".testcase-list .note input", -> $(this).parents(".best_in_place").attr("data-original-title", $(this).val()) $(document).on "ajax:error", ".best_in_place", () -> toastr.warning(I18n.t("js.tests.show.errors.conflict"), I18n.t("js.tests.show.errors.please_reload")) $(document).on "click", ".js-collapse-btn", -> heading = $(this).closest(".tr") headingLv = heading.data("heading-level") isCollapsed = heading.hasClass("collapsed") tr = heading.next(".tr") while tr.length == 1 if tr.hasClass("testcase") if isCollapsed tr.show() else tr.hide() tr = tr.next(".tr") else if tr.hasClass("heading") if tr.data("heading-level") > headingLv if isCollapsed tr.removeClass("collapsed") else tr.addClass("collapsed") tr = tr.next(".tr") else break heading.toggleClass("collapsed") $(document).on "click", ".js-collapse-all-btn", -> for heading in $(".heading") $(heading).addClass("collapsed") for testcase in $(".testcase") $(testcase).hide() $(this).hide() $(".js-expand-all-btn").show() $(document).on "click", ".js-expand-all-btn", -> for heading in $(".heading") $(heading).removeClass("collapsed") for testcase in $(".testcase") $(testcase).show() $(this).hide() $(".js-collapse-all-btn").show() ================================================ FILE: app/assets/javascripts/validation.coffee ================================================ $(document).ready -> $(document).on "keydown", ".form-control.validation", (e) -> if (e.keyCode != 13) $(this).popover("destroy") $(document).on "ajax:beforeSend", "form", (e, xhr, settings) -> # console.log "ajax:beforeSend" $(document).on "ajax:success", "form", (e, data, status, xhr) -> # console.log "ajax:success" $(document).on "ajax:error", "form", (e, xhr, status, error) -> # console.log("ajax:error") errors = $.parseJSON(xhr.responseText) form = $(e.currentTarget) name = form.find(".form-control").attr("name") model = name.match(/^(.*)\[.*\]$/)[1] for k, v of errors field = $("##{model}_#{k}") msg = v group = field.parent(".form-group") if group.find(".form-control-feedback").length == 0 group.addClass("has-error has-feedback") field.popover({ content: msg, placement: "bottom", trigger: "manual" }).popover("show") popover = group.children(".popover") popover.addClass("error") popover.css("left", 0) group.find(".popover-content").text(msg) btn = group.nextAll(".ladda-button:first") btn.ladda().ladda("stop") $(document).on "ajax:complete", "form", (e, xhr, status) -> # console.log("complete") ================================================ FILE: app/assets/stylesheets/application.scss ================================================ /* *= require_tree . *= require_self */ @import "bootstrap-sprockets"; @import "bootstrap"; @import "bootstrap_overrides"; @import "color"; @import "toastr.min"; @import "perfect-scrollbar.min"; @import "toastr_overrides"; @import "ladda-themeless.min"; @import "jquery.bootgrid.min"; @import "font-awesome"; @import "vendor/*"; ================================================ FILE: app/assets/stylesheets/bootstrap_overrides.scss ================================================ a { &:hover, &:focus { text-decoration: none; } } textarea { resize: none; } .btn, .form-control { border-radius: 2px; } button.btn.ladda-button { &:focus { outline: none; } } .btn-link { &:hover, &:focus { text-decoration: none; outline: 0; } } .modal-xs, .modal-sm { margin: 100px auto; } .modal-xs { width: 300px; .modal-footer { padding: 15px; } } .modal-sm { width: 450px; } .modal-dialog { .modal-content { border-radius: 0; .modal-header { border-bottom: none; } .modal-body { padding: 10px 30px 30px; } .modal-footer { border-top: none; } } } .dropdown-menu { .dropdown-header { padding: 8px 12px 4px 12px; &:first-child { padding-top: 4px; } } .divider { margin: 4px 0; } } // TODO: .navbar-nav > li { float: left; } .navbar-nav { margin: 0; } .navbar-nav .open .dropdown-menu { background-color: #FFF; } // TODO: .navbar-right { float: right !important; } ================================================ FILE: app/assets/stylesheets/color.scss ================================================ // http://www.materialui.co/colors // background-color: 400 // background-color(hover): 500 // border-color: 600 .color-red { background-color: #F44336; color: #FFF; &.btn:hover, &.btn:focus { background-color: #E53935; color: #FFF; } &.btn, &.label { border: 1px solid #C62828; } } .color-pink { background-color: #E91E63; color: #FFF; &.btn:hover, &.btn:focus { background-color: #D81B60; color: #FFF; } &.btn, &.label { border: 1px solid #AD1457; } } .color-purple { background-color: #9C27B0; color: #FFF; &.btn:hover, &.btn:focus { background-color: #8E24AA; color: #FFF; } &.btn, &.label { border: 1px solid #6A1B9A; } } .color-deeppurple { background-color: #673AB7; color: #FFF; &.btn:hover, &.btn:focus { background-color: #5E35B1; color: #FFF; } &.btn, &.label { border: 1px solid #4527A0; } } .color-indigo { background-color: #3F51B5; color: #FFF; &.btn:hover, &.btn:focus { background-color: #3949AB; color: #FFF; } &.btn, &.label { border: 1px solid #283593; } } .color-blue { background-color: #2196F3; color: #FFF; outline: 0; &.btn:hover, &.btn:focus { background-color: #1E88E5; color: #FFF; } &.btn, &.label { border: 1px solid #1565C0; } } .color-cyan { background-color: #00BCD4; color: #FFF; &.btn:hover, &.btn:focus { background-color: #00ACC1; color: #FFF; } &.btn, &.label { border: 1px solid #00838F; } } .color-teal { background-color: #009688; color: #FFF; &.btn:hover, &.btn:focus { background-color: #00897B; color: #FFF; } &.btn, &.label { border: 1px solid #00695C; } } .color-green, .color-primary { background-color: #4CAF50; color: #FFF; &.btn:hover, &.btn:focus { background-color: #43A047; color: #FFF; } &.btn, &.label { border: 1px solid #2E7D32; } } .color-lightgreen { background-color: #8BC34A; color: #FFF; &.btn:hover, &.btn:focus { background-color: #7CB342; color: #FFF; } &.btn, &.label { border: 1px solid #558B2F; } } .color-lime { background-color: #CDDC39; color: #FFF; &.btn:hover, &.btn:focus { background-color: #C0CA33; color: #FFF; } &.btn, &.label { border: 1px solid #9E9D24; } } .color-yellow { background-color: #FFEB3B; color: #FFF; &.btn:hover, &.btn:focus { background-color: #FDD835; color: #FFF; } &.btn, &.label { border: 1px solid #F9A825; } } .color-amber { background-color: #FFC107; color: #FFF; &.btn:hover, &.btn:focus { background-color: #FFB300; color: #FFF; } &.btn, &.label { border: 1px solid #FF8F00; } } .color-orange { background-color: #FF9800; color: #FFF; &.btn:hover, &.btn:focus { background-color: #FB8C00; color: #FFF; } &.btn, &.label { border: 1px solid #EF6C00; } } .color-deeporange { background-color: #FF5722; color: #FFF; &.btn:hover, &.btn:focus { background-color: #F4511E; color: #FFF; } &.btn, &.label { border: 1px solid #D84315; } } .color-brown { background-color: #795548; color: #FFF; &.btn:hover, &.btn:focus { background-color: #6D4C41; color: #FFF; } &.btn, &.label { border: 1px solid #4E342E; } } .color-gray { background-color: #9E9E9E; color: #FFF; &.btn:hover, &.btn:focus { background-color: #757575; color: #FFF; } &.btn, &.label { border: 1px solid #616161; } } .color-bluegray { background-color: #607D8B; color: #FFF; &.btn:hover, &.btn:focus { background-color: #546E7A; color: #FFF; } &.btn, &.label { border: 1px solid #37474F; } } .color-lightgray { background-color: #DDD; color: #424242; &.btn:hover, &.btn:focus { background-color: #CCC; color: #424242; } &.btn, &.label { border: 1px solid #AAA; } } .color-white { background-color: #FFF; color: #333; &.btn:hover, &.btn:focus { background-color: #EEE; color: #333; } &.btn, &.label { border: 1px solid #BDBDBD; } } ================================================ FILE: app/assets/stylesheets/common.scss ================================================ .modal-fullscreen { width: 100% !important; height: 100% !important; margin: 0 !important; padding: 20px; .modal-content { height: 100%; border-radius: 0; .modal-header { height: 55px; } .modal-body { position: absolute; top: 55px; left: 0; right: 0; bottom: 50px; } .modal-footer { position: absolute; bottom: 0; left: 0; right: 0; } } } .text-red { color: #d32f2f !important; } .btn-ghost { border: 3px solid #FFF; border-radius: 4px; background-color: transparent; color: #FFF; font-size: 20px; padding: 18px 20px; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -o-transition: all 0.3s ease; &:hover, &:focus { background-color: #FFF; color: #01AB52; } } .inline-block { display: inline-block !important; } .has-feedback .form-control.validation { padding-right: 16px; } .popover.error { width: 100%; max-width: 100%; background-color: #F44336; color: #FFF; &.top .arrow:after, &.bottom .arrow:after { border-bottom-color: #F44336; } } .empty-data { padding: 140px 0; text-align: center; .symbol { margin-bottom: 10px; font-size: 150px; color: #BBB; } h3 { margin-top: 0; margin-bottom: 40px; color: #BBB; font-size: 26px; } } .color-picker { input[type=radio] { display: none; margin: 0; + label { height: 20px; width: 20px; padding: 0; margin: 0; border-radius: 2px; cursor: pointer; } &:checked + label { border: 2px solid #333; } } } .card { position: relative; width: 500px; margin: 0 auto; padding: 50px 60px; background-color: #FFF; border: 1px solid #e8e8e8; border-radius: .25rem; box-shadow: 0 1px 0 rgba(0,0,0,.25); &.card-lg { width: 800px; } &.card-alert { margin: 20px auto -20px; padding: 20px; border-left: 6px solid #C62828; h2 { margin-top: 0; margin-bottom: 16px; font-size: 16px; font-weight: bold; } ul { padding-left: 20px; margin-bottom: 0; li:not(:last-child) { margin-bottom: 5px; } } } &.card-form { margin: 40px auto 0; padding: 40px 80px; text-align: center; .card-title { margin-top: 0; margin-bottom: 30px; font-size: 28px; } .btn[type='submit'] { margin-bottom: 20px; } } } .setting-list { margin-bottom: 0; padding-left: 0; list-style: none; li { position: relative; padding-bottom: 20px; border-bottom: 1px solid #DDD; &:not(:last-child) { margin-bottom: 20px; } .collapse-btn { position: absolute; top: 0; right: 0; } .accordion-title { font-size: 16px; font-weight: bold; } .accordion-description { margin-bottom: 20px; padding-right: 100px; color: #555; } .form-control { display: inline-block; margin-right: 5px; margin-bottom: 5px; } } } ================================================ FILE: app/assets/stylesheets/data_table.scss ================================================ .actionBar .table-toolbar { display: inline-block; float: right; .selected-count { display: inline-block; padding: 7px 10px; margin-right: 10px; } .btn { display: inline-block; padding: 6px 10px; } } ================================================ FILE: app/assets/stylesheets/layouts/navbar.scss ================================================ .navbar { background-color: #FFF; > .container-fluid { margin-top: 7px; } .navbar-nav > li > a { padding: 6px 12px; border-radius: 2px; } .navbar-nav > li:not(:last-child) { margin-right: 10px; } .navbar-nav.pull-right { padding-right: 20px; } } .navbar.navbar-with-sidebar { padding-left: 280px; .navbar-content { height: 50px; padding-left: 20px; padding-right: 20px; .btn { padding: 6px 12px; margin-top: 8px; } .navbar-title { padding: 4px; display: inline-block; font-size: 18px; font-weight: bold; padding-top: 12px; .dropdown { .fa-angle-down { padding: 0 8px; font-size: 85%; } } } } } ================================================ FILE: app/assets/stylesheets/layouts/sidebar.scss ================================================ .page-with-sidebar { .sidebar { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; height: 100%; } ul { list-style: none; padding-left: 0; margin: 0; } .root-sidebar { background-color: #192637; a { color: #C5D0DE; display: block; &:hover, &:focus { background-color: #364760; color: #FFF; } } .home-btn, .add-team-btn { padding: 16px 0; text-align: center; } .home-btn { background-color: #01AB52; color: #FFF; &:hover, &:focus { background-color: #01AB52; } } .add-team-btn { background-color: #192637; font-size: 28px; } ul { -webkit-flex: 1; flex: 1; li { text-align: center; .team-btn { height: 60px; text-align: center; .hilight-bar { display: inline-block; float: left; width: 4px; height: 60px; background-color: transparent; } .symbol { display: block; font-size: 20px; font-weight: bold; padding-top: 6px; } .description { display: inline-block; overflow: hidden; width: 46px; // sidebar-width - hilight-bar-width - (margin-left + margin-right) font-size: 11px; } } } li.active { .team-btn { background-color: #364760; color: #FFF; } .hilight-bar { background-color: #01AB52; } } } } .team-sidebar { border-right: 1px solid #DDD; background-color: #FFF; .sidebar-title { .dropdown-toggle { display: inline-block; width: 100%; margin-bottom: 10px; padding: 10px 20px; font-size: 20px; font-weight: bold; .team-name { > span { display: inline-block; word-wrap: break-word; max-width: 155px; } .fa-angle-down { padding-top: 0; width: 20px; font-size: 85%; text-align: right; } } &:hover { background-color: #EEE; border-radius: 2px; } } .username { display: block; overflow: hidden; margin-top: 5px; color: #777; font-size: 14px; font-weight: normal; font-style: italic; } } .list-header { padding: 0 20px; margin-bottom: 5px; font-size: 14px; font-weight: bold; color: #555; .add-project-btn { float: right; padding: 0 4px; } } .sidebar-list { -webkit-flex: 1; flex: 1; padding: 0 20px; li { overflow-x: hidden; a { display: inline-block; width: 100%; padding: 6px; font-size: 12px; font-weight: bold; } &:hover, &.active { background-color: #EEE; border-radius: 2px; } } } .coach-mark { color: #AAA; font-size: 16px; height: 60px; .fa { margin-top: 22px; margin-left: 10px; } } } } ================================================ FILE: app/assets/stylesheets/layouts/style.scss ================================================ body, html { width: 100%; height: 100%; } body { -webkit-text-size-adjust: 100%; margin: 0; } body { &.registrations, &.sessions, &.passwords, &.confirmations, &.static_pages.terms { padding-top: 50px; background-color: #F3F3F3; .navbar { background-color: #FFF; box-shadow: 0 1px 1px rgba(0,0,0,.1); } .page { padding: 40px; } } } .logo { display: inline-block; color: #555; font-size: 24px; font-weight: bold; &:hover, &:focus { color: #555; } } .ps-scrollbar-x-rail, .ps-scrollbar-y-rail { z-index: 99; } .page { height: 100%; .contents { position: relative; padding: 20px; min-height: 100%; } } .page-with-sidebar { .sidebar { position: fixed; top: 0; bottom: 0; } .root-sidebar { left: 0; width: 60px; z-index: 40; } .team-sidebar { left: 60px; // Equal to root-sidebar width width: 220px; z-index: 30; } .contents { padding-top: 70px; padding-left: 300px; z-index: 10; .navbar { z-index: 20; } } } ================================================ FILE: app/assets/stylesheets/static_pages/terms.scss ================================================ .terms { h1 { margin-top: 0; } h2 { margin-top: 30px; padding-bottom: 10px; border-bottom: 1px solid #DDD; } } ================================================ FILE: app/assets/stylesheets/static_pages/top.scss ================================================ .static_pages.top { .navbar { background-color: #01AB52; box-shadow: none; margin-bottom: 0; border-radius: 0; .logo { color: #FFF; } .navbar-nav > li > a { color: #FFF; &:hover, &:focus { color: #01AB52; } } } .contents { padding: 0; } .branding { background-color: #01AB52; color: #FFF; padding: 50px 20px 0; @media (min-width: 768px) { padding-top: 100px; } h1 { margin-top: 0; margin-bottom: 20px; font-size: 34px; font-weight: bold; @media (min-width: 768px) { font-size: 52px; } } p { margin-bottom: 70px; font-size: 18px; @media (min-width: 768px) { font-size: 24px; } } .browser-outline { background-color: #F6F6F6; max-width: 900px; border-top-left-radius: 6px; border-top-right-radius: 6px; margin-top: 80px; margin-right: auto; margin-left: auto; .browser-header { text-align: left; padding: 2px 6px; font-size: 10px; @media (min-width: 768px) { padding: 5px 10px; font-size: 14px; } .browser-btn { li { display: inline-block; margin: 0 2px; .red { color: #FF4440; } .yellow { color: #FFBD00; } .green { color: #00D42A; } } } } } .carousel { border-style: solid; border-color: #DDD; border-width: 1px 1px 0 1px; .carousel-control { background-image: none; @media (min-width: 992px) { &.left { margin-left: -120px; } &.right { margin-right: -120px; } } } .carousel-indicators { li { border-color: #01AB52; &.active { background-color: #01AB52; } } } } } .feature { .col-xs-12 { margin-bottom: 40px; } .feature-item { .feature-icon { margin-bottom: 10px; font-size: 60px; text-align: center; } .feature-title { margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center; } .feature-content { font-size: 14px; text-align: left; } } } section { padding-top: 40px; padding-bottom: 30px; text-align: center; border-top: 1px solid #DDD; color: #666; h2 { margin-bottom: 40px; font-size: 42px; font-weight: bold; } ul { display: inline-block; padding-left: 0; margin-bottom: 0; list-style: none; text-align: left; li { margin-bottom: 30px; font-size: 20px; .fa { margin-right: 5px; } } } } .footer { border-top: 1px solid #DDD; text-align: center; padding-top: 17px; padding-bottom: 17px; position: relative; a { color: #555; &:hover { text-decoration: underline; } } .list-inline { display: inline-block; margin-bottom: 0; li { padding-left: 10px; padding-right: 10px; } } .copyright { display: block; margin-top: 18px; margin-right: 10px; @media (min-width: 768px) { position: absolute; top: 0; right: 14px; } } } } ================================================ FILE: app/assets/stylesheets/teams/team_users.scss ================================================ .team-users-modal { .search-user { text-align: center; width: 300px; margin: 0 auto; margin-bottom: 30px; .form-group { text-align: left; margin-bottom: 30px; label { font-weight: normal; } } .ladda-button { .fa-chevron-down { margin-right: 4px; } &:disabled .fa-chevron-down { display: none; } } } } ================================================ FILE: app/assets/stylesheets/tests/edit_result_label.scss ================================================ .edit-result-label-modal { .popover-content-template { display: none; } .checkbox { margin-left: 8px; } table { table-layout: fixed; width: 100%; td { padding: 8px; } tr { .result-label-text { input { display: inline-block; border: none; outline:none; padding: 2px 4px; border-radius: 2px; width: 100%; &:hover { background-color: #FFFFCC; } } } .result-label-color { .select-color { display: inline-block; width: 20px; height: 20px; padding: 0; color: transparent; } } .drag-handle-icon, .delete-result-label-icon { color: transparent; cursor: pointer; } .delete-result-label { text-align: right; .delete-result-label-icon { padding: 6px 4px; } } &:hover, &:focus { .drag-handle-icon { color: #555; } .delete-result-label-icon { color: #C62828; } } } } .table-footer { display: block; text-align: right; padding-right: 2px; .add-result-label-icon { cursor: pointer; width: 30px; padding: 6px 4px; text-align: center; } } .drag-handle { cursor: move !important; cursor: -webkit-grabbing !important; } } ================================================ FILE: app/assets/stylesheets/tests/form.scss ================================================ .tests.new, .tests.edit { .contents { position: relative; .test-title { position: absolute; left: 300px; right: 20px; top: 55px; height: 44px; } .test-editor { position: absolute; top: 110px; left: 300px; right: 20px; bottom: 65px; border: 1px solid #DDD; .test-markdown, .test-preview { display: inline-block; height: 100%; width: 50%; .test-editor-header { padding: 5px 10px; height: 30px; background-color: #EEE; color: #AAA; } } .test-markdown { border-right: 1px solid #DDD; .textoverlay-wrapper { height: calc(100% - 30px); } textarea { border: none; width: 100%; height: 100%; box-shadow: none; -moz-box-shadow: none; -webkit-box-shadow: none; overflow-x: hidden; } } .test-preview { float: right; .test-preview-content, .test-cheatsheet { width: 100%; height: calc(100% - 30px); overflow-y: scroll; padding: 10px; } .test-cheatsheet { color: #AAA; padding: 20px; .cheatsheet-header { margin-bottom: 15px; font-size: 18px; font-weight: bold; } .cheatsheet-block { margin-bottom: 20px; .cheatsheet-title { font-size: 14px; font-weight: bold; } .cheatsheet-body { padding: 10px 15px; background-color: #FAFAFA; border-radius: 4px; line-height: 22px; } } } } } footer { position: absolute; left: 20px; right: 20px; bottom: 10px; } } } ================================================ FILE: app/assets/stylesheets/tests/move.scss ================================================ .move-test-modal { .project-list { .unset-project { padding: 5px 7px; font-weight: bold; } .team-name { padding: 2px 10px; margin-top: 2px; margin-bottom: 2px; font-weight: bold; } .project-name { padding: 5px 10px 5px 20px; } ul { &:not(:last-child) { margin-bottom: 10px; } .clickable { border-radius: 3px; cursor: pointer; border: 3px dashed transparent; &:hover, &:focus { border-color: #4CAF50; } &.active { background-color: #4CAF50; color: #C8E6C9; } } } } } ================================================ FILE: app/assets/stylesheets/tests/show.scss ================================================ .tests.show { .progress-count-list { margin-bottom: 0; td { text-align: right; padding: 4px; &:first-child { padding-right: 8px; } &:last-child { color: #777; } .label { display: block; } } tr:nth-last-child(2) > td { padding-bottom: 10px; } tr:last-child { border-top: 1px solid #AAA; } } .contents { padding-top: 70px; .progress.progress-animation { position: fixed; left: 0; right: 0; bottom: 0; height: 7px; padding-top: 1px; margin-bottom: 0; z-index: 1030; border-radius: 0; box-shadow: none; background-color: #DDD; &.margin-sidebar { padding-left: 280px; } } .test-title { margin-top: 0; } .test-description { position: relative; margin-bottom: 20px; padding: 6px 34px 6px 10px; background-color: #EEE; border-radius: 2px; p { margin-bottom: 0; word-wrap: break-word; } .placeholder { color: #BBB; } .tool-menu { position: absolute; top: 0; right: 0; display: inline-block; } } .test-detail { display: block; margin-bottom: 30px; color: #777; text-align: right; } .bip-placeholder { color: #CCC; } } } ================================================ FILE: app/assets/stylesheets/tests/testcase.scss ================================================ .testcase-list { .testcase-group { background-color: #FFF; border: 1px solid #DDD; border-top-style: none; border-radius: 2px; margin-bottom: 16px; } .dropdown-menu { background-color: #324851; a { color: #FFF; } } .tr { display: table; width: 100%; border-top: 1px solid #DDD; } .td { display: table-cell; vertical-align: middle; padding: 2px 2px; } .heading { background-color: #FAFAFA; color: #717171; font-size: 14px; min-height: 29px; padding: 2px 2px 2px 48px; word-break: break-all; .expand-icon, .collapse-icon { width: 8px; color: #AAA; } .expand-icon {display: none; } .collapse-icon { display: inline-block; } &.collapsed { .expand-icon { display: inline-block; } .collapse-icon { display: none; } } .body { font-weight: bold; } &.heading-level-1 { font-size: 16px; letter-spacing: 0.1em; margin-top: 16px; padding-left: 8px; height: 34px; &:first-child { margin-top: 0; } } &.heading-level-2 { padding-left: 18px; } &.heading-level-3 { padding-left: 28px; } &.heading-level-4 { padding-left: 38px; } } .testcase { height: 43px; padding-left: 8px; .number { color: #CCC; padding-left: 4px; width: 30px; font-size: 12px; } .body { padding: 4px 10px; line-height: 16px; } .result { .result-btn-group { .result-btn, .dropdown-toggle { height: 32px; } .result-btn { width: 110px; overflow: hidden; border-top-left-radius: 3px; border-bottom-left-radius: 3px; font-size: 12px; } .dropdown-toggle { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } } } } .result { width: 150px; text-align: center; .result-item, .result-bulk-item { display: block; cursor: pointer; color: #FFF; padding: 3px 20px; margin: 0; &:hover { background-color: #E1E3EA; color: #333; } } } .note { width: 200px; max-width: 200px; padding: 6px 4px; .best_in_place { white-space: nowrap; overflow: hidden; } } } ================================================ FILE: app/assets/stylesheets/toastr_overrides.scss ================================================ #toast-container > div { opacity: 1.0; } .toast-info { background-color: #4CAF50; } .toast-warning { background-color: #FF9800; } ================================================ FILE: app/assets/stylesheets/vendor/best_in_place.scss ================================================ .best_in_place { display: block; width: 100%; padding: 4px; border: 1px solid transparent; border-radius: 2px; &:hover { background-color: #FFFFCC; border-color: #CCC; cursor: text; } input, textarea { width: 100%; padding: 0; background-color: transparent; border: none; outline: 0; } textarea { resize: none; margin-bottom: -25px; white-space: normal; } } ================================================ FILE: app/assets/stylesheets/vendor/bootgrid.scss ================================================ .bootgrid-header { margin-top: 0; .actionBar { padding-left: 0; padding-right: 0; text-align: left; .search { margin-right: 0; .search-field { width: 200px; &:focus { -webkit-transition: width .15s ease-out .1s; -moz-transition: width .15s ease-out .1s; transition: width .15s ease-out .1s; width: 300px; } } } } } .bootgrid-table { .no-results { padding: 30px; } } ================================================ FILE: app/assets/stylesheets/vendor/ladda.scss ================================================ .ladda-button { &:disabled { cursor: default; } &[data-loading=""] .ladda-spinner { top: 50% !important; } .ladda-spinner { top: 0 !important; } } ================================================ FILE: app/controllers/application_controller.rb ================================================ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception before_filter :set_locale, :set_timezone class Forbidden < ActionController::ActionControllerError; end def routing_error raise ActionController::RoutingError.new("No route matches #{request.path.inspect}") end def forbidden_error redirect_to root_url, flash: { warning: t("errors.forbidden") } end def after_sign_in_path_for(resource) dashboard_path end def format_error_message(object) msgs = object.errors.full_messages object.errors.messages.each_with_index.map { |(k, v), i| [k, msgs[i]] }.to_h end private def set_locale client_locale = http_accept_language.compatible_language_from(I18n.available_locales) if current_user locale = current_user.locale if locale.nil? locale = client_locale current_user.update(locale: locale) end else locale = client_locale end I18n.locale = locale end def set_timezone client_tz = Time.zone.name if current_user tz = current_user.timezone if tz.nil? tz = client_tz current_user.update(timezone: tz) end else tz = client_tz end Time.zone = tz end end ================================================ FILE: app/controllers/concerns/.keep ================================================ ================================================ FILE: app/controllers/dashboard_controller.rb ================================================ class DashboardController < ApplicationController before_action :authenticate_user! def index @tests = current_user.tests.includes(:project, project: [:team]) end end ================================================ FILE: app/controllers/projects_controller.rb ================================================ class ProjectsController < ApplicationController before_action :authenticate_user! before_action :authorize!, except: [:new, :create] def show @tests = @project.tests.includes(:user) end def new @project = Project.new end def edit end def create @project = Project.new(project_params) @project.user_id = current_user.id team = Team.find_by(name: params[:team_name]) @project.team_id = team.id unless @project.save render json: format_error_message(@project), status: :unprocessable_entity end end def update unless @project.update(project_params) render json: format_error_message(@project), status: :unprocessable_entity end end def destroy @project.destroy redirect_to team_path(params[:team_name]), notice: t("projects.messages.destroy") end def settings end private def set_project team = Team.find_by(name: params[:team_name]) @project = team.projects.find_by(name: params[:project_name]) routing_error if @project.nil? end def authorize! @project || set_project forbidden_error unless @project.team.authorized?(current_user) end def project_params params.require(:project).permit(:name, :user_id, :team_id) end end ================================================ FILE: app/controllers/static_pages_controller.rb ================================================ class StaticPagesController < ApplicationController def top @user = User.new end def terms end end ================================================ FILE: app/controllers/team_users_controller.rb ================================================ class TeamUsersController < ApplicationController before_action :authenticate_user! def index @team = Team.find_by(name: params[:team_name]) @team_users = @team.team_users.includes(:user) @team_user = TeamUser.new end def create @team = Team.find_by(name: params[:team_name]) user = User.find_by(email: params[:email]) TeamUser.create(team_id: @team.id, user_id: user.id) unless TeamUser.find_by(team_id: @team.id, user_id: user.id) @team_users = @team.team_users @team_user = TeamUser.new end def destroy @team_user = TeamUser.find(params[:id]) @team = @team_user.team @team_user.destroy @team_users = @team.team_users @team_user = TeamUser.new end def ajax_search_user @user = User.find_by(email: params[:email]) render json: @user.present? end end ================================================ FILE: app/controllers/teams_controller.rb ================================================ class TeamsController < ApplicationController before_action :authenticate_user! before_action :authorize!, except: [:new, :create] def show if project = @team.projects.first redirect_to team_project_path(@team, project) end end def new @team = current_user.teams.build if user_signed_in? end def edit end def create @team = current_user.teams.build(team_params) @team.user_id = current_user.id if @team.save @team.authorized!(current_user) else render json: format_error_message(@team), status: :unprocessable_entity end end def update unless @team.update(team_params) render json: format_error_message(@team), status: :unprocessable_entity end end def destroy @team.destroy redirect_to dashboard_path, notice: t("teams.messages.destroy") end def settings end private def set_team @team = Team.find_by(name: params[:name]) routing_error if @team.nil? end def authorize! @team || set_team forbidden_error unless @team.authorized?(current_user) end def team_params params.require(:team).permit(:name) end end ================================================ FILE: app/controllers/testcases_controller.rb ================================================ class TestcasesController < ApplicationController before_action :set_testcase, only: [:update] def update test = Test.find_by(slug: params[:test_slug]) if @testcase.update(testcase_params) render json: @testcase, status: 200 else render json: @testcase.errors, status: :unprocessable_entity, notice: "Unknown error" end end private def set_testcase test = Test.find_by(slug: params[:test_slug]) if test.testcases.exists?(id: params[:id]) @testcase = test.testcases.find(params[:id]) else render json: nil, status: :unprocessable_entity, notice: "Unknown error" end end def testcase_params params.require(:testcase).permit(:result, :note) end end ================================================ FILE: app/controllers/tests_controller.rb ================================================ class TestsController < ApplicationController before_action :authenticate_user!, except: [:show] before_action :authorize!, except: [:index, :new, :create, :bulk_move, :bulk_destroy] def index @tests = Test.all end def show respond_to do |format| format.html do gon.test = @test gon.resultLabelTexts = @test.result_label_texts gon.resultLabels = @test.result_labels_or_default end format.csv do encode = "UTF-8" encode = "Windows-31J" if I18n.locale == :ja csv_data = render_to_string.encode(encode, :invalid => :replace, :undef => :replace) send_data csv_data, type: "text/csv; charset=#{encode}", filename: "testcase.csv" end end end def new @test = Test.new # Duplicate test if source = Test.find_by(slug: params[:source]) @test.project_id = source.project_id @test.title = source.title @test.source = source.slug source.set_markdown(false) @test.markdown = source.markdown end end def edit forbidden_error unless @test.user # Owner unknown @test.set_markdown(true) end def create @test = current_user.tests.build(test_params) team = Team.find_by(name: params[:test][:team_name]) project = team.projects.find_by(name: params[:test][:project_name]) if team && team.authorized?(current_user) @test.project_id = project.id if project # Duplicate test if source = Test.find_by(slug: @test.source) @test.description = source.description @test.result_labels = source.result_labels end if @test.save @test.make_testcase end end def update @test.updated_at = Time.now if @test.update(test_params) @test.make_testcase if @test.markdown end end def destroy team = view_context.current_team project = view_context.current_project @test.destroy if project redirect_to team_project_path(team_name: team.name, project_name: project.name), notice: t("tests.messages.destroy") else redirect_to dashboard_path, notice: t("tests.messages.destroy") end end def description end def result_label end def update_result_label @test.result_labels = nil if params[:test][:use_default] == "0" # Use default label keys = params[:test][:result_label_texts] vals = params[:test][:result_label_colors] hash = Hash[*keys.zip(vals).flatten] @test.result_labels = hash end @test.save end def move @teams = current_user.teams end def bulk_move if params[:project].blank? project_id = nil # Unset project else return unless project = Project.find(params[:project]) # Project not found return unless project.team.authorized?(current_user) # Forbidden error project_id = project.id # Move to project end params[:tests].each do |i| test = Test.find(i) next if test.project.nil? && test.user != current_user next unless test.authorized?(current_user) test.update_column(:project_id, project_id) end end def bulk_destroy params[:tests].each do |i| test = Test.find(i) next if test.project.nil? && test.user != current_user next unless test.authorized?(current_user) test.destroy end end def user_association @test.update(user_id: current_user.id) unless @test.user redirect_to @test end private def set_test @test = Test.find_by(slug: params[:slug]) routing_error if @test.nil? end def authorize! @test || set_test if @project = @test.project team = @project.team || Team.find_by(name: params[:team_name]) forbidden_error unless team.authorized?(current_user) end end def test_params params.require(:test).permit(:title, :description, :project_id, :markdown, :source) end end ================================================ FILE: app/controllers/user_settings_controller.rb ================================================ class UserSettingsController < ApplicationController before_action :authenticate_user! before_action :set_user, only: [:edit, :update] def edit end def update unless @user.update(user_params) render json: format_error_message(@user), status: :unprocessable_entity end end private def set_user @user = current_user end def user_params params.require(:user).permit(:username, :timezone, :locale) end end ================================================ FILE: app/controllers/users/registrations_controller.rb ================================================ class Users::RegistrationsController < Devise::RegistrationsController def edit routing_error # Invalidate a "/users/edit" end def update self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key) prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email) resource_updated = update_resource(resource, account_update_params) yield resource if block_given? if resource_updated if is_flashing_format? flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ? :update_needs_confirmation : :updated set_flash_message :notice, flash_key end sign_in resource_name, resource, bypass: true # respond_with resource, location: after_update_path_for(resource) render "user_settings/update" else clean_up_passwords resource # respond_with resource render json: format_error_message(resource), status: :unprocessable_entity end end end ================================================ FILE: app/helpers/application_helper.rb ================================================ module ApplicationHelper def controller_action controller_name + "#" + action_name end def color_names %w(red pink purple deeppurple indigo blue cyan teal green lightgreen lime yellow amber orange deeporange brown gray bluegray lightgray white) end def page_title(title) if title.blank? "Chibineko: #{I18n.t('.static_pages.top.branding.title')}" else "#{title} | Chibineko" end end end ================================================ FILE: app/helpers/dashboard_helper.rb ================================================ module DashboardHelper end ================================================ FILE: app/helpers/projects_helper.rb ================================================ module ProjectsHelper def project_collection current_team.projects if current_team end def current_project team_name = params[:team_name] project_name = params[:project_name] if team_name && project_name team = Team.find_by(name: team_name) team.projects.find_by(name: project_name) else current_test.project if current_test end end end ================================================ FILE: app/helpers/static_pages_helper.rb ================================================ module StaticPagesHelper end ================================================ FILE: app/helpers/team_users_helper.rb ================================================ module TeamUsersHelper end ================================================ FILE: app/helpers/teams_helper.rb ================================================ module TeamsHelper def current_team if team_name = params[:name] || params[:team_name] Team.find_by(name: team_name) elsif current_test current_test.try(:project).try(:team) end end end ================================================ FILE: app/helpers/tests_helper.rb ================================================ module TestsHelper def current_test Test.find_by(slug: params[:slug]) end end ================================================ FILE: app/mailers/.keep ================================================ ================================================ FILE: app/models/.keep ================================================ ================================================ FILE: app/models/concerns/.keep ================================================ ================================================ FILE: app/models/project.rb ================================================ class Project < ActiveRecord::Base belongs_to :team has_many :tests, :dependent => :destroy before_save { self.name = name.downcase } validates :name, presence: true, uniqueness: { case_sensitive: false, :scope => :team_id }, length: { maximum: 30 }, format: { with: /\A[a-z0-9_]+\z/i } default_scope { order("id ASC") } def to_param name end end ================================================ FILE: app/models/team.rb ================================================ class Team < ActiveRecord::Base has_many :projects, :dependent => :destroy has_many :team_users, :dependent => :destroy has_many :users, :through => :team_users belongs_to :user before_save { self.name = name.downcase } validates :name, presence: true, uniqueness: { case_sensitive: false }, length: { in: 4..30 }, format: { with: /\A[a-z0-9_]+\z/i } default_scope { order("id ASC") } def authorized?(user) user && self.team_users.find_by(user_id: user.id) ? true : false end def authorized!(user) self.team_users.create(team_id: self.id, user_id: user.id) end def to_param name end end ================================================ FILE: app/models/team_user.rb ================================================ class TeamUser < ActiveRecord::Base belongs_to :team belongs_to :user default_scope { order("id ASC") } end ================================================ FILE: app/models/test.rb ================================================ class Test < ActiveRecord::Base belongs_to :user belongs_to :project has_many :testcases, dependent: :destroy after_initialize :set_slug serialize :result_labels validates :title, presence: true, length: { maximum: 255 } validates :description, length: { maximum: 4096 } default_scope { order("id ASC") } attr_accessor :markdown, :source def to_param slug end def authorized?(user) if project = self.project project.team.authorized?(user) else true end end def result_labels_or_default labels = I18n.t("tests.result_labels") result_labels || { labels[:unexecuted] => "white", labels[:pass] => "green", labels[:fail] => "red", labels[:blocked] => "orange", labels[:na] => "gray", } end def result_label_texts result_labels_or_default.keys end def result_label_colors result_labels_or_default.values end def testcase_groups groups = [] buff = [] testcases = self.testcases testcases.each_with_index do |c, i| buff << c if testcases[i + 1].try(:heading_level) == 1 groups << buff buff = [] end end groups << buff if buff.size groups end def set_markdown(with_result = false) array = [] self.testcases.each do |t| buff = nil case t.type when :blank buff = "" when :heading buff = ("#" * t.heading_level) + " " + t.body when :testcase buff = t.body if with_result if t.result && t.result != self.result_label_texts.first buff += ", [#{t.result}]" unless t.note.blank? buff += ", #{t.note}" end end end end array << buff end self.markdown = array.join("\n") end def make_testcase self.testcases.delete_all self.markdown.each_line do |line| level = Testcase.heading_level(line) body = Testcase.body(line) result = Testcase.result(line) note = Testcase.note(line) self.testcases.create(heading_level: level, body: body, result: result, note: note) end end private def set_slug self.slug = self.slug.blank? ? generate_slug : self.slug end def generate_slug token = SecureRandom.urlsafe_base64 self.class.where(slug: token).blank? ? token : generate_slug end end ================================================ FILE: app/models/testcase.rb ================================================ class Testcase < ActiveRecord::Base belongs_to :test validates :body, length: { maximum: 1024 } validates :result, length: { maximum: 255 } validates :note, length: { maximum: 1024 } default_scope { order("id ASC") } def type case self.heading_level when -1 :blank when 0 :testcase else :heading end end def result super || self.test.result_label_texts.first # Default value end def result_color self.test.result_labels_or_default[self.result] || "white" # Default value end class << self def heading_level(text) return -1 if text.blank? text.strip! level = 0 if text.start_with?("#") text.each_char {|char| char == "#" ? level += 1 : break } end level end def body(text) text.try(:strip!) return nil if text.blank? level = self.heading_level(text) if level == 0 text =~ /(.*)(?:,\s\[.*\])/ body = $1 || text else body = text[level..-1] body.strip end end def result(text) text.try(:strip!) return nil if text.blank? text =~ /,(?:\s)?\[(.*)\]/ result = $1 end def note(text) text.try(:strip!) return nil if text.blank? text =~ /,(?:\s)?\[(?:.*)\],(.*)/ note = $1 note.strip if note end end end ================================================ FILE: app/models/user.rb ================================================ class User < ActiveRecord::Base has_many :tests has_many :team_users has_many :teams, :through => :team_users validates :email, length: { maximum: 255 } validates :username, length: { maximum: 30 } default_scope { order("id ASC") } # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable def display_name username || email.split("@").first end end ================================================ FILE: app/views/dashboard/_navbar.html.slim ================================================ nav.navbar.navbar-fixed-top.navbar-with-sidebar .navbar-content = link_to fa_icon("plus", text: t("common.button.new_test")), new_test_path(team_name: current_team.try(:name), project_name: current_project.try(:name)), class: "btn color-blue new-test-btn pull-right", data: { toggle: "tooltip", placement: "bottom", title: t("common.button.create_new_test") } ================================================ FILE: app/views/dashboard/index.html.slim ================================================ - provide :title, t("dashboard.index.title") = render "navbar" - if @tests.size == 0 .empty-data = fa_icon("inbox", class: "symbol") h3 = t("projects.show.empty_message") = link_to fa_icon("plus", text: t(".navbar.new_test")), new_test_path, class: "btn btn-lg color-blue" - else = render "tests/table_toolbar" table.table.table-condensed.table-hover.data-table style="table-layout:fixed;" thead tr th.hidden data-column-id="id" data-type="numeric" data-identifier="true" data-visible="false" ID th.hidden data-column-id="slug" data-visible="false" slug th data-column-id="title" data-formatter="title" data-searchable="true" data-width="50%" = t("activerecord.attributes.test.title") th data-column-id="project" data-searchable="false" = t("common.project") th data-column-id="updated" data-converter="date" data-searchable="false" data-order="desc" = t("common.updated_on") tbody - @tests.reorder("updated_at DESC").each do |test| tr td.hidden = test.id td.hidden = test.slug td = link_to test.title, test td = "#{test.project.team.name}##{test.project.name}" if test.project td = test.updated_at.strftime("%Y-%m-%d %H:%M:%S") ================================================ FILE: app/views/devise/confirmations/new.html.slim ================================================ - if devise_error_messages!.present? .card.card-alert = devise_error_messages! .card.card-form = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| h1.card-title = t(".resend_confirmation") .card-body .form-group = f.email_field :email, class: "form-control input-lg", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email), autofocus: true, placeholder: t("activerecord.attributes.user.email"), required: true = f.submit t(".send_mail"), class: "btn btn-lg color-primary btn-block", data: { disable_with: "Processing" } ================================================ FILE: app/views/devise/mailer/confirmation_instructions.en.html.slim ================================================ p = "Welcome #{@email} !" p You can confirm your account email through the link below: p = link_to "Confirm my account", confirmation_url(@resource, confirmation_token: @token) ================================================ FILE: app/views/devise/mailer/confirmation_instructions.ja.html.slim ================================================ p = "ようこそ #{@email} さん。" p 下記のリンクをクリックして、ユーザ登録を完了してください。 p = link_to "メールアドレスを認証", confirmation_url(@resource, confirmation_token: @token) ================================================ FILE: app/views/devise/mailer/password_change.html.erb ================================================

Hello <%= @resource.email %>!

We're contacting you to notify you that your password has been changed.

================================================ FILE: app/views/devise/mailer/reset_password_instructions.en.html.slim ================================================ p = "Hello #{@resource.email} !" p Someone has requested a link to change your password. You can do this through the link below. p = link_to "Change my password", edit_password_url(@resource, reset_password_token: @token) p If you didn't request this, please ignore this email. p Your password won't change until you access the link above and create a new one. ================================================ FILE: app/views/devise/mailer/reset_password_instructions.ja.html.slim ================================================ p = "こんにちは #{@resource.email} さん。" p パスワードリセットのリクエストが行われました。
下記のリンクからパスワードを変更することができます。 p = link_to "パスワードを再設定", edit_password_url(@resource, reset_password_token: @token) p もしこのメールに心当たりがない場合は、無視して下さい。
上記リンクをクリックして新しいパスワードを作成しない限り、パスワードは変更されません。 ================================================ FILE: app/views/devise/passwords/new.html.slim ================================================ - if devise_error_messages!.present? .card.card-alert = devise_error_messages! .card.card-form = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| h1.card-title = t(".forgot_password") .card-body .form-group = f.email_field :email, class: "form-control input-lg", autofocus: true, placeholder: t("activerecord.attributes.user.email"), required: true = f.submit t(".send_mail"), class: "btn btn-lg btn-block color-primary", data: { disable_with: "Processing" } ================================================ FILE: app/views/devise/registrations/new.html.slim ================================================ - if devise_error_messages!.present? .card.card-alert = devise_error_messages! .card.card-form = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| h1.card-title = t(".sign_up") .card-body .form-group = f.email_field :email, class: "form-control input-lg", autofocus: true, placeholder: t("activerecord.attributes.user.email"), required: true .form-group = f.password_field :password, class: "form-control input-lg", autocomplete: "off", placeholder: t("activerecord.attributes.user.password"), required: true .form-group = f.password_field :password_confirmation, class: "form-control input-lg", autocomplete: "off", placeholder: t("activerecord.attributes.user.password_confirmation"), required: true = f.submit t(".sign_up"), class: "btn btn-lg color-primary btn-block", data: { disable_with: "Processing" } .text-left = link_to t(".not_receive"), new_confirmation_path(resource_name) ================================================ FILE: app/views/devise/sessions/new.html.slim ================================================ .card.card-form = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| h1.card-title = t(".sign_in") .card-body .form-group = f.email_field :email, class: "form-control input-lg", autofocus: true, placeholder: t("activerecord.attributes.user.email"), required: true .form-group = f.password_field :password, class: "form-control input-lg", autocomplete: "off", placeholder: t("activerecord.attributes.user.password"), required: true = f.submit t(".sign_in"), class: "btn btn-lg color-primary btn-block" .text-left = link_to t(".remember_password"), new_password_path(resource_name) ================================================ FILE: app/views/devise/shared/_links.html.erb ================================================ <%- if controller_name != 'sessions' %> <%= link_to "Log in", new_session_path(resource_name) %>
<% end -%> <%- if devise_mapping.registerable? && controller_name != 'registrations' %> <%= link_to "Sign up", new_registration_path(resource_name) %>
<% end -%> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> <%= link_to "Forgot your password?", new_password_path(resource_name) %>
<% end -%> <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
<% end -%> <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
<% end -%> <%- if devise_mapping.omniauthable? %> <%- resource_class.omniauth_providers.each do |provider| %> <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
<% end -%> <% end -%> ================================================ FILE: app/views/layouts/_dashboard_sidebar.html.slim ================================================ .sidebar.team-sidebar .sidebar-title.dropdown a.dropdown-toggle aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button" .team-name span = t("dashboard.index.title") = fa_icon "angle-down" span.username = current_user.display_name = render "layouts/main_menu" ul.sidebar-list li class="sidebar-list-item active" = link_to t(".your_test"), dashboard_path - if current_user.teams.size == 0 .coach-mark = fa_icon("hand-o-left", text: t(".coach_mark")) ================================================ FILE: app/views/layouts/_logo.html.slim ================================================ / = link_to "Chibineko", root_url, class: "logo" ================================================ FILE: app/views/layouts/_main_menu.html.slim ================================================ ul.dropdown-menu style="top: 44px; left: 18px;" li.dropdown-header = t("common.you") li = link_to fa_icon("cog", text: t(".account_settings")), user_settings_edit_path, remote: true li = render "layouts/sign_out_link" - if current_team li.dropdown-header = t("common.team") li = link_to fa_icon("cog", text: t(".team_settings")), settings_team_path(current_team), remote: true li = link_to fa_icon("smile-o", text: t(".edit_members")), team_team_users_path(current_team), remote: true ================================================ FILE: app/views/layouts/_navbar.html.slim ================================================ - if controller_action == "static_pages#top" nav.navbar .container-fluid = link_to "Chibineko", root_url, class: "logo" ul.nav.navbar-nav.navbar-right li = link_to fa_icon("github-alt", text: t(".github_page")), "https://github.com/tabbyz/chibineko" - if user_signed_in? li.dropdown a href="#" class="dropdown-toggle" data-toggle="dropdown" data-container="body" role="button" aria-expanded="false" = current_user.display_name span.caret ul.dropdown-menu.dropdown-menu-right role="menu" li = link_to fa_icon("home", text: t("dashboard.index.title")), dashboard_path li = render "layouts/sign_out_link" - else li = render "layouts/sign_in_link" - else nav.navbar.navbar-fixed-top .container-fluid = link_to "Chibineko", root_url, class: "logo" ul.nav.navbar-nav.pull-right - if user_signed_in? li = render "layouts/sign_out_link" - else li = render "layouts/sign_in_link" li = render "layouts/sign_up_link" ================================================ FILE: app/views/layouts/_root_sidebar.html.slim ================================================ .sidebar.root-sidebar = link_to fa_icon("home 2x"), dashboard_path, class: "home-btn", data: { toggle: "tooltip", placement: "right", title: t("dashboard.index.title") } ul.hover-scroll / li = link_to fa_icon("home 2x"), dashboard_path, class: "home-btn", data: { toggle: "tooltip", placement: "right", title: t("dashboard.index.title") } - current_user.teams.reorder("name").each do |team| li class="#{'active' if team == current_team}" = link_to team_path(team), class: "team-btn", title: team.name do .hilight-bar span.symbol = team.name.first.upcase span.description = team.name = link_to fa_icon("plus-circle", right: true), new_team_path, class: "add-team-btn", data: { toggle: "tooltip", placement: "right", title: t(".create_team") }, remote: true ================================================ FILE: app/views/layouts/_sign_in_link.html.slim ================================================ = link_to t("common.button.sign_in"), new_user_session_path ================================================ FILE: app/views/layouts/_sign_out_link.html.slim ================================================ = link_to fa_icon("sign-out", text: t("common.button.sign_out")), destroy_user_session_path, method: :delete ================================================ FILE: app/views/layouts/_sign_up_link.html.slim ================================================ = link_to t("common.button.sign_up"), new_user_registration_path, class: "btn color-blue sign-up" ================================================ FILE: app/views/layouts/_team_sidebar.html.slim ================================================ .sidebar.team-sidebar .sidebar-title.dropdown a.dropdown-toggle aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button" .team-name span = current_team.try(:name) = fa_icon "angle-down" span.username = current_user.display_name = render "layouts/main_menu" .list-header span = t("common.project") = link_to fa_icon("plus-circle"), new_team_project_path(current_team), class: "add-project-btn", data: { toggle: "tooltip", placement: "top", title: t("common.button.create_new_project") }, remote: true ul.sidebar-list.hover-scroll - current_team.projects.reorder("name").each do |project| li class="#{'active' if project == current_project}" = link_to project.name, team_project_path(team_name: current_team.name, project_name: project.name) ================================================ FILE: app/views/layouts/application.html.slim ================================================ doctype html html head title = page_title(yield :title) - if controller_action == "static_pages#top" meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" = stylesheet_link_tag "application", media: "all" = favicon_link_tag = include_gon = javascript_include_tag "application" = javascript_include_tag "translations" javascript: I18n.defaultLocale = "#{I18n.default_locale}" I18n.locale = "#{I18n.locale}" I18n.fallbacks = true = csrf_meta_tags body class="#{controller_name} #{action_name}" - if flash - flash.each do |f| - type = f[0].to_s.gsub("alert", "warning").gsub("notice", "info") javascript: toastr["#{type}"]("#{f[1]}") - if controller_name.in?(%w"dashboard teams projects tests") - if user_signed_in? .page.page-with-sidebar = render "layouts/root_sidebar" = current_team ? render("layouts/team_sidebar") : render("layouts/dashboard_sidebar") .contents == yield - else .page .contents == yield - else = render "layouts/navbar" .page .contents == yield .modal.fade tabindex="-1" role="dialog" aria-hidden="true" ================================================ FILE: app/views/projects/_navbar.html.slim ================================================ nav.navbar.navbar-fixed-top.navbar-with-sidebar .navbar-content .navbar-title .dropdown a.dropdown-toggle aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button" = current_project.try(:name) = fa_icon "angle-down" ul.dropdown-menu li.dropdown-header = t("common.project") li = link_to fa_icon("cog", text: t(".menu.project_settings")), settings_team_project_path(current_team, current_project), remote: true = link_to fa_icon("plus", text: t("common.button.new_test")), new_test_path(team_name: current_team.try(:name), project_name: current_project.try(:name)), class: "btn color-blue new-test-btn pull-right", data: { toggle: "tooltip", placement: "bottom", title: t("common.button.create_new_test") } ================================================ FILE: app/views/projects/_new.html.slim ================================================ .new-project-modal.modal-dialog.modal-sm .modal-content = form_for @project, url: team_projects_path, remote: true do |f| .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body .form-group = f.text_field :name, class: "form-control input-lg validation", placeholder: t(".enter_name"), autofocus: true, maxlength: 30, required: true p.text-danger.error-message .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.cancel") = f.submit class: "btn btn-lg color-primary" ================================================ FILE: app/views/projects/_save.js.erb ================================================ $(".modal").modal("hide") $(window.location.replace("<%= team_project_path(@project.team.name, @project.name) %>")) ================================================ FILE: app/views/projects/_settings.html.slim ================================================ .project-settings-modal.modal-dialog .modal-content .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body ul.setting-list li = link_to t("common.button.expand"), "#editName", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "editName" } p.accordion-title = t(".name.title") p.accordion-description = t(".name.current_value_html", name: current_project.name) .collapse#editName = form_for current_project, url: team_project_path, remote: true do |f| .form-group.inline-block = f.text_field :name, class: "form-control validation", style: "width:250px;", maxlength: 255, required: true = button_tag class: "btn color-green ladda-button", data: { style: "expand-right" } do = content_tag :span, t("helpers.submit.update"), class: "ladda-label" li = link_to t("common.button.expand"), "#deleteProject", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "deleteProject" } p.accordion-title.text-red = t(".delete.title") .collapse#deleteProject p.accordion-description = t(".delete.description") = link_to t(".delete.delete_project"), team_project_path(current_team.name, current_project.name), class: "btn btn-sm btn-danger", data: { confirm: t("messages.delete_confirm") }, :method => :delete .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.close") ================================================ FILE: app/views/projects/create.js.erb ================================================ <%= render 'save' %> ================================================ FILE: app/views/projects/new.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'new') %>") $(".modal").modal("show") ================================================ FILE: app/views/projects/settings.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'settings') %>") Ladda.bind(".ladda-button") $(".modal").modal("show") ================================================ FILE: app/views/projects/show.html.slim ================================================ - provide :title, "#{@project.team.name}##{@project.name}" = render "navbar" - if @project.tests.size == 0 .empty-data = fa_icon("inbox", class: "symbol") h3 = t(".empty_message") = link_to fa_icon("plus", text: t("common.button.create_new_test")), new_test_path(team_name: current_team.try(:name), project_name: current_project.try(:name)), class: "btn btn-lg color-blue" - else = render "tests/table_toolbar" table.table.table-condensed.table-hover.data-table style="table-layout:fixed;" thead tr th.hidden data-column-id="id" data-type="numeric" data-identifier="true" data-visible="false" ID th.hidden data-column-id="slug" data-visible="false" slug th data-column-id="title" data-formatter="title" data-searchable="true" data-width="50%" = t("activerecord.attributes.test.title") th data-column-id="created" data-searchable="false" = t("common.created_by") th data-column-id="updated" data-converter="date" data-searchable="false" data-order="desc" = t("common.updated_on") tbody - @tests.reorder("updated_at DESC").try(:each) do |test| tr td.hidden = test.id td.hidden = test.slug td = link_to test.title, test td = test.user.display_name td = test.updated_at.strftime("%Y-%m-%d %H:%M:%S") ================================================ FILE: app/views/projects/update.js.erb ================================================ <%= render 'save' %> ================================================ FILE: app/views/static_pages/terms.html.slim ================================================ .card.card-lg h1.card-title = t(".title") .card-body = t(".body_html") ================================================ FILE: app/views/static_pages/top.html.slim ================================================ .container-fluid .row.branding.text-center h1 = t(".branding.title") p = t(".branding.sub_title") - if user_signed_in? = link_to t(".branding.get_started"), dashboard_path, class: "btn-ghost" - else = link_to t(".branding.sign_up_free"), new_user_registration_path, class: "btn-ghost" .browser-outline .browser-header ul.nav.browser-btn - %w(red yellow green).each do |color| li = fa_icon("circle", class: color) #carousel-screenshot.carousel.slide data-ride="carousel" ol.carousel-indicators li.active data-slide-to="0" data-target="#carousel-screenshot" li data-slide-to="1" data-target="#carousel-screenshot" li data-slide-to="2" data-target="#carousel-screenshot" .carousel-inner role="listbox" - %w(screenshot_1.png screenshot_2.png screenshot_3.png).each_with_index do |file, i| div class="item #{'active' if i == 1}" img alt=("First") data-holder-rendered="true" data-src=("First slide") src="images/#{file}" / a.left.carousel-control data-slide="prev" href="#carousel-screenshot" role="button" span.glyphicon.glyphicon-chevron-left aria-hidden="true" span.sr-only Previous a.right.carousel-control data-slide="next" href="#carousel-screenshot" role="button" span.glyphicon.glyphicon-chevron-right aria-hidden="true" span.sr-only Next section.row.feature h2 = t(".feature.title") .container row .col-md-3.col-sm-6.col-xs-12.feature-item .feature-icon = fa_icon("bar-chart") .feature-title = t(".feature.test_management_title") .feature-content = t(".feature.test_management_body") .col-md-3.col-sm-6.col-xs-12.feature-item .feature-icon = fa_icon("group") .feature-title = t(".feature.team_management_title") .feature-content = t(".feature.team_management_body") .col-md-3.col-sm-6.col-xs-12.feature-item .feature-icon = fa_icon("share") .feature-title = t(".feature.share_result_title") .feature-content = t(".feature.share_result_body") .col-md-3.col-sm-6.col-xs-12.feature-item .feature-icon = fa_icon("code-fork") .feature-title = t(".feature.oss_title") .feature-content = t(".feature.oss_body") section.row.section.support h2 = t(".support") ul li = mail_to "support@chibineko.jp", fa_icon("envelope-o", text: "Email"), encode: "hex" li = link_to fa_icon("github-alt", text: "GitHub Issues"), "https://github.com/tabbyz/chibineko/issues" li = link_to fa_icon("twitter", text: "Twitter"), "https://twitter.com/chibinekojp" .row.footer ul.list-inline li = link_to t(".terms"), terms_path li = link_to t(".releases"), "https://github.com/tabbyz/chibineko/releases" span.copyright = link_to "© 2015 SHIFT, Inc.", "http://www.shiftinc.jp/", target: "blank" ================================================ FILE: app/views/team_users/_index.html.slim ================================================ .team-users-modal.modal-dialog.modal-sm .modal-content .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body .search-user = form_for @team_user, url: team_team_users_path, remote: true do |f| = hidden_field_tag :team_name, @team.name .form-group label = t(".search_user") = email_field_tag :email, nil, class: "form-control", placeholder: "name@domain.com", required: true = button_tag type: "submit", class: "btn color-green ladda-button", data: { style: "expand-right" } do = fa_icon "chevron-down" = content_tag :span, t(".add_user"), class: "ladda-label" table.table.table-condensed thead tr th = t("activerecord.attributes.user.email").capitalize th = t("activerecord.attributes.user.name").capitalize th tbody - @team_users.each do |team_user| tr td = team_user.user.email td = team_user.user.username td - if team_user.user != current_user = form_for team_user, url: team_team_user_path(@team, team_user), :html => { :method => :delete }, remote: true do |form| = button_tag type: "submit", class: "btn-link", data: { confirm: t(".delete_confirm") } do = fa_icon "close" .modal-footer button.btn.btn-lg.btn-default.pull-right type="button" data-dismiss="modal" = t("dialog.done") ================================================ FILE: app/views/team_users/_save.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'index') %>") Ladda.bind(".ladda-button") ================================================ FILE: app/views/team_users/create.js.erb ================================================ <%= render 'save' %> ================================================ FILE: app/views/team_users/destroy.js.erb ================================================ <%= render 'save' %> ================================================ FILE: app/views/team_users/index.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'index') %>") Ladda.bind(".ladda-button") $(".modal").modal("show") ================================================ FILE: app/views/teams/_new.html.slim ================================================ .new-team-modal.modal-dialog.modal-sm .modal-content = form_for @team, remote: true do |f| .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body .form-group = f.text_field :name, class: "form-control input-lg validation", placeholder: t(".enter_name"), autofocus: true, maxlength: 30, required: true p.text-danger.error-message .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.cancel") = f.submit class: "btn btn-lg color-primary" ================================================ FILE: app/views/teams/_save.js.erb ================================================ $(".modal").modal("hide") $(window.location.replace("<%= team_path(@team.name) %>")) ================================================ FILE: app/views/teams/_settings.html.slim ================================================ .team-settings-modal.modal-dialog .modal-content .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body ul.setting-list li = link_to t("common.button.expand"), "#editName", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "editName" } p.accordion-title = t(".name.title") p.accordion-description = t(".name.current_value_html", name: current_team.name) .collapse#editName = form_for current_team, remote: true do |f| .form-group.inline-block = f.text_field :name, class: "form-control validation", style: "width:250px;", maxlength: 255, required: true = button_tag class: "btn color-green ladda-button", data: { style: "expand-right" } do = content_tag :span, t("helpers.submit.update"), class: "ladda-label" li = link_to t("common.button.expand"), "#deleteTeam", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "deleteTeam" } p.accordion-title.text-red = t(".delete.title") .collapse#deleteTeam p.accordion-description = t(".delete.description") = link_to t(".delete.delete_team"), current_team, class: "btn btn-sm btn-danger", data: { confirm: t("messages.delete_confirm") }, :method => :delete .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.close") ================================================ FILE: app/views/teams/create.js.erb ================================================ <%= render 'save' %> ================================================ FILE: app/views/teams/new.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'new') %>") $(".modal").modal("show") ================================================ FILE: app/views/teams/settings.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'settings') %>") Ladda.bind(".ladda-button") $(".modal").modal("show") ================================================ FILE: app/views/teams/show.html.slim ================================================ - provide :title, @team.name - if @team.projects.size == 0 .empty-data = fa_icon("inbox", class: "symbol") h3 = t(".empty_message") = link_to fa_icon("plus", text: t("common.button.create_new_project")), new_team_project_path(current_team), class: "btn btn-lg color-blue add-project-btn", remote: true ================================================ FILE: app/views/teams/update.js.erb ================================================ <%= render 'save' %> ================================================ FILE: app/views/tests/_alert.html.slim ================================================ - if @test.user && @test.project.nil? .alert.alert-info p = t("messages.public_test_html") - if @test.user.nil? .alert.alert-warning p = t("messages.created_by_guest_html") p span = t("messages.please_association_html") = link_to t("messages.attach_current_user"), user_association_test_path, class: "btn btn-sm color-primary", :method => :patch if user_signed_in? ================================================ FILE: app/views/tests/_cheatsheet.en.html.slim ================================================ .cheatsheet p.cheatsheet-header = fa_icon("question-circle", text: "Tips: how to create test cases") .cheatsheet-block p.cheatsheet-title ・Your listed contents can be your test items as it is p.cheatsheet-body To be able to register an user
To be able to sign in .cheatsheet-block p.cheatsheet-title ・Group items by adding "#" at the beginning of a line p.cheatsheet-body # User management function
## User registration
To be able to register an user
## Sign in
To be able to log in with correct ID and Password
To be unable to log in with wrong ID and Password ================================================ FILE: app/views/tests/_cheatsheet.ja.html.slim ================================================ .cheatsheet p.cheatsheet-header = fa_icon("question-circle", text: "書き方のヒント") .cheatsheet-block p.cheatsheet-title ・箇条書きにした内容がそのままテスト項目になります p.cheatsheet-body ユーザー登録できること
ログインできること .cheatsheet-block p.cheatsheet-title ・行の先頭に # を記入するとグループ化することができます p.cheatsheet-body # ユーザー管理機能
## ユーザー登録
新規登録できること
## ログイン
正しいID/PASSでログインできること
誤ったID/PASSではエラーが表示されること ================================================ FILE: app/views/tests/_description.html.slim ================================================ .edit-description-modal.modal-dialog.modal-lg .modal-content = form_for @test, remote: true do |f| .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body = f.text_area :description, class: "form-control", autofocus: true, rows: 20, maxlength: 4096 .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.cancel") = f.submit t("helpers.submit.submit"), class: "btn btn-lg color-primary" ================================================ FILE: app/views/tests/_form.html.slim ================================================ = render "navbar" = form_for @test, remote: true do |f| = f.hidden_field :source = f.hidden_field :team_name, value: current_team.try(:name) = f.hidden_field :project_name, value: current_project.try(:name) .test-title = f.text_field :title, class: "form-control input-lg", placeholder: t(".title_placeholder"), autofocus: (action_name == "new" ? true : false), maxlength: 255, required: true .test-editor .test-markdown .test-editor-header p = t(".markdown_header") = f.text_area :markdown, class: "form-control", placeholder: t(".markdown_placeholder"), autofocus: (action_name == "edit" ? true : false), required: true .test-preview .test-editor-header p = t(".preview_header") .test-preview-content .test-cheatsheet style="display:none;" = render "cheatsheet" footer = f.submit class: "btn btn-lg color-primary pull-right" ================================================ FILE: app/views/tests/_move.html.slim ================================================ .move-test-modal.modal-dialog.modal-xs .modal-content = form_for @test, remote: true do |f| = f.hidden_field :project_id, class: "selected-id-field" .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body .project-list ul.nav li class="unset-project clickable #{'active' if current_project.nil?}" = t(".unset_project") - @teams.each do |team| - if team.projects.size != 0 p.team-name = team.name ul.nav - team.projects.each do |project| li data-project-id="#{project.id}" class="project-name clickable #{'active' if project == current_project}" = project.name .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.cancel") = f.submit t(".submit"), class: "btn btn-lg color-primary" ================================================ FILE: app/views/tests/_navbar.html.slim ================================================ nav.navbar.navbar-fixed-top.navbar-with-sidebar .navbar-content - if action_name == "show" = link_to fa_icon("pencil", text: t(".edit_test")), edit_test_path(@test), class: "btn color-blue edit-test-btn #{'disabled' unless @test.user}", data: { toggle: "tooltip", placement: "bottom", title: t(".edit_current_test") } - elsif action_name.in?(%w"new edit") .navbar-title span = t("tests.form.title.#{action_name}") ul.nav.navbar-nav.navbar-right - if action_name == "show" li = link_to fa_icon("line-chart"), "#", class: "btn progress-count", data: { toggle: "popover", placement: "bottom", trigger: "hover", html: "true", content: "" } li = link_to fa_icon("download"), test_path(format: :csv), class: "btn", data: { toggle: "tooltip", placement: "bottom", title: t(".export_csv") } li = link_to fa_icon("copy"), new_test_path(team_name: current_team.try(:name), project_name: current_project.try(:name), source: @test.slug), class: "btn", data: { toggle: "tooltip", placement: "bottom", title: t(".duplicate_test") } li = link_to fa_icon("file-o"), new_test_path(team_name: current_team.try(:name), project_name: current_project.try(:name)), class: "btn", data: { toggle: "tooltip", placement: "bottom", title: t("common.button.create_new_test") } - if @test.user - if @test.project || @test.user == current_user li.dropdown a.btn data-target="#" href="#" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" = fa_icon "cog" ul.dropdown-menu li.dropdown-header = t(".test_settings") li = link_to fa_icon("external-link", text: t(".move_to")), move_test_path(@test), remote: true li = link_to fa_icon("pencil", text: t(".customize_result_label")), result_label_test_path(@test), remote: true li.divider role="separator" li = link_to fa_icon("trash-o", text: t(".delete_test")), @test, class: "text-red", data: { confirm: t("messages.delete_confirm") }, :method => :delete ================================================ FILE: app/views/tests/_result_label.html.slim ================================================ .edit-result-label-modal.modal-dialog.modal-xs .modal-content = form_for @test, url: update_result_label_test_path, remote: true do |f| .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body .checkbox = f.label :use_default do = f.check_box :use_default, class: "user-default-result-label", checked: (@test.result_labels.nil? ? true : false) = t(".use_default_label") table style=("display: none;" if @test.result_labels.nil?) tbody#resultLabelList - @test.result_labels_or_default.each do |k, v| tr td.drag-handle style="width:30px;" = fa_icon "bars", class: "drag-handle-icon" td.result-label-text = text_field_tag "test[result_label_texts][]", k, { id: nil, placeholder: t(".label_placeholder"), maxlength: 10, required: true } td.result-label-color style="width:40px;" input class="btn select-color color-#{v}" name="test[result_label_colors][]" value="#{v}" type="text" tabindex="0" role="button" data-toggle="popover" data-trigger="focus" data-placement="left" td.delete-result-label style="width:40px;" = fa_icon("close", class: "delete-result-label-icon") .over .table-footer style=("display: none;" if @test.result_labels.nil?) = fa_icon("plus", class: "add-result-label-icon") .popover-content-template .color-picker - color_names.each do |color| input type="radio" name="color-radio" id="radio-#{color}" value="#{color}" label for="radio-#{color}" class="btn color-item color-#{color}" .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.cancel") = f.submit t("helpers.submit.submit"), class: "btn btn-lg color-primary" ================================================ FILE: app/views/tests/_table_toolbar.html.slim ================================================ .table-toolbar span.selected-count ul.nav.navbar-nav.navbar-right li = link_to fa_icon("trash-o", text: t("common.button.delete")), "#", class: "btn text-red bulk-destroy-btn" li.dropdown a.btn data-target="#" href="#" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" = fa_icon "external-link", text: t("common.button.move") ul.dropdown-menu li = link_to t("tests.move.unset_project"), "#", class: "bulk-move-btn" - current_user.teams.includes(:projects).each do |team| li.dropdown-header = team.name - team.projects.each do |project| li = link_to project.name, "#", class: "bulk-move-btn", data: { project_id: project.id } ================================================ FILE: app/views/tests/_testcase.html.slim ================================================ .testcase-toolbar button type="button" class="btn-link js-collapse-all-btn" = fa_icon "caret-down", text: t(".collapse_all"), class: "collapse-icon" button type="button" class="btn-link js-expand-all-btn" style="display:none;" = fa_icon "caret-right", text: t(".expand_all"), class: "expand-icon" .testcase-list - case_id = 0 - @test.testcase_groups.each do |group| .testcase-group - group.each do |c| - case c.type - when :heading div class="tr heading heading-level-#{c.heading_level}" data-heading-level="#{c.heading_level}" .td.body = fa_icon "caret-down", class: "collapse-icon" = fa_icon "caret-right", class: "expand-icon" button type="button" class="btn-link js-collapse-btn" = c.body - when :testcase .tr.testcase .td.number = case_id += 1 .td.body = c.body .td.result .btn-group.result-btn-group = button_tag c.result, type: "button", class: "btn result-btn color-#{c.result_color}", data: { "case-id" => c.id } = button_tag type: "button", class: "btn dropdown-toggle color-#{c.result_color}", data: { toggle: "dropdown" } do span.caret span.sr-only Toggle Dropdown ul.dropdown-menu.pull-right role="menu" - @test.result_label_texts.each do |label| li p.result-item = label .td.note = best_in_place c, :note, url: test_testcase_path(test_slug: @test.slug, id: c.id), place_holder: t(".note"), html_attrs: { maxlength: 1024 }, data: { toggle: "tooltip", placement: "top", "original-title" => c.note } ================================================ FILE: app/views/tests/bulk_destroy.js.erb ================================================ window.location.reload() ================================================ FILE: app/views/tests/bulk_move.js.erb ================================================ window.location.reload() ================================================ FILE: app/views/tests/create.js.erb ================================================ $(".modal").modal("hide") $(window.location.replace("<%= test_path(@test.slug) %>")) ================================================ FILE: app/views/tests/description.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'description') %>") $(".modal").modal("show") ================================================ FILE: app/views/tests/edit.html.slim ================================================ = render "form" ================================================ FILE: app/views/tests/move.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'move') %>") $(".modal").modal("show") ================================================ FILE: app/views/tests/new.html.slim ================================================ = render "form" ================================================ FILE: app/views/tests/result_label.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'result_label') %>") $(".modal").modal("show") Sortable.create(resultLabelList, { handle: ".drag-handle" }); $(".select-color").popover({ html : true, content: $(".popover-content-template").html() }) ================================================ FILE: app/views/tests/show.csv.ruby ================================================ require 'csv' testcases = @test.testcases # Examine the maximum heading level max_level = 0 testcases.each do |c| if c.type == :heading max_level = c.heading_level if c.heading_level > max_level end end headings = Array.new(max_level) # Header header = Array.new(max_level, "-") header << I18n.t("tests.csv.case") << I18n.t("tests.csv.result") << I18n.t("tests.csv.note") # Generate csv CSV.generate(headers: header, write_headers: true, force_quotes: true) do |csv| # CSV.generate do |csv| testcases.each do |c| case c.type when :heading # Clear unnecessary column length = max_level - c.heading_level headings[c.heading_level, length] = Array.new(length, "") # Set value to column headings[c.heading_level - 1] = c.body when :testcase row = Array.new(headings) row << c.body << c.result << c.note csv << row end end end ================================================ FILE: app/views/tests/show.html.slim ================================================ - provide :title, @test.title - if !user_signed_in? = render "layouts/navbar" - else = render "navbar" .progress.progress-animation class=(user_signed_in? ? "margin-sidebar" : "") - @test.result_labels_or_default.drop(1).push(@test.result_labels_or_default.first).each do |k, v| div class="progress-bar progress-bar-xs color-#{v}" style="width: 0%; transition: width 0.5s ease;" data-result-text="#{k}" = render "alert" h3.test-title = @test.title .test-description - if @test.description.blank? p.placeholder = t(".blank_description") - else = auto_link simple_format(html_escape(@test.description)), :html => { target: "_blank" } .tool-menu = link_to fa_icon("pencil"), description_test_path(@test), class: "btn", data: { toggle: "tooltip", placement: "left", title: t("tests.description.title") }, remote: true if @test.user && user_signed_in? .test-detail span = t(".created_by", username: @test.user ? @test.user.display_name : t("common.guest"), updated_at: l(@test.updated_at, format: :short)) = render "testcase" ================================================ FILE: app/views/tests/update.js.erb ================================================ $(".modal").modal("hide") $(window.location.replace("<%= test_path(@test.slug) %>")) ================================================ FILE: app/views/tests/update_result_label.js.erb ================================================ $(".modal").modal("hide") $(window.location.reload()) ================================================ FILE: app/views/user_settings/_modal.html.slim ================================================ .account-settings-modal.modal-dialog .modal-content .modal-header button.close type="button" data-dismiss="modal" aria-label="Close" span aria-hidden="true" × h4.modal-title = t(".title") .modal-body ul.setting-list li = link_to t("common.button.expand"), "#editUsername", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "editUsername" } p.accordion-title = t(".username.title") p.accordion-description - if @user.username.blank? = t(".username.blank") - else = t(".username.current_value_html", username: @user.username) .collapse#editUsername = form_for @user, url: user_settings_update_path, remote: true do |f| .form-group.inline-block = f.text_field :username, class: "form-control validation", style: "width:200px;", maxlength: 255, required: true = button_tag class: "btn color-green ladda-button", data: { style: "expand-right" } do = content_tag :span, t("helpers.submit.submit"), class: "ladda-label" li = link_to t("common.button.expand"), "#editPassword", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "editPassword" } p.accordion-title = t(".password.title") p.accordion-description = t(".password.change_password") .collapse#editPassword = form_for @user, as: "user", url: registration_path("user"), html: { method: :put }, remote: true do |f| .form-group.inline-block label for="user_current_password" = t(".password.current_password") = f.password_field :current_password, class: "form-control validation", style: "width:200px; display:block;", required: true br .form-group.inline-block label for="user_password" = t(".password.new_password") = f.password_field :password, class: "form-control validation", style: "width:200px; display:block;", autocomplete: "off", required: true br .form-group.inline-block label for="user_password" = t(".password.confirm_password") = f.password_field :password_confirmation, class: "form-control validation", style: "width:200px; display:block;", autocomplete: "off", required: true br = button_tag class: "btn color-green ladda-button", data: { style: "expand-right" } do = content_tag :span, t(".password.update"), class: "ladda-label" li = link_to t("common.button.expand"), "#editTimezone", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "editTimezone" } p.accordion-title = t(".timezone.title") p.accordion-description = t(".timezone.current_value_html", timezone: @user.timezone) .collapse#editTimezone = form_for @user, url: user_settings_update_path, remote: true do |f| = f.time_zone_select :timezone, nil, { include_blank: false }, { class: "form-control", required: true } = button_tag class: "btn color-green ladda-button", data: { style: "expand-right" } do = content_tag :span, t(".timezone.update"), class: "ladda-label" li = link_to t("common.button.expand"), "#editLanguage", class: "btn color-lightgray collapse-btn", data: { toggle: "collapse" }, aria: { expanded: "false", controls: "editLanguage" } p.accordion-title = t(".language.title") p.accordion-description = t(".language.current_value_html", language: t("language_name")) .collapse#editLanguage = form_for @user, url: user_settings_update_path, remote: true do |f| = f.select :locale, { "English" => "en", "日本語" => "ja" }, {}, { class: "form-control", style: "width:200px;" } = button_tag class: "btn color-green ladda-button", data: { style: "expand-right" } do = content_tag :span, t("helpers.submit.submit"), class: "ladda-label" .modal-footer button.btn.btn-lg.btn-default type="button" data-dismiss="modal" = t("dialog.close") ================================================ FILE: app/views/user_settings/edit.js.erb ================================================ $(".modal").html("<%= escape_javascript(render 'modal') %>") Ladda.bind(".ladda-button") $(".modal").modal("show") ================================================ FILE: app/views/user_settings/update.js.erb ================================================ $(".modal").modal("hide") $(window.location.reload()) ================================================ FILE: bin/bundle ================================================ #!/usr/bin/env ruby ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) load Gem.bin_path('bundler', 'bundle') ================================================ FILE: bin/rails ================================================ #!/usr/bin/env ruby APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands' ================================================ FILE: bin/rake ================================================ #!/usr/bin/env ruby require_relative '../config/boot' require 'rake' Rake.application.run ================================================ FILE: bin/setup ================================================ #!/usr/bin/env ruby require 'pathname' # path to your application root. APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) Dir.chdir APP_ROOT do # This script is a starting point to setup your application. # Add necessary setup steps to this file: puts "== Installing dependencies ==" system "gem install bundler --conservative" system "bundle check || bundle install" # puts "\n== Copying sample files ==" # unless File.exist?("config/database.yml") # system "cp config/database.yml.sample config/database.yml" # end puts "\n== Preparing database ==" system "bin/rake db:setup" puts "\n== Removing old logs and tempfiles ==" system "rm -f log/*" system "rm -rf tmp/cache" puts "\n== Restarting application server ==" system "touch tmp/restart.txt" end ================================================ FILE: circle.yml ================================================ machine: ruby: version: 2.3.0 database: pre: - cp config/mailer.yml.example config/mailer.yml ================================================ FILE: config/application.rb ================================================ require File.expand_path('../boot', __FILE__) require "rails" # Pick the frameworks you want: require "active_model/railtie" require "active_job/railtie" require "active_record/railtie" require "action_controller/railtie" require "action_mailer/railtie" require "action_view/railtie" require "sprockets/railtie" # require "rails/test_unit/railtie" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module Chibineko class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de config.i18n.available_locales = %w(en ja) # Do not swallow errors in after_commit/after_rollback callbacks. config.active_record.raise_in_transactional_callbacks = true config.assets.initialize_on_precompile = true config.web_console.development_only = false end end ================================================ FILE: config/boot.rb ================================================ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' # Set up gems listed in the Gemfile. ================================================ FILE: config/database.yml ================================================ # SQLite version 3.x # gem install sqlite3 # # Ensure the SQLite 3 gem is defined in your Gemfile # gem 'sqlite3' # default: &default adapter: sqlite3 pool: 5 timeout: 5000 development: <<: *default database: db/development.sqlite3 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default database: db/test.sqlite3 production: <<: *default database: db/production.sqlite3 ================================================ FILE: config/environment.rb ================================================ # Load the Rails application. require File.expand_path('../application', __FILE__) # Initialize the Rails application. Rails.application.initialize! ================================================ FILE: config/environments/development.rb ================================================ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false # Do not eager load code on boot. config.eager_load = false # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. config.assets.debug = true # Asset digests allow you to set far-future HTTP expiration dates on all assets, # yet still be able to expire them through the digest params. config.assets.digest = true # Adds additional error checking when serving assets at runtime. # Checks for improperly declared sprockets dependencies. # Raises helpful error messages. config.assets.raise_runtime_errors = true # Raises error for missing translations # config.action_view.raise_on_missing_translations = true # Bullet settings config.after_initialize do Bullet.enable = true Bullet.alert = true Bullet.console = true Bullet.rails_logger = true end # Mailer settings. config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :letter_opener_web end ================================================ FILE: config/environments/production.rb ================================================ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. config.cache_classes = true # Eager load code on boot. This eager loads most of Rails and # your application in memory, allowing both threaded web servers # and those relying on copy on write to perform better. # Rake tasks automatically ignore this option for performance. config.eager_load = true # Full error reports are disabled and caching is turned on. config.consider_all_requests_local = false config.action_controller.perform_caching = true # Enable Rack::Cache to put a simple HTTP cache in front of your application # Add `rack-cache` to your Gemfile before enabling this. # For large-scale production use, consider using a caching reverse proxy like # NGINX, varnish or squid. # config.action_dispatch.rack_cache = true # Compress JavaScripts and CSS. config.assets.js_compressor = :uglifier # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false # Asset digests allow you to set far-future HTTP expiration dates on all assets, # yet still be able to expire them through the digest params. config.assets.digest = true # Disable Rails's static asset server (Apache or nginx will already do this). config.serve_static_files = true # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb # Specifies the header that your server uses for sending files. # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true # Use the lowest log level to ensure availability of diagnostic information # when problems arise. config.log_level = :debug # Prepend all log lines with the following tags. # config.log_tags = [ :subdomain, :uuid ] # Use a different logger for distributed setups. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) # Use a different cache store in production. # config.cache_store = :mem_cache_store # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.action_controller.asset_host = 'http://assets.example.com' # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false end ================================================ FILE: config/environments/test.rb ================================================ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped # and recreated between test runs. Don't rely on the data there! config.cache_classes = true # Do not eager load code on boot. This avoids loading your whole application # just for the purpose of running a single test. If you are using a tool that # preloads Rails for running tests, you may have to set it to true. config.eager_load = false # Configure static file server for tests with Cache-Control for performance. config.serve_static_files = true config.static_cache_control = 'public, max-age=3600' # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test # Randomize the order test cases are executed. config.active_support.test_order = :random # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr # Raises error for missing translations # config.action_view.raise_on_missing_translations = true end ================================================ FILE: config/i18n-js.yml ================================================ # Split context in several files. # By default only one file with all translations is exported and # no configuration is required. Your settings for asset pipeline # are automatically recognized. # # If you want to split translations into several files or specify # locale contexts that will be exported, just use this file to do # so. # # If you're going to use the Rails 3.1 asset pipeline, change # the following configuration to something like this: # # translations: # - file: "app/assets/javascripts/i18n/translations.js" # # If you're running an old version, you can use something # like this: # translations: - file: "public/javascripts/translations.js" only: "*.js" ================================================ FILE: config/initializers/action_mailer.rb ================================================ unless Chibineko::Application.config.action_mailer.nil? if options = YAML.load_file(Rails.root.join("config", "mailer.yml"))[Rails.env] Chibineko::Application.config.action_mailer.merge! options.deep_symbolize_keys! end end ================================================ FILE: config/initializers/assets.rb ================================================ # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. Rails.application.config.assets.version = '1.0' # Add additional assets to the asset load path # Rails.application.config.assets.paths << Emoji.images_path # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. # Rails.application.config.assets.precompile += %w( search.js ) ================================================ FILE: config/initializers/backtrace_silencers.rb ================================================ # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. # Rails.backtrace_cleaner.remove_silencers! ================================================ FILE: config/initializers/cookies_serializer.rb ================================================ # Be sure to restart your server when you modify this file. Rails.application.config.action_dispatch.cookies_serializer = :json ================================================ FILE: config/initializers/devise.rb ================================================ # Use this hook to configure devise mailer, warden hooks and so forth. # Many of these configuration options can be set straight in your model. Devise.setup do |config| # The secret key used by Devise. Devise uses this key to generate # random tokens. Changing this key will render invalid all existing # confirmation, reset password and unlock tokens in the database. # Devise will use the `secret_key_base` on Rails 4+ applications as its `secret_key` # by default. You can change it below and use your own secret key. # config.secret_key = '35175ed82fda77f2e1c26412bc6ae3d0c295fdfc00205063ab570e74ffc1b1a6217959602c468032b3fecbb119f517d9a3c144f9a43892585470c3c5f1a1fc5e' # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class # with default "from" parameter. config.mailer_sender = '"Chibineko" ' # Configure the class responsible to send e-mails. # config.mailer = 'Devise::Mailer' # ==> ORM configuration # Load and configure the ORM. Supports :active_record (default) and # :mongoid (bson_ext recommended) by default. Other ORMs may be # available as additional gems. require 'devise/orm/active_record' # ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating a user. The default is # just :email. You can configure it to use [:username, :subdomain], so for # authenticating a user, both parameters are required. Remember that those # parameters are used only when authenticating and not when retrieving from # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. # config.authentication_keys = [:email] # Configure parameters from the request object used for authentication. Each entry # given should be a request method and it will automatically be passed to the # find_for_authentication method and considered in your model lookup. For instance, # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. # The same considerations mentioned for authentication_keys also apply to request_keys. # config.request_keys = [] # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. config.case_insensitive_keys = [:email] # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or # modifying a user and when used to authenticate or find a user. Default is :email. config.strip_whitespace_keys = [:email] # Tell if authentication through request.params is enabled. True by default. # It can be set to an array that will enable params authentication only for the # given strategies, for example, `config.params_authenticatable = [:database]` will # enable it only for database (email + password) authentication. # config.params_authenticatable = true # Tell if authentication through HTTP Auth is enabled. False by default. # It can be set to an array that will enable http authentication only for the # given strategies, for example, `config.http_authenticatable = [:database]` will # enable it only for database authentication. The supported strategies are: # :database = Support basic authentication with authentication key + password # config.http_authenticatable = false # If 401 status code should be returned for AJAX requests. True by default. # config.http_authenticatable_on_xhr = true # The realm used in Http Basic Authentication. 'Application' by default. # config.http_authentication_realm = 'Application' # It will change confirmation, password recovery and other workflows # to behave the same regardless if the e-mail provided was right or wrong. # Does not affect registerable. # config.paranoid = true # By default Devise will store the user in session. You can skip storage for # particular strategies by setting this option. # Notice that if you are skipping storage for all authentication paths, you # may want to disable generating routes to Devise's sessions controller by # passing skip: :sessions to `devise_for` in your config/routes.rb config.skip_session_storage = [:http_auth] # By default, Devise cleans up the CSRF token on authentication to # avoid CSRF token fixation attacks. This means that, when using AJAX # requests for sign in and sign up, you need to get a new CSRF token # from the server. You can disable this option at your own risk. # config.clean_up_csrf_token_on_authentication = true # ==> Configuration for :database_authenticatable # For bcrypt, this is the cost for hashing the password and defaults to 10. If # using other encryptors, it sets how many times you want the password re-encrypted. # # Limiting the stretches to just one in testing will increase the performance of # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use # a value less than 10 in other environments. Note that, for bcrypt (the default # encryptor), the cost increases exponentially with the number of stretches (e.g. # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). config.stretches = Rails.env.test? ? 1 : 10 # Setup a pepper to generate the encrypted password. # config.pepper = '1dd38cee240f1b8caf5c7450a71fa6fc0053db3fc62d31706fcaf4b7135e03f616008ccc8124b934dedfd1d9b56306b441b9ef6cdd4a9f091355c0f2a4defa74' # Send a notification email when the user's password is changed # config.send_password_change_notification = false # ==> Configuration for :confirmable # A period that the user is allowed to access the website even without # confirming their account. For instance, if set to 2.days, the user will be # able to access the website for two days without confirming their account, # access will be blocked just in the third day. Default is 0.days, meaning # the user cannot access the website without confirming their account. # config.allow_unconfirmed_access_for = 2.days # A period that the user is allowed to confirm their account before their # token becomes invalid. For example, if set to 3.days, the user can confirm # their account within 3 days after the mail was sent, but on the fourth day # their account can't be confirmed with the token any more. # Default is nil, meaning there is no restriction on how long a user can take # before confirming their account. # config.confirm_within = 3.days # If true, requires any email changes to be confirmed (exactly the same way as # initial account confirmation) to be applied. Requires additional unconfirmed_email # db field (see migrations). Until confirmed, new email is stored in # unconfirmed_email column, and copied to email column on successful confirmation. config.reconfirmable = true # Defines which key will be used when confirming an account # config.confirmation_keys = [:email] # ==> Configuration for :rememberable # The time the user will be remembered without asking for credentials again. # config.remember_for = 2.weeks # Invalidates all the remember me tokens when the user signs out. config.expire_all_remember_me_on_sign_out = true # If true, extends the user's remember period when remembered via cookie. # config.extend_remember_period = false # Options to be passed to the created cookie. For instance, you can set # secure: true in order to force SSL only cookies. # config.rememberable_options = {} # ==> Configuration for :validatable # Range for password length. config.password_length = 4..72 # Email regex used to validate email formats. It simply asserts that # one (and only one) @ exists in the given string. This is mainly # to give user feedback and not to assert the e-mail validity. # config.email_regexp = /\A[^@]+@[^@]+\z/ # ==> Configuration for :timeoutable # The time you want to timeout the user session without activity. After this # time the user will be asked for credentials again. Default is 30 minutes. # config.timeout_in = 30.minutes # ==> Configuration for :lockable # Defines which strategy will be used to lock an account. # :failed_attempts = Locks an account after a number of failed attempts to sign in. # :none = No lock strategy. You should handle locking by yourself. # config.lock_strategy = :failed_attempts # Defines which key will be used when locking and unlocking an account # config.unlock_keys = [:email] # Defines which strategy will be used to unlock an account. # :email = Sends an unlock link to the user email # :time = Re-enables login after a certain amount of time (see :unlock_in below) # :both = Enables both strategies # :none = No unlock strategy. You should handle unlocking by yourself. # config.unlock_strategy = :both # Number of authentication tries before locking an account if lock_strategy # is failed attempts. # config.maximum_attempts = 20 # Time interval to unlock the account if :time is enabled as unlock_strategy. # config.unlock_in = 1.hour # Warn on the last attempt before the account is locked. # config.last_attempt_warning = true # ==> Configuration for :recoverable # # Defines which key will be used when recovering the password for an account # config.reset_password_keys = [:email] # Time interval you can reset your password with a reset password key. # Don't put a too small interval or your users won't have the time to # change their passwords. config.reset_password_within = 6.hours # When set to false, does not sign a user in automatically after their password is # reset. Defaults to true, so a user is signed in automatically after a reset. # config.sign_in_after_reset_password = true # ==> Configuration for :encryptable # Allow you to use another encryption algorithm besides bcrypt (default). You can use # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) # and :restful_authentication_sha1 (then you should set stretches to 10, and copy # REST_AUTH_SITE_KEY to pepper). # # Require the `devise-encryptable` gem when using anything other than bcrypt # config.encryptor = :sha512 # ==> Scopes configuration # Turn scoped views on. Before rendering "sessions/new", it will first check for # "users/sessions/new". It's turned off by default because it's slower if you # are using only default views. # config.scoped_views = false # Configure the default scope given to Warden. By default it's the first # devise role declared in your routes (usually :user). # config.default_scope = :user # Set this configuration to false if you want /users/sign_out to sign out # only the current scope. By default, Devise signs out all scopes. # config.sign_out_all_scopes = true # ==> Navigation configuration # Lists the formats that should be treated as navigational. Formats like # :html, should redirect to the sign in page when the user does not have # access, but formats like :xml or :json, should return 401. # # If you have any extra navigational formats, like :iphone or :mobile, you # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. # config.navigational_formats = ['*/*', :html] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :delete # ==> OmniAuth # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or # change the failure app, you can configure them inside the config.warden block. # # config.warden do |manager| # manager.intercept_401 = false # manager.default_strategies(scope: :user).unshift :some_external_strategy # end # ==> Mountable engine configurations # When using Devise inside an engine, let's call it `MyEngine`, and this engine # is mountable, there are some extra configurations to be taken into account. # The following options are available, assuming the engine is mounted as: # # mount MyEngine, at: '/my_engine' # # The router that invoked `devise_for`, in the example above, would be: # config.router_name = :my_engine # # When using OmniAuth, Devise cannot automatically set OmniAuth path, # so you need to do it manually. For the users scope, it would be: # config.omniauth_path_prefix = '/my_engine/users/auth' end ================================================ FILE: config/initializers/filter_parameter_logging.rb ================================================ # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. Rails.application.config.filter_parameters += [:password] ================================================ FILE: config/initializers/inflections.rb ================================================ # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections # are locale specific, and you may define rules for as many different # locales as you wish. All of these examples are active by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.plural /^(ox)$/i, '\1en' # inflect.singular /^(ox)en/i, '\1' # inflect.irregular 'person', 'people' # inflect.uncountable %w( fish sheep ) # end # These inflection rules are supported but not enabled by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.acronym 'RESTful' # end ================================================ FILE: config/initializers/mime_types.rb ================================================ # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: # Mime::Type.register "text/richtext", :rtf ================================================ FILE: config/initializers/session_store.rb ================================================ # Be sure to restart your server when you modify this file. Rails.application.config.session_store :cookie_store, key: '_chibineko_session' ================================================ FILE: config/initializers/time_formats.rb ================================================ ================================================ FILE: config/initializers/wrap_parameters.rb ================================================ # Be sure to restart your server when you modify this file. # This file contains settings for ActionController::ParamsWrapper which # is enabled by default. # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. ActiveSupport.on_load(:action_controller) do wrap_parameters format: [:json] if respond_to?(:wrap_parameters) end # To enable root element in JSON for ActiveRecord objects. # ActiveSupport.on_load(:active_record) do # self.include_root_in_json = true # end ================================================ FILE: config/locales/devise.en.yml ================================================ # Additional translations at https://github.com/plataformatec/devise/wiki/I18n en: devise: confirmations: confirmed: "Your email address has been successfully confirmed." send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." failure: already_authenticated: "You are already signed in." inactive: "Your account is not activated yet." invalid: "Invalid email or password." locked: "Your account is locked." last_attempt: "You have one more attempt before your account is locked." not_found_in_database: "Invalid email or password." timeout: "Your session expired. Please sign in again to continue." unauthenticated: "You need to sign in or sign up before continuing." unconfirmedX: "You have to confirm your email address before continuing." mailer: confirmation_instructions: subject: "[Chibineko] Confirmation instructions" reset_password_instructions: subject: "[Chibineko] Reset password instructions" unlock_instructions: subject: "[Chibineko] Unlock instructions" password_change: subject: "[Chibineko] Password Changed" omniauth_callbacks: failure: "Could not authenticate you from %{kind} because \"%{reason}\"." success: "Successfully authenticated from %{kind} account." passwords: no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." updated: "Your password has been changed successfully. You are now signed in." updated_not_active: "Your password has been changed successfully." registrations: destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." signed_up: "Welcome! You have signed up successfully." signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." updated: "Your account has been updated successfully." sessions: signed_in: "Signed in successfully." signed_out: "Signed out successfully." already_signed_out: "Signed out successfully." unlocks: send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." unlocked: "Your account has been unlocked successfully. Please sign in to continue." errors: messages: already_confirmed: "was already confirmed, please try signing in" confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" expired: "has expired, please request a new one" not_found: "not found" not_locked: "was not locked" not_saved: "could not be processed" ================================================ FILE: config/locales/devise.ja.yml ================================================ # Additional translations at https://github.com/plataformatec/devise/wiki/I18n ja: devise: confirmations: confirmed: "メールアドレスの確認が完了しました" send_instructions: "アカウントの有効化についてメールを送信しました" send_paranoid_instructions: "あなたのメールアドレスが登録済みの場合、本人確認用のメールが数分以内に送信されます" failure: already_authenticated: "すでにログインしています" inactive: "アカウントが有効化されていません。メールに記載された手順にしたがって、アカウントを有効化してください" invalid: "メールアドレスもしくはパスワードが正しくありません" locked: "あなたのアカウントは凍結されています" last_attempt: "あなたのアカウントが凍結される前に、複数回の操作がおこなわれています" not_found_in_database: "メールアドレスもしくはパスワードが正しくありません" timeout: "セッションがタイムアウトしました。もう一度ログインしてください" unauthenticated: "アカウント登録もしくはログインしてください" unconfirmed: "メールアドレスの認証が完了していません。送信されたメールの内容を確認してください" mailer: confirmation_instructions: subject: "[Chibineko] アカウントの有効化について" reset_password_instructions: subject: "[Chibineko] パスワードの再設定について" unlock_instructions: subject: "[Chibineko] アカウントの凍結解除について" password_change: subject: "[Chibineko] パスワードが変更されました" omniauth_callbacks: failure: "%{kind} アカウントによる認証に失敗しました。理由:(%{reason})" success: "%{kind} アカウントによる認証に成功しました" passwords: no_token: "このページにはアクセスできません。パスワード再設定メールのリンクからアクセスされた場合には、URL をご確認ください" send_instructions: "パスワードの再設定について数分以内にメールでご連絡いたします" send_paranoid_instructions: "あなたのメールアドレスが登録済みの場合、パスワード再設定用のメールが数分以内に送信されます" updated: "パスワードが正しく変更されました" updated_not_active: "パスワードが正しく変更されました" registrations: destroyed: "アカウントを削除しました。またのご利用をお待ちしております" signed_up: "アカウント登録が完了しました" signed_up_but_inactive: "ログインするためには、アカウントを有効化してください" signed_up_but_locked: "アカウントが凍結されているためログインできません" signed_up_but_unconfirmed: "本人確認用のメールを送信しました。メール内のリンクからアカウントを有効化させてください" update_needs_confirmation: "アカウント情報を変更しました。変更されたメールアドレスの本人確認のため、本人確認用メールより確認処理をおこなってください" updated: "アカウント情報を変更しました" sessions: signed_in: "ログインしました" signed_out: "ログアウトしました" already_signed_out: "既にログアウト済みです" unlocks: send_instructions: "アカウントの凍結解除方法を数分以内にメールでご連絡します" send_paranoid_instructions: "アカウントが見つかった場合、アカウントの凍結解除方法を数分以内にメールでご連絡します" unlocked: "アカウントを凍結解除しました" errors: messages: already_confirmed: "は既に登録済みです。ログインしてください" confirmation_period_expired: "の期限が切れました。%{period} までに確認する必要があります。 新しくリクエストしてください" expired: "の有効期限が切れました。新しくリクエストしてください" not_found: "が見つかりませんでした" not_locked: "は凍結されていません" not_saved: "処理できませんでした" ================================================ FILE: config/locales/devise_view.en.yml ================================================ en: devise: registrations: new: sign_up: "Sign up" not_receive: "Didn't receive confirmation instructions?" confirmations: new: resend_confirmation: "Resend confirmation instructions" send_mail: "Send mail" sessions: new: sign_in: "Sign in" remember_password: "Remember password" passwords: new: forgot_password: "Forgot password" send_mail: "Send mail" ================================================ FILE: config/locales/devise_view.ja.yml ================================================ ja: devise: registrations: new: sign_up: "ユーザー登録" not_receive: "登録確認メールが届かない方はこちら" confirmations: new: resend_confirmation: "登録確認メールを再送信" send_mail: "メールを送信" sessions: new: sign_in: "ログイン" remember_password: "パスワードを忘れた場合はこちら" passwords: new: forgot_password: "パスワードを再設定" send_mail: "メールを送信" ================================================ FILE: config/locales/en.yml ================================================ en: language_name: "English" common: created_by: "Created by" updated_at: "Updated at" updated_on: "Updated on" dashboard: "Dashboard" team: "Team" project: "Project" you: "You" guest: "Guest user" button: new_team: "New team" create_new_team: "Create a new team" new_project: "New project" create_new_project: "Create a new project" new_test: "New test" create_new_test: "Create a new test" sign_in: "Sign in" sign_out: "Sign out" sign_up: "Sign up" expand: "edit" close: "close" move: "Move" delete: "Delete" helpers: submit: create: "Create" submit: "Save" update: "Update" test: create: "Create test" update: "Update test" dialog: done: "Done" close: "Close" cancel: "Cancel" time: formats: default: "%a, %d %b %Y %H:%M:%S %z" long: "%B %d, %Y %H:%M" short: "%d %b %H:%M" activerecord: models: user: "User" team: "Team" project: "Project" attributes: user: email: "Email" current_password: "Current password" password: "Password" password_confirmation: "Password confirmation" name: "User name" team: name: "Team name" project: name: "Project name" test: title: "Title" errors: models: team: invalid: "may only contain letters (a-z), numbers, and underscores." project: invalid: "may only contain letters (a-z), numbers, and underscores." messages: too_short: "does not meet our requirements. (At least %{count} characters long)." too_long: "does not meet our requirements. (At most %{count} characters long)." taken: "has already been taken." invalid: "is invalid." confirmation: "doesn't match." errors: format: "%{attribute} %{message}" forbidden: "You are not authorized to view this directory or page. Please contact team admin." messages: delete_confirm: "Are you sure? You can't undo the operation." public_test_html: "This test can be accessed by anyone who knows the URL. Use the team features If you need to limit the access." created_by_guest_html: "Because this test was created by a guest user, some features such as editing and deleting of the test items are limited." please_association_html: "To user all features, log in and correlate to the user." attach_current_user: "Correlate to the current user" layouts: navbar: github_page: "GitHub page" root_sidebar: create_team: "Create a new team" dashboard_sidebar: your_test: "Your test" coach_mark: "Create a team HERE !" main_menu: account_settings: "Account settings" team_settings: "Team settings" edit_members: "Edit team members" static_pages: top: branding: title: "The simplest test supporting tool" sub_title: "Chibineko is a simple test supporting tool specializing in the management of manual tests" get_started: "Get Started" sign_up_free: "Sign Up for Free" feature: title: "Feature" test_management_title: "Test Management" test_management_body: "Chibineko supports your manual testing with its useful functions like test case creation, test execution, and progress management. Its simple design expands the usability in various situations." team_management_title: "Team" team_management_body: "With \"Team\" feature, you can share your test data among limited users. It enables you to secure your data information and manage test cases effectively." share_result_title: "Share" share_result_body: "Each test case has its individual URL. How do you share your test data with other users? All you need is to share the URL, you don't need to exchange any files any more." oss_title: "OSS" oss_body: "Our sourse code is available on GitHub. If you want to add some functions, feel free to extend. Issue and Pull Request will also be welcome." support: "Support" terms: "Terms of Service" releases: "Releases" roadmap: "Roadmap" dashboard: index: title: "Dashboard" navbar: new_test: "Create a new test" teams: show: empty_message: "No Projects" new: title: "New team" enter_name: "Enter team name" settings: title: "Team settings" name: title: "Team name" current_value_html: "Current team name: %{name}" delete: title: "Delete a team" description: "When you delete a team, all of the related data such as projects and tests are also deleted." delete_team: "Delete this team" messages: destroy: "The team has been successfully deleted." team_users: index: title: "Edit team members" search_user: "Search Chibineko user:" add_user: "Add this user to the team" delete_confirm: "Do you remove the user from the team members?" projects: navbar: menu: project_settings: "Project settings" show: empty_message: "No Tests" new: title: "New project" enter_name: "Enter project name" settings: title: "Project settings" name: title: "Project name" current_value_html: "Current project name:: %{name}" delete: title: "Delete a project" description: "When you delete a project, all of the related tests are also deleted." delete_project: "Delete this project" messages: destroy: "Project has been successfully deleted." tests: show: blank_description: "No description" created_by: "This test was created by %{username} (Updated at: %{updated_at})" form: title: new: "Create a new test" edit: "Edit test" title_placeholder: "Enter test title" markdown_header: "Test items" markdown_placeholder: "Itemize test cases" preview_header: "Preview" description: title: "Edit description" testcase: note: "Notes" collapse_all: "Collapse all" expand_all: "Expand all" move: title: "Move to" unset_project: "No projects" submit: "Move" result_label: title: "Customize result labels" use_default_label: "Use the default labels" label_placeholder: "Enter labels" navbar: edit_test: "Edit test" edit_current_test: "Edit the current test" export_csv: "Export CSV" test_settings: "Test settings" duplicate_test: "Duplicate this test" move_to: "Move to" customize_result_label: "Customize result labels" delete_test: "Delete this test" messages: destroy: "The test has been successfully deleted." result_labels: unexecuted: "NOT RUN" pass: "PASS" fail: "FAIL" blocked: "BLOCKED" na: "N/A" csv: case: "Test cases" result: "Results" note: "Notes" user_settings: modal: title: "Account settings" expand: "edit" close: "close" username: title: "User name" blank: "E-mail address is used as an user name until a new name is entered." current_value_html: "Your user name is %{username}" password: title: "Password" change_password: "Change sign in password" current_password: "Current password" new_password: "New password" confirm_password: "Confirm new password" update: "Update Password" timezone: title: "Time Zone" current_value_html: "Your current time zone: %{timezone}" update: "Save Time Zone" language: title: "Language" current_value_html: "Your current language: %{language}" ================================================ FILE: config/locales/ja.yml ================================================ ja: language_name: "日本語" common: created_by: "作成者" updated_at: "更新日時" updated_on: "更新日" dashboard: "ダッシュボード" team: "チーム" project: "プロジェクト" you: "あなた" guest: "ゲスト" button: new_team: "新規チーム" create_new_team: "新しいチームを作成" new_project: "新規プロジェクト" create_new_project: "新しいプロジェクトを作成" new_test: "新規テスト" create_new_test: "新しいテストを作成" sign_in: "ログイン" sign_out: "ログアウト" sign_up: "ユーザー登録" expand: "編集" close: "閉じる" move: "移動" delete: "削除" helpers: submit: create: "作成" submit: "保存" update: "更新" test: create: "テストを作成" update: "テストを更新" dialog: done: "完了" close: "閉じる" cancel: "キャンセル" time: formats: default: "%Y/%m/%d %H:%M:%S" long: "%Y年%m月%d日(%a) %H時%M分%S秒 %z" short: "%y/%m/%d %H:%M" activerecord: models: user: "ユーザー" team: "チーム" project: "プロジェクト" attributes: user: email: "メールアドレス" current_password: "現在のパスワード" password: "パスワード" password_confirmation: "パスワード(確認)" name: "ユーザー名" team: name: "チーム名" project: name: "プロジェクト名" test: title: "タイトル" errors: models: team: invalid: "は半角英数字かアンダースコアで入力してください" project: invalid: "は半角英数字かアンダースコアで入力してください" messages: too_short: "は%{count}文字以上で入力してください" too_long: "は%{count}文字以内で入力してください" taken: "はすでに存在します" invalid: "は不正な値です" confirmation: "が一致しません" errors: format: "%{attribute}%{message}" forbidden: "アクセスする権限がありません。チーム管理者に問い合わせてください。" messages: delete_confirm: "本当に削除しますか?この操作は取り消せません。" public_test_html: "このテストはURLを知っているユーザーなら誰でもアクセスできます。アクセスできるユーザーを制限したい場合はチーム機能をご利用ください。" created_by_guest_html: "このテストはゲストユーザーによって作成されたため、テスト項目の編集や削除など一部機能が制限されています。" please_association_html: "すべての機能を使用したい場合は、ログインしてユーザーとの紐付けを行ってください。" attach_current_user: "現在のユーザーに紐付ける" layouts: navbar: github_page: "GitHubページ" root_sidebar: create_team: "新しいチームを作成" dashboard_sidebar: your_test: "あなたのテスト" coach_mark: "チーム作成はこちら" main_menu: account_settings: "アカウント設定" team_settings: "チーム設定" edit_members: "チームメンバーを編集" static_pages: top: branding: title: "世界で最もシンプルなテスト支援ツール" sub_title: "Chibinekoはマニュアルテストの管理に特化したシンプルなテストツールです" get_started: "今すぐはじめる" sign_up_free: "無料でサインアップ" feature: title: "機能" test_management_title: "テスト管理" test_management_body: "テストケース作成、テスト実行、進捗管理など、マニュアルテストに必要な一通りの機能を提供します。シンプルがゆえに自由度が高いため、様々なシーンで活用いただけます。" team_management_title: "チーム管理" team_management_body: "チームを作成してユーザーをアサインすれば、限られたユーザーのみでテスト内容を共有することができます。セキュリティを保ちつつ効率的にテストケースを管理することができます。" share_result_title: "結果の共有" share_result_body: "テストケースにはユニークなURLが割り当てられるため、他のユーザーとテスト結果を共有したい場合はURLをシェアするだけ。面倒なファイルのやり取りなどは不要です。" oss_title: "OSS" oss_body: "このサービスのソースコードはすべてGitHub上で公開されています。もし足りない機能がある場合は、自由に拡張してください。issueやPull Requestも歓迎します。" support: "サポート" terms: "利用規約" releases: "リリースノート" roadmap: "ロードマップ" dashboard: index: title: "ダッシュボード" navbar: new_test: "新しいテストを作成" teams: show: empty_message: "プロジェクトがまだありません" new: title: "新しいチーム" enter_name: "チーム名を入力" settings: title: "チーム設定" name: title: "チーム名" current_value_html: "現在のチーム名: %{name}" delete: title: "チームを削除" description: "チームを削除すると、チームに含まれるプロジェクトおよびテストもすべて削除されます。" delete_team: "このチームを削除する" messages: destroy: "チームを削除しました" team_users: index: title: "チームメンバーを編集" search_user: "Chibinekoユーザーを検索:" add_user: "ユーザーをチームに追加" delete_confirm: "チームメンバーから削除しますか?" projects: navbar: menu: project_settings: "プロジェクト設定" show: empty_message: "テストがまだありません" new: title: "新しいプロジェクト" enter_name: "プロジェクト名を入力" settings: title: "プロジェクト設定" name: title: "プロジェクト名" current_value_html: "現在のプロジェクト名: %{name}" delete: title: "プロジェクトを削除" description: "プロジェクトを削除すると、プロジェクトに含まれるテストもすべて削除されます。" delete_project: "このプロジェクトを削除する" messages: destroy: "プロジェクトを削除しました" tests: show: blank_description: "説明が入力されていません" created_by: "このテストは %{username} によって作成されました(更新日時: %{updated_at})" form: title: new: "新しいテストを作成" edit: "テストを編集" title_placeholder: "タイトルを入力" markdown_header: "テスト項目" markdown_placeholder: "テスト項目を箇条書きで入力" preview_header: "プレビュー" description: title: "説明を編集" testcase: note: "メモ" collapse_all: "折りたたむ" expand_all: "すべて展開" move: title: "テストを移動" unset_project: "プロジェクト未設定" submit: "移動" result_label: title: "結果ラベルをカスタマイズ" use_default_label: "デフォルトラベルを使用" label_placeholder: "ラベルを入力" navbar: edit_test: "テストを編集" edit_current_test: "現在のテストを編集" export_csv: "CSVエクスポート" test_settings: "テスト設定" duplicate_test: "このテストを複製" move_to: "別プロジェクトへ移動" customize_result_label: "結果ラベルをカスタマイズ" delete_test: "このテストを削除" messages: destroy: "テストを削除しました" result_labels: unexecuted: "未実行" pass: "OK" fail: "NG" blocked: "保留" na: "対象外" csv: case: "テスト項目" result: "結果" note: "備考" user_settings: modal: title: "アカウント設定" expand: "編集" close: "閉じる" username: title: "ユーザー名" blank: "ユーザー名が未設定の場合、メールアドレスがユーザー名として表示されます" current_value_html: "あなたのユーザー名: %{username}" password: title: "パスワード" change_password: "ログインパスワードを変更します" current_password: "現在のパスワード" new_password: "新しいパスワード" confirm_password: "新しいパスワード(確認)" update: "パスワードを更新" timezone: title: "タイムゾーン" current_value_html: "現在のタイムゾーン: %{timezone}" update: "タイムゾーンを保存" language: title: "言語" current_value_html: "現在の言語: %{language}" ================================================ FILE: config/locales/js.en.yml ================================================ en: js: team_sidebar: edit_members: search_user: "Search user..." add_user: "Add this user to the team" not_found: "User not found" unknown_error: "Unknown error" tests: show: errors: conflict: "Test cases might have been editted by other uses." please_reload: "Reload the page" new: messages: unsaved_changes: "You have unsaved changes." edit_result_label: errors: too_short: "You can not delete any more." too_long: "You can not add any more." navbar: total: "Total" toolbar: selected: "selected" bootgrid: loading: "Loading..." no_results: "No results found." search: "Search by title" common: messages: delete_confirm: "Are you sure? You can't undo the operation." collapse: expand: "edit" close: "close" ladda: saving: "saving..." processing: "processing..." ================================================ FILE: config/locales/js.ja.yml ================================================ ja: js: team_sidebar: edit_members: search_user: "検索中..." add_user: "ユーザーをチームに追加" not_found: "ユーザーが見つかりません" unknown_error: "エラーが発生しました" tests: show: errors: conflict: "テスト項目が他のユーザーによって変更された可能性があります" please_reload: "ページをリロードしてください" new: messages: unsaved_changes: "変更内容が保存されていません。" edit_result_label: errors: too_short: "これ以上削除できません。" too_long: "これ以上追加できません。" navbar: total: "合計" toolbar: selected: "件選択中" bootgrid: loading: "読み込み中..." no_results: "検索結果がありません" search: "タイトルで検索" common: messages: delete_confirm: "本当に削除しますか?この操作は取り消せません。" collapse: expand: "編集" close: "閉じる" ladda: saving: "保存中..." processing: "処理中..." ================================================ FILE: config/locales/terms.en.yml ================================================ en: static_pages: terms: title: "Terms of Service" body_html: |

Last modified: January 20, 2015

Article 1. Using our Services

  1. These Terms and Conditions (the "Terms") set forth the terms between SHIFT Inc.(“SHIFT”) regarding the usage of the service under the name of "Chibineko" (the "Service") provided by SHIFT. The User shall use the Services in accordance with the contents of the Terms.
  2. The Terms apply to all acts of Users in their use of the service.
  3. Users are considered to have agreed to all the content stated in the Terms upon using the service.

Article 2. Definition

  1. "Chibineko" means the WEB service (https://chibineko.jp) that SHIFT owns and operates. Chibineko includes the followings:
    (a) similar or related Chibineko Website (including subdomain, different languages, widgets, smartphones, and so on), (b) a platform operated by SHIFT, (c) social plug ins, (d) software (e.g. toolbar), devices, and networks, existing or not-yet existing.
  2. "The other Terms and regulations" mean agreements other than the Agreement that specify the Terms of the Services, regardless of their titles, including use agreements for fee-based services and use agreements for any other services.
  3. "Platform" means a set of APIs and services (such as contents) that enable others, including application developers and website operators, to retrieve data from Chibineko or provide data to us.
  4. "Information" mean facts and other information about you, including actions taken by Users and non-Users who interact with Chibineko.
  5. "Development Information" means programs, contents and materials related to software, Web sites, applications or the like that Users of the Services or others involved with Chibineko use this Services to plan, develop, store and manage; records of development-related communication; information obtained by using any of the Services; facts or the like of their being engaged in development; and any other development-related facts or information.
  6. "Other contents" mean every information posted by Users and the third parties, and it.
  7. "User information" mean the information related to Users, development information, and all or a part of other contents.
  8. "data" means any data, including a user's contents or information that you or a third party can retrieve from Chibineko or provide to Chibineko through Platform.
  9. "post" means to post on Chibineko or otherwise make available by using Chibineko.
  10. "use" means to use, run, copy, publicly perform or display, distribute, modify, translate, and create derivative works of.

Article 3. Scope of the Terms

  1. The conditions of the Service use is published in the Terms and other Terms and regulations. The Other Terms of Use shall make-up a part of the Terms, regardless of the title.
  2. If provisions of the Term and the other Terms (and regulations) are not consistent, the Term shall prevail the other Terms.

Article 4. Changes of the Terms

  1. SHIFT reserves the right to change the Terms at its discretion without providing prior notice to Users.
  2. The modification will become effective once the modified Terms are posted on an appropriate location within Chibineko Website, unless otherwise specified by SHIFT.
  3. Users shall be deemed to have granted valid and irrevocable consent to the modified Terms by continuing to use the Service.

Article 5. Responsibility of Uses

  1. SHIFT believes Users’ privacy is very important.
  2. SHIFT handles personal information properly based on our privacy policy.

Article 6. Preparation of Computer Environment

  1. Users shall supply the necessary computer environment (hardware, software, data connection) for using the Service under Users' own responsibility and at Users' own expense.
  2. Users shall, with its own costs and responsibilities, prepare and maintain security systems suitable for the Users' environment for use of the Service to avoid computer virus attacks, unauthorized access, information leakage, etc.
  3. SHIFT will not be responsible for preparation of computer environment.

Article 7. Users' Responsibility

  1. Users use this service on their own responsibility.
  2. Users are responsible for all the information they provide to this service. SHIFT is not responsible for the information.
  3. The User understands that SHIFT is not responsible for damages and loss of the data, and the User shall take backup of the data.
  4. In the event of defamation of character, violation of privacy rights, the unauthorized disclosure of a third party's personal information, the Copyright Act of Japan violations or any other infringement on the rights of individuals, the User shall bear all responsibility in those cases.

Article 8. Prohibited Matters

Users shall be prohibited from engaging in any of the following activities with respect to the use of the Service. In cases where SHIFT determines that the act of the Users to transmit information in the Service falls or is likely to fall under any of the items of Article 8, SHIFT may delete all or part of such information without any prior notice to Users. SHIFT shall in no event be responsible or liable for any damage incurred by the User as a result of an action taken by SHIFT pursuant to this Article 8.
(1) Infringement or possible infringement of SHIFT or any third party right, including without limitation any copyright, intellectual property, privacy or proprietary right.
(2) Any conduct which defames, harasses, disadvantages, damages or discredits (including slander and libel) a third party or SHIFT.
(3) Use this Service by pretending to be someone else or misrepresenting that he/she has authority of representation or agency when in fact he/she has no such authority or that he/she has a tie-up with or in a collaborative relationship with another person or organization.
(4) Any conduct which induces or tends to criminal acts, such as a fraud, drug abuse, child prostitution, illegal trade of saving accounts or mobile phone.
(5) Performance of any of the following acts related to the information of obscene, child pornography, or child abuse, develop information, and contents("these information" hereafter)
  1. Acts to post or to indicate the information.
  2. Acts to sell media saved the information.
  3. Acts to post or to indicate advertisements that induce to spread (send, indicate, sell) the information.
(6) Acts that utilize the Service for the purpose of sexual activity or other sexual or obscene activities.
(7) Acts that lead Users to gamble illegally or to join it.
(8) Acts that take on, intermediate, and induce illegal acts(transfer of guns, production of the explosive substance, offer of the child porno, forgery of an official document, murder, threats, and so on).
(9) Acts to induce a suicide or to seduce into it.
(10) Acts to post or to indicate information related to followings.
  1. Pictures and information of murder and crime scene.
  2. Pictures and information of corpses.
  3. Pictures and information of other cruelties.
(11) Acts to post information related to following contents.
  1. Any content that discriminates or defames SHIFT, any other person or any entity, encourages discrimination against and/or damage the reputation of SHIFT, any other person or entity.
  2. Information enabling identification of a specific person, such as names, addresses, where to work, phone number. It includes the case that enabling identification by collating with other information.
  3. False information and contents that are difficult to check the facts.
  4. Information that should be reported to proper public institution, such as problems in sanitary management.
  5. Information related to troubles or claims.
  6. Other information that SHIFT considers inappropriate.
(12) Acts to post or send message to others about following information.
  1. Information of commercial advertisement, publicity, and campaigns except for what SHIFT approved.
  2. Information includes affiliates.
  3. Information that induce others to.
  4. Information that includes a bank account number.
  5. Information that lead Users to websites that SHIFT considers inappropriate, which intend to circulate harmful programs (i.e. website for adults, child pornography, internet virus), one-click fraud website, illegal gambling (especially including posting the links).
  6. Acts that cause discomfort to other users, such as posting grotesque images.
  7. All other acts that SHIFT considers inappropriate.
(13) Acts that post personal information enabling specific person on this website where every user can see it.
(14) Acts that randomly disseminate messages to unsolicited Users and/or invites unsolicited Users.
(15) Acts that SHIFT considers as a spam(postings, message, word, and URL), chainmail or multi-post, which randomly sends other Users email message and comments, etc.
  1. spam word; randomly posting more than one word that has no or little relevance to such space, extremely lengthy sentence or a large number of words to any space of the site where a User can post.
  2. spam URL; posting or transmitting identical URL to any space of the site where a User can post.
  3. Other acts that SHIFT considers as a spam act.
(16) Illegally change or delete data on this site.
(17) Send or post computer viruses.
(18) All acts that may cause trouble to the Service and our network, including mass data transfer that give too much load on the server.
(19) Act that provides links to encourage an action, knowing that the action may fall under any of the preceding items.
(20) Act that is deemed by SHIFT to be against any law, ordinance, public order, standards of decency, Terms of Service or other Terms and regulations, or to be an infringement of any right of others.
(21) Other activities that SHIFT thinks not acceptable.

Article 9. Changes of Services

SHIFT reserves the right to modify or cease, at the SHIFT's own discretion, the Service at anytime without any prior notice to Users.

Article 10. Right of the User information and its use

  1. Users shall guarantee the User information not to violate any third party right. In case any dispute occurs among the User and the third party, the Users must solve at users' own responsibility.
  2. All the intellectual property rights, such as copyright, moral rights of authors of the Contents belong to the original creator.
  3. Users may, from time to time, be provided with features that may enable them; to the extent the User sets, to share with other users; let other multiple users to edit or the like; or open to the general public, pieces of the User information posted by users on the Services. In that case, it is understood that the User uses those features on his/her own responsibility, without any responsibility whatsoever on the part of SHIFT.
  4. SHIFT can browse, use, and save the User information and other contents contributed by Users as needed to operate its business, build and improve systems, and for its maintenance.
  5. The User information in the form as determined in the preceding paragraph and when the User uses the User information, the name or part of the Information displayed can be omitted.
  6. When SHIFT utilizes the User information by the Article 4, we respect the publication range of the Information that the User sets.

Article 11. Delete of Information

Under the following circumstances, SHIFT approves and agrees to be able to delete all or a part of the contents whether it is illegal or breaches of the Terms. However, SHIFT is not obligated to implement such measures.
(1) If Information containing photographs or depictions of (partially or fully) naked people (including characters from anime/manga, regardless of whether the person is real or not) is posted.
(2) If a public organization or an expert (i.e. the nation, a local public body, reliability assurance body that is defined by the guideline under the Act on the Limitation of Liability for Damages of Specified Telecommunications Service Providers and the Right to Demand Disclosure of Identification Information of the Senders, Internet Hotline Center and lawyers) points out or expresses its opinion saying Contents is illegal, against public moral or violates rights of others.
(3) If a right holder, makes a claim that Contents violate the rights of the right holder. However, this is limited to situations in which documentation is shown by the right holder, it can be reasonably concluded that the right holder have the rights, and SHIFT has deemed the right holder to have proprietary rights after careful consideration.

Article 12. Disclaimers

  1. When a dispute occurs among Users, the Users shall resolve it and SHIFT bears no responsibility for it. User shall take some reasonable measures to prevent dispute among Users on their own.
  2. In any of the following events, SHIFT may temporarily stop operation of Services without prior notice to or approval by users. The same applies to damages emerging from a loss in display speed due to overloaded access or other unforeseen factors.
  3. SHIFT has no obligation to observe and delete the User information that users provide.
  4. SHIFT has no obligation to save the User information that users provide.
  5. SHIFT can post advertisements of SHIFT or third party's on the website.
  6. SHIFT does not guarantee the Contents in the Service or other related service for their legality, morality, reliability, accuracy, appropriateness, completeness, etc. The User information posted by the users concerning the internal rules of propriety for corporations and organizations they belong to, SHIFT assumes no responsibility.
  7. SHIFT is not responsible for any offensive, inappropriate, obscene, unlawful or otherwise objectionable content or information the User may encounter on Chibineko.
  8. In cases below, SHIFT can disclose the information to others without any responsibilities for losses.
    (1) To elucidate the causes of technical failure of the Service, if necessary cancellation.
    (2) Inquired by public organization(such as court, police).
    (3) In the case of a violation of Terms has occurred and SHIFT considered that we need to check the User information.
    (4) If SHIFT judges that there is an urgent need to prevent damages to a person's life, body, or property.
    (5) If it is necessary to properly operate this Service more.
  9. SHIFT, shall not be liable if it determines there is sufficient probable cause to believe that an act done or conduct may violate the Terms or other Terms and regulations as the registered user who performed the acts will be punished with cancellation of the account, removal of all or part of information, and even in cases for changes in the public that caused loses.
  10. In any of the following events, SHIFT may temporarily stop operation of services without prior notice to or approval by Users, which Users in advance approve. Even if a temporary stop of operation of Services causes damages of Users, SHIFT shall not be liable for it.
    (1) SHIFT performs maintenance of a server for Services, modifies or changes specifications of Services, fix a defect in Services, etc.
    (2) Operation of Services becomes difficult or impossible because an act of God or other emergency event has occurred or threatens to occur or a relevant law, regulations or the like has been revised or newly established.
    (3) Otherwise, SHIFT judges that it is necessary to temporarily stop operation of services, due to any compelling reason.
  11. If this Terms or the other terms and regulations fall under a consumer contract provided for in paragraph 3 of Article 2 of the Consumer Contract Act of Japan, the provisions of the Terms or other terms and conditions that completely exclude liability of SHIFT for damages shall not apply. In such cases, if any loss or damage is caused to a Developer as a result of default or tort committed by SHIFT, SHIFT shall be liable for damages only if such default or a tort is committed intentionally or through gross negligence, and the SHIFT’s liability shall be limited to the actual loss or damage suffered by the said Developer.

Article 13. Validity of the Terms and other Terms and regulations

  1. When a part of the Terms becomes invalid or is cancelled due to the change of law etc., the validity of the other Terms and regulations would stay valid.
  2. When a part or entire Terms is considered to be invalid or cancelled with a particular user, the Terms is still valid to other users.

Article 14. Measures for violation of this Agreement and other Terms and regulations

  1. If a violation is discovered, Users can report the violation using the query form. However, SHIFT takes no responsibility for answering, solving or handling the inquiry.
  2. If a user is found to violate these Terms, or in any other situation the Service has deemed necessary, SHIFT may take the following action against that user.
    (1) Request that the conduct that is in violation of these Terms be ceased and that the same conduct not be repeated.
    (2) Act to require the User to delete or modify the User information.
    (3) Act to delete whole or a part of the User information, and to change its areas open to limited or private.
    (4) Act to force suspension disposal.
  3. The user can't object to SHIFT's measure.

Article 15. Disputes

  1. These Terms shall be governed by the laws of Japan.
  2. Any dispute arising out of the Terms shall be subject to the exclusive venue of the Tokyo District Court in Japan, and the parties hereby consent to the venue and jurisdiction of the courts.
  3. If anyone brings a claim against SHIFT related to users' actions, contents or Information on this service, the user will indemnify and hold SHIFT harmless from and against all damages, losses, and expenses of any kind (including reasonable legal fees and costs) related to such claim. Although SHIFT provides rules for user conduct, it does not control or direct users' actions on this Service and are not responsible for contents or Information transmit or share on Chibineko. SHIFT is not responsible for any offensive, inappropriate, obscene, unlawful or otherwise objectionable content or information you may encounter on Chibineko. SHIFT is not responsible for the conduct, whether online or offline, of any user of Chibineko.

Article 16. General

  1. If any portion of this Statement is found to be unenforceable, the remaining portion will remain in full force and effect.
  2. If we fail to enforce any of this Statement, it will not be considered a waiver.
  3. The Users are not allowed to transfer the rights and obligation of the Terms without SHIFT's approval.
  4. All of our rights and obligations under this Statement are freely assignable by us in connection with a merger, acquisition, or sale of assets, or by operation of law or otherwise.
  5. Nothing in this Statement shall prevent us from complying with the law.
  6. This Statement does not confer any third party beneficiary rights.
  7. SHIFT reserves all rights in and to this service not otherwise granted in this Agreement.
  8. The User will comply with all applicable laws when using or accessing this service.

Article 17. Language

  1. In the event of discrepancy between the English version and the Japanese version of this Terms, the Japanese version shall prevail.

Supplementary Provision

  1. This Terms of Use is effective since January 20th 2015.
  2. This Terms is applied to activities of users before the release date.
================================================ FILE: config/locales/terms.ja.yml ================================================ ja: static_pages: terms: title: "サービス利用規約" body_html: |

2015年1月20日 制定

第 1 条 ご利用にあたって

  1. この利用規約(以下「本規約」)は、「Chibineko」(以下「本サービス」)の利用条件を定めるものです。ユーザーは、本規約に従い本サービスを利用するものとします。
  2. 本規約は、ユーザーが本サービスを利用する際の一切の行為に適用されます。
  3. ユーザーは、本サービスを利用することにより、本規約の全ての記載内容について同意したものとみなされます。

第 2 条 定義

  1. 「Chibineko」とは、株式会社SHIFT(以下「弊社」)が運営するWebサービス( https://chibineko.jp )をいいます。 これには以下を通じて提供されるものを含みます。
    (a) ****.chibineko.jp の弊社ウェブサイト及びその他の Chibineko ブランド又は共同ブランドウェブサイト(サブドメイン、各国語バージョン、ウィジェット、モバイルバージョンなど)、(b) 弊社のプラットフォーム、(c) ソーシャルプラグイン、(d) 現存する、又は今後開発されるその他の媒体やソフトウェア(ツールバーなど)、デバイス、ネットワーク。
  2. 「その他の利用規約等」とは、有料サービス利用規約その他各サービスの利用規約等名称の如何に関わらず、本規約以外の規定であって、本サービスの利用条件を定めるものをいいます。
  3. 「プラットフォーム」とは、アプリケーション開発者及びウェブサイト運営者を含む他の個人又は組織が本サービスからデータを取得したり、弊社にデータを提供することを可能にする、一連の API 及びサービス(コンテンツなど)を意味します。
  4. 「情報」とは、ユーザーやユーザー以外の者で本サービスに関わる人が実行したアクションなどの事実や情報を意味します。
  5. 「開発情報」とは、ユーザーやユーザー以外の者で本サービスに関わる者が本サービスを利用し、企画、開発、保管、運用するソフトウェア、ウェブサイト、アプリケーションなどに関係するプログラム、コンテンツ、素材、開発に関するコミュニケーション記録、本サービスの各サービスを利用した結果得られた情報、開発を実施している事実等、その他開発に関係する一切の事実や情報を意味します。
  6. 「その他のコンテンツ」とは、「情報」「開発情報」の定義に含まれない、ユーザー又は他の人が本サービスに投稿するあらゆるものを意味します。
  7. 「ユーザー情報」とは、ユーザーに関連する情報、開発情報、その他のコンテンツの全部又は一部を意味します。
  8. 「データ」とは、ユーザーや第三者が本サービスから取り出したり、プラットフォームを通じて本サービスに提供することができる、ユーザーの情報、開発情報、コンテンツを含むあらゆるデータを意味します。
  9. 「投稿」とは、本サービス上での投稿、又は本サービスを使って提供される投稿を意味します。
  10. 「使用」とは、使用、実行、複製、公演、公開、配信、変更、翻訳、及び派生作品の作成を意味します。

第 3 条 適用範囲

  1. 本サービスの利用条件は、本規約及びその他の利用規約等において規定されています。その他の利用規約等は名称の如何に関わらず本規約の一部を構成するものとします。
  2. 本規約の規定とその他の利用規約等の規定が異なる場合は、当該その他の利用規約等の規定が優先して適用されるものとします。

第 4 条 規約の変更

  1. 弊社は、弊社の判断により、本規約をいつでも任意の理由で変更することができるものとします。
  2. 変更後の利用規約は、弊社が別途定める場合を除いて、本サービス上に表示した時点より効力を生じるものとします。
  3. ユーザーが、本規約の変更の効力が生じた後に 本サービスを利用した場合には、変更後の利用規約の全ての記載内容に同意したものとみなされます。

第 5 条 個人情報の取り扱い

  1. 本サービスはユーザーのプライバシーを尊重しています。
  2. 弊社は、ユーザーの個人情報を、弊社が定めるプライバシーポリシーに基づき、適切に取り扱うものとします。

第 6 条 利用環境の整備

  1. ユーザーは、本サービスを利用するために必要なあらゆる機器、ソフトウェア、通信手段を自己の責任と費用において、適切に整備するものとします。
  2. ユーザーは自己の利用環境に応じて、コンピューター・ウィルスの感染の防止、不正アクセス及び情報漏洩の防止等のセキュリティ対策を講じるものとします。
  3. 弊社はユーザーの利用環境について一切関与せず、また一切の責任を負いません。

第 7 条 ユーザーの責任

  1. ユーザーは、ユーザーの責任において 本サービスを利用するものとし、本サービスを利用してなされた一切の行為及びその結果について一切の責任を負います。
  2. 本サービスを利用してユーザーが投稿した情報、開発情報、その他のコンテンツに関する責任は、ユーザー自身にあります。弊社はユーザーが本サービスを利用して投稿した情報、開発情報、その他のコンテンツの内容について、一切責任を負いません。
  3. ユーザーは、ユーザーが本サービスにおいて投稿、利用、保管、運用しているユーザー情報について弊社に保存義務がないことを認識し、必要な情報、開発情報、その他のコンテンツについては適宜自己の責任においてバックアップをとるものとします。弊社は、ユーザー情報の毀損、消失につき、一切の責任を負いません。
  4. ユーザーが他人の名誉を毀損した場合、プライバシー権を侵害した場合、許諾なく第三者の個人情報を開示した場合、著作権法(昭和 45 年法律第 48 号)に違反する行為を行った場合その他他人の権利を侵害した場合には、当該ユーザーは自身の責任と費用において解決しなければならず、弊社は一切の責任を負いません。

第 8 条 禁止事項

ユーザーは、本サービスの利用にあたり、次に掲げる行為を行ってはならないものとします。禁止事項に違反した場合には、利用停止、ユーザー情報の全部若しくは一部の削除、又は公開範囲の変更等の不利益な措置を採ることがあります。
(1) 弊社若しくは他者の著作権、商標権等の知的財産権を侵害する行為、又は侵害するおそれのある行為。
(2) 弊社若しくは他者の財産、プライバシー若しくは肖像権を侵害する行為、又は侵害するおそれのある行為。
(3) 弊社若しくは他者を不当に差別若しくは誹謗中傷し、他者への不当な差別を助長し、又はその名誉若しくは信用を毀損する行為。
(4) 自分以外の人物を名乗ったり、代表権や代理権がないにもかかわらずあるものと装ったり、又は他の人物や組織と提携、協力関係にあると偽って 本サービスを利用する行為。
(5) 詐欺、規制薬物の濫用、児童売買春、預貯金口座及び携帯電話の違法な売買等の犯罪に結びつく、又は結びつくおそれのある行為。
(6) わいせつ、児童ポルノ又は児童虐待に相当する情報、開発情報、コンテンツ(以下、本号において「これらの情報等」といいます)について、次に掲げるいずれかの行為を行うこと。
  1. これらの情報等を投稿又は表示する行為。
  2. これらの情報等を収録した媒体を販売する行為。
  3. これらの情報等を収録した媒体の送信、表示、販売を想起させる広告を投稿又は表示する行為。
(7) 性行為、わいせつな行為等を目的として利用する行為。
(8) 違法な賭博・ギャンブルを行わせ、又は違法な賭博・ギャンブルへの参加を勧誘する行為。
(9) 違法行為(けん銃等の譲渡、爆発物の製造、児童ポルノの提供、公文書偽造、殺人、脅迫等)を請け負い、仲介し、又は誘引する行為。
(10) 他人を自殺に誘引又は勧誘する行為。
(11) 次に掲げる情報を投稿し、又は表示する行為。
  1. 人の殺害、傷害現場を撮影した情報。
  2. 死体を撮影した情報。
  3. その他残虐な行為を撮影した情報。
(12) 次に掲げる内容の情報を投稿する行為。
  1. 他のユーザーや第三者又は他のユーザーや第三者のユーザー情報等に対して(直接の対象となるユーザーやユーザー情報に限らず、関連する著作者等を含む。)を誹謗中傷する内容の情報。
  2. 氏名、住所、勤務先、電話番号等個人を特定しうる内容の情報。他の情報と照らし合わせることで個人を特定しうる場合を含む。
  3. 真否についての事実確認が困難な内容や虚偽の内容の情報。
  4. 衛生管理上の問題等のしかるべき公的機関へ届け出るべき事項に関する内容の情報。
  5. トラブル又はクレームに関する内容の情報。
  6. その他弊社が不適切と判断する内容の情報。
(13) 次に掲げる内容の情報を、本サービス内の投稿可能な箇所に投稿し、又は他のユーザーにメッセージで送信する行為。
  1. 商業用の広告、宣伝又は勧誘を目的とする情報。但し、弊社が別に認めたものを除く。
  2. アフィリエイトのリンクを含む情報。
  3. 無限連鎖講(ネズミ講)、チェーンメール、MLM、リードメール等他人を勧誘する内容の情報。
  4. 金融機関等の口座番号を含む情報。
  5. アダルトサイト、ワンクリック詐欺サイト、ウィルス等の有害なコンピュータプログラム等を流布させることを目的とするサイト等弊社が不適切と判断するサイトに誘導する情報(単にリンクを張る行為を含む。)。
  6. グロテスクな画像等の他のユーザーが不快を感じる可能性が高いと弊社が判断する情報。
  7. その他弊社が不適切と判断する情報。
(14) 自己又は第三者の住所、電話番号、メールアドレス等の個人が特定される連絡先を、本サイト内のユーザー全体に公開される箇所に投稿する行為。
(15) 他のユーザーに対して、無差別にメッセージを送信し、無差別にフレンドの追加を依頼する行為、又は全く面識のない人を無差別に招待する行為。
(16) 次に掲げるスパム行為を行うこと。
  1. スパム投稿・スパムメッセージ・・・ 一人又は複数のユーザーが、本サイト内の投稿可能な箇所に、同一又は類似の文章を投稿し、又はメッセージで送信する行為。
  2. スパムワード・・・ 一人又は複数のユーザーが、本サイト内の投稿可能な箇所に、当該箇所と無関係若しくは関連性の希薄な語句を複数羅列し、又は著しく長い文章若しくは大量の語句を投稿する行為。
  3. スパム URL・・・ 一人又は複数のユーザーが、本サービス内の投稿可能な箇所に、同一の URL を投稿し、又はメッセージで送信する行為。
  4. その他弊社がスパム行為と判断する行為。
(17) 弊社の設備に蓄積された情報を不正に書き換え、又は消去する行為。
(18) ウィルス等の有害なコンピュータプログラム等を送信又は掲載する行為。
(19) 弊社又は他者のサーバーに負担をかける行為、若しくは、本サービスの運営やネットワーク・システムに支障を与える行為、又はこれらのおそれのある行為。
(20) その行為が前各号のいずれかに該当することを知りつつ、その行為を助長する目的でリンクを貼る行為。
(21) 法令、公序良俗又は本利用規約若しくはその他の利用規約等に違反し、又は他者の権利を侵害すると弊社が判断する行為。
(22) その他、弊社が不適切と判断する行為。

第 9 条 サービスの変更

弊社は、弊社の都合により、本サービスをいつでも任意の理由で追加、変更、中断、終了することができます。

第 10 条 ユーザー情報の権利、利用

  1. ユーザーは本サービス上のユーザー情報について、弊社に対し、当該ユーザー情報が第三者の権利を侵害していないことを保証するものとします。万一、第三者との間で何らかの紛争が発生した場合には、当該ユーザーの費用と責任において問題を解決するとともに、弊社に何等の迷惑又は損害を与えないものとします。
  2. 本サービス上のユーザー情報の権利(著作権及び著作者人格権等の周辺権利)は、創作したユーザーに帰属します。
  3. 本サービス上で、ユーザーが投稿したユーザー情報を、当該ユーザーの設定する公開範囲に応じて、他のユーザーと共有し、複数のユーザーが編集等を行い、又は一般公開することができる機能が提供されることがあります。この場合、ユーザーは自己の責任においてこれらの機能を利用するものとし、弊社は一切の責任を負いません。
  4. 弊社は、ユーザー情報を、本サービスの円滑な提供、弊社システムの構築、改良、メンテナンスに必要な範囲内で、使用することができるものとします。
  5. 弊社が前項に定める形でユーザー情報を使用するにあたっては、情報の一部又は氏名表示を省略することができるものとします。
  6. 弊社が第 4 項に定める形でユーザー情報を使用するにあたっては、ユーザーが設定する情報の公開の範囲に従うものとします。

第 11 条 情報の削除

弊社は、次に掲げる場合には、ユーザー情報の違法性・規約違反の有無に関わらず、関連するユーザー情報について、その全部若しくは一部の削除又は公開範囲の変更等の不利益な措置を行うことができるものとします。
(1) 人(実在の人物であるか否かを問わず、漫画・アニメ等のキャラクターを含みます)の裸体(着衣の全部又は一部を欠くものをいいます)を撮影・描写した情報が投稿された場合。
(2) 公的な機関又は専門家(国、地方公共団体、特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律のガイドラインに規定された信頼性確認団体、インターネット・ホットラインセンター、弁護士等をいいます)から、ユーザー情報について、違法、公序良俗違反又は他人の権利を侵害する等の指摘・意見表明があった場合。
(3) 権利者と称する者から、ユーザー情報が自分の権利を侵害する旨の申告があった場合。但し、権利者と称する者から、権利者であることを合理的に判断できる資料を提示され、弊社にて慎重に検討した結果、権利者であると弊社が判断した場合に限る。

第 12 条 免責事項

  1. 弊社は、ユーザーの通信や活動に関与しません。万一ユーザー間の紛争があった場合でも、当該ユーザー間で解決するものとし、弊社はその責任を負いません。
  2. 弊社は、本サービスの内容の追加、変更、又は本サービスの中断、終了によって生じたいかなる損害についても、一切責任を負いません。アクセス過多、その他予期せぬ要因で表示速度の低下や障害等が生じた場合も同様とします。
  3. 弊社は、ユーザーによって投稿されるユーザー情報を監視したり、削除する義務を負いません。
  4. 弊社は、ユーザーによって投稿されるユーザー情報を保存する義務を負いません。
  5. 弊社は、本サービス上に弊社又は第三者の広告を掲載することができます。
  6. 弊社は、ユーザーによって投稿されるユーザー情報の合法性、道徳性、信頼性、正確性について責任を負いません。ユーザーによって投稿されるユーザー情報が、当該ユーザーが所属する法人・団体等の内部規則等に適合することについても、責任を負いません。
  7. 弊社は、ユーザーが本サービスで目にする可能性のある不快、不適切、わいせつ、不法、その他の好ましくないコンテンツや情報について一切責任を負いません。
  8. 弊社は、次に掲げる場合には、当該ユーザー情報の内容を閲覧したり、保存したり、第三者に開示したり、削除すること(以下、本項において「閲覧等」といいます)ができるものとします。弊社は、それによってユーザーに生じたいかなる損害についても、一切責任を負いません。
    (1) 本サービスの技術的不具合の原因を解明し、解消するため必要な場合。
    (2) 裁判所や警察などの公的機関から、法令に基づく正式な照会を受けた場合。
    (3) 本規約に違反する行為又はそのおそれのある行為が行われ、ユーザー情報の内容を確認する必要が生じたと弊社が判断した場合。
    (4) 人の生命、身体及び財産などに差し迫った危険があり、緊急の必要性があると弊社が判断した場合。
    (5) その他、本サービスを適切に運営するために必要が生じた場合。
  9. 弊社は、本規約又はその他の利用規約等に違反する行為又はそのおそれのある行為が行われたと判断した場合には、当該行為を行ったユーザーの強制利用停止処分、ユーザー情報の全部若しくは一部の削除、及び公開範囲の変更等を行う場合がありますが、それによって生じたいかなる損害についても、一切責任を負いません。
  10. 弊社は、次の各号に該当する場合には、ユーザーへの事前の通知や承諾なく、本サービスの一時的な停止を行うことがあり、ユーザーはこれを予め承諾するものとします。この場合、本サービスの停止により生じたユーザーの損害につき、弊社は一切責任を負いません。
    (1) 本サービスにかかるサーバーの保守又は本サービスの仕様変更若しくはシステム上の瑕疵の修補等を行う場合。
    (2) 天変地異その他非常事態が発生し、若しくは発生するおそれがあり、又は法令等の改正・成立により本サービスの運営が困難又は不可能になった場合。
    (3) その他やむを得ない事由により、本サービスの運営上一時的な停止が必要と当社が判断した場合。
  11. 本規約又はその他の利用規約等が消費者契約法(平成 12 年法律第 61 号)第 2 条 第 3 項の消費者契約に該当する場合には、本利用規約及びその他の利用規約等のうち、弊社の損害賠償責任を完全に免責する規定は適用されないものとします。この場合においてユーザーに発生した損害が弊社の債務不履行又は不法行為に基づくときは、弊社は、当該ユーザーが直接被った損害を上限として損害賠償責任を負うものとします。但し、弊社に故意又は重過失がある場合に限ります。

第 13 条 本規約及びその他の利用規約等の有効性

  1. 本規約及びその他の利用規約等の規定の一部が法令に基づいて無効と判断されても、本規約及びその他の利用規約等のその他の規定は有効とします。
  2. 本規約及びその他の利用規約等の規定の一部があるユーザーとの関係で無効とされ、又は取り消された場合でも、当該部分はその他のユーザーとの関係では有効とします。

第 14 条 本規約又はその他の利用規約等違反行為への対処

  1. 本規約又はその他の利用規約等に違反する行為を発見した場合には、お問い合わせリンクから通報してください。但し、個別のサービスにおいて、特に、運営事務局に通報するためのリンクが設けられている場合には、当該リンク先から通報してください。但し、弊社は、かかる問い合わせに対し、回答、対処、その他の対応を行う義務を負いません。
  2. ユーザーが本規約又はその他の利用規約等に違反したと認められる場合、その他弊社が必要と認める場合は、弊社は当該ユーザーに対し以下の対処を講ずることがあります。
    (1) 本規約又はその他の利用規約等に違反する行為等を止め、同様の行為を繰り返さないことを要求すること。
    (2) ユーザー情報の自発的削除・訂正を求めること。
    (3) ユーザー情報の全部若しくは一部を削除し、公開範囲を変更し、又は閲覧できない状態(非公開)にすること。
    (4) 強制利用停止処分とすること。
  3. ユーザーは、弊社が本規約に基づいて行った本規約又はその他の利用規約等に違反する行為等への対処について、異議を申し立てることはできないものとします。

第 15 条 争議

  1. 本規約の準拠法は、日本法とします。
  2. 本規約又は本サービスに起因又は関連する紛争については、東京地方裁判所を第一審の専属的合意管轄裁判所とします。
  3. 本サービスでのユーザーの行為、又はユーザー情報に関して、第三者が弊社に対して請求、クレーム、異議の申し立てを行った場合、ユーザーは、かかる請求等に関して自己の責任において解決するものとし、弊社をあらゆる被害、損失、及び費用負担(妥当な弁護士費用を含む)から免責するものとします。弊社はユーザーの行動について規定を設けていますが、本サービス上でのユーザーの活動を管理又は指揮する責任を負うものではなく、本サービスでのユーザーの行為又はユーザー情報について一切責任を負いません。

第 16 条 その他

  1. 本規約に施行不能な部分が見つかった場合でも、残りの部分は有効に存続するものとします。
  2. 弊社が本規約のいずれかの条項を施行しなかった場合でも、それは放棄とはみなされないものとします。
  3. ユーザーは、弊社の同意を得ることなく、本規約における権利又は義務を他人に譲渡することはできません。
  4. 本規約における弊社のすべての権利及び義務は、合併、買収、又は素材の販売に関連して、あるいは法律の規定に基づき 弊社が自由に譲渡できるものとします。
  5. 本規約の内容は弊社による法令遵守を何ら妨げないものとします。
  6. 本規約により、第三者受益権が与えられることは一切ありません。
  7. 弊社はユーザーに対して明示的に許諾されていない権利を留保します。
  8. ユーザーは、本サービスを利用又はアクセスする際、あらゆる準拠法に従うものとします。

附則

  1. 本利用規約は 2015年1月20日 から施行します。
  2. 本利用規約の施行前にユーザーによって行われた行為についても本利用規約が適用されます。
================================================ FILE: config/mailer.yml.example ================================================ defaults: &defaults development: default_url_options: host: "localhost" port: "3000" test: default_url_options: host: "localhost" port: "3000" production: default_url_options: host: "example.com" delivery_method: :smtp smtp_settings: enable_starttls_auto: true address: "smtp.gmail.com" port: 587 domain: "example.com" authentication: "plain" user_name: "@gmail.com" password: "" ================================================ FILE: config/routes.rb ================================================ Rails.application.routes.draw do root 'static_pages#top' get 'terms' => 'static_pages#terms' get 'dashboard' => 'dashboard#index' devise_for :users, controllers: { registrations: 'users/registrations' } get 'user_settings/edit' patch 'user_settings/update' resources :tests, path: 't', param: 'slug', except: ['index'] do member do get 'move' get 'description' get 'result_label' patch 'update_result_label' patch 'user_association' end collection do patch 'bulk_move' patch 'bulk_destroy' end resources :testcases, path: 'cases', only: ['update'] end resources :teams, param: 'name', except: ['index'] do get 'settings', on: :member resources :team_users do get 'ajax_search_user', on: :collection end resources :projects, param: 'project_name', except: ['index'] do get 'settings', on: :member end end mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development? end ================================================ FILE: config/secrets.yml ================================================ # Be sure to restart your server when you modify this file. # Your secret key is used for verifying the integrity of signed cookies. # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. # You can use `rake secret` to generate a secure secret key. # Make sure the secrets in this file are kept private # if you're sharing your code publicly. development: secret_key_base: 114d4d9fd1ab0b977e3ad1cc31a9bf198a8c1ddb8073d9afdbf69e290f72e250a7a3847516ddf7b7d4ac66a472ba1cbdefb7d49c46928e2a32ada66169dfed71 test: secret_key_base: af784b6579303fab5e4b19d1cdfa9479e0468befcbd75929f8d8f520f04a4329b5ee29466271bb5d5a3f281bac4c30aa920339296e9363602e281c3ba7b0bbe4 # Do not keep production secrets in the repository, # instead read values from the environment. production: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> ================================================ FILE: config.ru ================================================ # This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) run Rails.application ================================================ FILE: db/migrate/20151224022612_devise_create_users.rb ================================================ class DeviseCreateUsers < ActiveRecord::Migration def change create_table(:users) do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable t.integer :sign_in_count, default: 0, null: false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip ## Confirmable t.string :confirmation_token t.datetime :confirmed_at t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true add_index :users, :confirmation_token, unique: true # add_index :users, :unlock_token, unique: true end end ================================================ FILE: db/migrate/20151224030646_create_teams.rb ================================================ class CreateTeams < ActiveRecord::Migration def change create_table :teams do |t| t.string :name, null: false t.integer :user_id t.timestamps null: false end add_index :teams, :name end end ================================================ FILE: db/migrate/20151225045046_create_projects.rb ================================================ class CreateProjects < ActiveRecord::Migration def change create_table :projects do |t| t.string :name, null: false t.integer :user_id t.integer :team_id t.timestamps null: false end add_index :projects, :name add_index :projects, [:name, :team_id], unique: true end end ================================================ FILE: db/migrate/20151225141534_create_tests.rb ================================================ class CreateTests < ActiveRecord::Migration def change create_table :tests do |t| t.string :slug, null: false t.string :title t.text :description t.text :result_labels t.integer :user_id t.integer :project_id t.timestamps null: false end add_index :tests, :slug end end ================================================ FILE: db/migrate/20151226051709_create_testcases.rb ================================================ class CreateTestcases < ActiveRecord::Migration def change create_table :testcases do |t| t.integer :heading_level t.text :body t.string :result t.text :note t.integer :test_id t.timestamps null: false end add_index :testcases, [:result, :test_id] end end ================================================ FILE: db/migrate/20160103121144_add_column_to_user.rb ================================================ class AddColumnToUser < ActiveRecord::Migration def change add_column :users, :username, :string add_column :users, :timezone, :string add_column :users, :locale, :string end end ================================================ FILE: db/migrate/20160109162956_create_team_users.rb ================================================ class CreateTeamUsers < ActiveRecord::Migration def change create_table :team_users do |t| t.integer :team_id t.integer :user_id t.timestamps null: false end add_index :team_users, :team_id add_index :team_users, :user_id add_index :team_users, [:team_id, :user_id], unique: true end end ================================================ FILE: db/migrate/20160319072222_add_created_at_index_to_test.rb ================================================ class AddCreatedAtIndexToTest < ActiveRecord::Migration def change add_index :tests, :created_at add_index :tests, :updated_at end end ================================================ FILE: db/schema.rb ================================================ # encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # # Note that this schema.rb definition is the authoritative source for your # database schema. If you need to create the application database on another # system, you should be using db:schema:load, not running all the migrations # from scratch. The latter is a flawed and unsustainable approach (the more migrations # you'll amass, the slower it'll run and the greater likelihood for issues). # # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 20160319072222) do create_table "projects", force: :cascade do |t| t.string "name", null: false t.integer "user_id" t.integer "team_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end add_index "projects", ["name", "team_id"], name: "index_projects_on_name_and_team_id", unique: true add_index "projects", ["name"], name: "index_projects_on_name" create_table "team_users", force: :cascade do |t| t.integer "team_id" t.integer "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end add_index "team_users", ["team_id", "user_id"], name: "index_team_users_on_team_id_and_user_id", unique: true add_index "team_users", ["team_id"], name: "index_team_users_on_team_id" add_index "team_users", ["user_id"], name: "index_team_users_on_user_id" create_table "teams", force: :cascade do |t| t.string "name", null: false t.integer "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end add_index "teams", ["name"], name: "index_teams_on_name" create_table "testcases", force: :cascade do |t| t.integer "heading_level" t.text "body" t.string "result" t.text "note" t.integer "test_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end add_index "testcases", ["result", "test_id"], name: "index_testcases_on_result_and_test_id" create_table "tests", force: :cascade do |t| t.string "slug", null: false t.string "title" t.text "description" t.text "result_labels" t.integer "user_id" t.integer "project_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end add_index "tests", ["created_at"], name: "index_tests_on_created_at" add_index "tests", ["slug"], name: "index_tests_on_slug" add_index "tests", ["updated_at"], name: "index_tests_on_updated_at" create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "unconfirmed_email" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "username" t.string "timezone" t.string "locale" end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true add_index "users", ["email"], name: "index_users_on_email", unique: true add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end ================================================ FILE: db/seeds.rb ================================================ user = User.create(email: "test@example.com", password: "test") user.skip_confirmation! user.save ================================================ FILE: lib/assets/.keep ================================================ ================================================ FILE: lib/tasks/.keep ================================================ ================================================ FILE: log/.keep ================================================ ================================================ FILE: public/404.en.html ================================================ 404 | Chibineko

404

We can't find the page you're looking for.

The page you are looking for might have been moved or deleted.

Go to the top
================================================ FILE: public/404.ja.html ================================================ 404 | Chibineko

404

ページが見つかりませんでした。

お探しのページは移動されたか削除された可能性があります。
お手数ですがトップページからアクセスし直してください。

トップページへ
================================================ FILE: public/422.en.html ================================================ 422 | Chibineko

422

A server error occurred.

Try again. If the problem isn't solved yet, please contact our support.

Go to the top
================================================ FILE: public/422.ja.html ================================================ 422 | Chibineko

422

エラーが発生しました。

繰り返し同様の操作をしても改善しない場合、お手数ですが
Chibinekoサポートまでお問い合わせください。

トップページへ
================================================ FILE: public/500.en.html ================================================ 500 | Chibineko

500

A server error occurred.

Try again later. If the problem isn't solved yet, please contact our support.

Go to the top
================================================ FILE: public/500.ja.html ================================================ 500 | Chibineko

500

エラーが発生しました。

しばらく時間を置いても解決しない場合、お手数ですが
Chibinekoサポートまでお問い合わせください。

トップページへ
================================================ FILE: public/error.css ================================================ body { text-align: center; padding-top: 60px; color: #555; } .dialog { display: inline-block; text-align: left; padding-bottom: 50px; } .symbol { display: block; margin-bottom: 30px; text-align: center; font-size: 250px; } h1 { margin-top: 0; margin-bottom: 10px; font-size: 48px; font-weight: bold; } h2 { margin-top: 0; margin-bottom: 30px; font-size: 36px; font-weight: bold; } p { font-size: 18px; } a { color: #337ab7; text-decoration: none; font-size: 18px; } .top-link { display: inline-block; margin-top: 20px; } ================================================ FILE: public/javascripts/translations.js ================================================ var I18n = I18n || {}; I18n.translations = {"en":{"js":{"team_sidebar":{"edit_members":{"search_user":"Search user...","add_user":"Add this user to the team","not_found":"User not found","unknown_error":"Unknown error"}},"tests":{"show":{"errors":{"conflict":"Test cases might have been editted by other uses.","please_reload":"Reload the page"}},"new":{"messages":{"unsaved_changes":"You have unsaved changes."}},"edit_result_label":{"errors":{"too_short":"You can not delete any more.","too_long":"You can not add any more."}},"navbar":{"total":"Total"},"toolbar":{"selected":"selected"}},"bootgrid":{"loading":"Loading...","no_results":"No results found.","search":"Search by title"},"common":{"messages":{"delete_confirm":"Are you sure? You can't undo the operation."},"collapse":{"expand":"edit","close":"close"},"ladda":{"saving":"saving...","processing":"processing..."}}}},"ja":{"js":{"team_sidebar":{"edit_members":{"search_user":"検索中...","add_user":"ユーザーをチームに追加","not_found":"ユーザーが見つかりません","unknown_error":"エラーが発生しました"}},"tests":{"show":{"errors":{"conflict":"テスト項目が他のユーザーによって変更された可能性があります","please_reload":"ページをリロードしてください"}},"new":{"messages":{"unsaved_changes":"変更内容が保存されていません。"}},"edit_result_label":{"errors":{"too_short":"これ以上削除できません。","too_long":"これ以上追加できません。"}},"navbar":{"total":"合計"},"toolbar":{"selected":"件選択中"}},"bootgrid":{"loading":"読み込み中...","no_results":"検索結果がありません","search":"タイトルで検索"},"common":{"messages":{"delete_confirm":"本当に削除しますか?この操作は取り消せません。"},"collapse":{"expand":"編集","close":"閉じる"},"ladda":{"saving":"保存中...","processing":"処理中..."}}}}}; ================================================ FILE: public/maintenance.html ================================================ 503 | Chibineko

503

Server Unavailable.

The site is temporarily down for maintenance.
Please check back soon.

================================================ FILE: public/robots.txt ================================================ # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file # # To ban all spiders from the entire site uncomment the next two lines: # User-agent: * # Disallow: / ================================================ FILE: spec/factories/testcases.rb ================================================ FactoryGirl.define do factory :testcase do end end ================================================ FILE: spec/factories/tests.rb ================================================ FactoryGirl.define do factory :test do sequence(:title) { |n| "test_title_#{n}" } end end ================================================ FILE: spec/models/test_spec.rb ================================================ require 'rails_helper' RSpec.describe Test, type: :model do let(:test) { create(:test) } let(:labels) { result_labels = I18n.t("tests.result_labels") { result_labels[:unexecuted] => "white", result_labels[:pass] => "green", result_labels[:fail] => "red", result_labels[:blocked] => "orange", result_labels[:na] => "gray", } } describe '#result_labels_or_default' do it 'returns labels' do expect(test.result_labels_or_default).to eq(labels) end it 'memoize labels' do labels = I18n.t("tests.result_labels") expected_labels = { labels[:memoized_key] => "memoized_value" } test.result_labels = expected_labels expect(test.result_labels_or_default).to eq(expected_labels) end end describe '#result_label_texts' do it 'returns label keys' do expect(test.result_label_texts).to eq(labels.keys) end end describe '#result_label_colors' do it 'returns label values' do expect(test.result_label_colors).to eq(labels.values) end end describe '#testcase_groups' do let(:group_a) { [build(:testcase, heading_level: 1), build(:testcase, heading_level: 0)] } let(:group_b) { [build(:testcase, heading_level: 1), build(:testcase, heading_level: 0)] } it 'groups testcases' do test.testcases = [group_a, group_b].flatten expect(test.testcase_groups).to eq([group_a, group_b]) end end describe '#set_markdown' do let(:testcases) { [ build(:testcase, heading_level: 1, body: 'heading_level_1'), build(:testcase, heading_level: 2, body: 'heading_level_2'), build(:testcase, heading_level: 0, body: 'testcase', result: 'test_result', note: 'test_note'), build(:testcase, heading_level: -1, body: 'blank') ] } context 'with with_result option' do it 'sets testcases as markdown with result' do test.testcases = testcases expect(test.set_markdown(true)).to eq("# heading_level_1\n## heading_level_2\ntestcase, [test_result], test_note\n") end end context 'without with_result option' do it 'sets testcases as markdown' do test.testcases = testcases expect(test.set_markdown).to eq("# heading_level_1\n## heading_level_2\ntestcase\n") end end end describe '#make_testcase' do let(:markdown) { "# heading_level_1\n## heading_level_2\ntestcase, [test_result], test_note\n" } it 'creates testcases form markdown' do test.testcases.delete_all test.markdown = markdown test.make_testcase expect(test.testcases.any? {|tc| tc.heading_level == 1 && tc.body == "heading_level_1" }).to be_truthy expect(test.testcases.any? {|tc| tc.heading_level == 2 && tc.body == "heading_level_2" }).to be_truthy expect(test.testcases.any? {|tc| tc.heading_level == 0 && tc.body == "testcase" && tc.result == "test_result" && tc.note == "test_note" }).to be_truthy end end end ================================================ FILE: spec/models/testcase_spec.rb ================================================ require 'rails_helper' RSpec.describe Testcase, type: :model do let(:testcase) { create(:testcase, test: create(:test)) } let(:testcases) { [testcase] } describe '#type' do context 'with type -1' do it 'returns :blnak' do testcase.heading_level = -1 expect(testcase.type).to be(:blank) end end context 'with type 0' do it 'returns :blnak' do testcase.heading_level = 0 expect(testcase.type).to be(:testcase) end end context 'with other type' do it 'returns :heading' do testcase.heading_level = 1 expect(testcase.type).to be(:heading) end end end describe '#result_color' do context 'with valid result' do it 'returns matching color' do testcase.result = 'PASS' expect(testcase.result_color).to eq('green') testcase.result = 'NOT RUN' expect(testcase.result_color).to eq('white') testcase.result = 'FAIL' expect(testcase.result_color).to eq('red') testcase.result = 'BLOCKED' expect(testcase.result_color).to eq('orange') testcase.result = 'N/A' expect(testcase.result_color).to eq('gray') end end context 'with invalid result' do it 'returns white color' do testcase.result = 'INVALID_COLOR' expect(testcase.result_color).to eq('white') end end end describe 'heading_level' do context 'with blank' do it 'returns -1' do expect(Testcase.heading_level('')).to eq(-1) end end context 'start with #' do it 'returns heading_level' do expect(Testcase.heading_level('#level1')).to eq(1) expect(Testcase.heading_level('##level2')).to eq(2) end end context 'not start with #' do it 'returns 0' do expect(Testcase.heading_level('test')).to eq(0) end end end describe 'body' do context 'with blank' do it 'returns nil' do expect(Testcase.body('')).to eq(nil) end end context 'with head' do it 'returns body removed #' do expect(Testcase.body('#level1')).to eq('level1') expect(Testcase.body('##level2')).to eq('level2') end end context 'with no head' do it 'returns body' do text = 'testcase, [PASS], test_note' expect(Testcase.body(text)).to eq('testcase') end end end describe 'result' do context 'with blank' do it 'returns nil' do expect(Testcase.result('')).to eq(nil) end end context 'with valid text' do it 'returns result' do expect(Testcase.result('testcase, [PASS], test_note')).to eq('PASS') end end context 'with invalid text' do it 'returns nil' do expect(Testcase.result('invalid text')).to eq(nil) end end end describe 'note' do context 'with blank' do it 'returns nil' do expect(Testcase.note('')).to eq(nil) end end context 'with valid text' do it 'returns note' do expect(Testcase.note('testcase, [PASS], test_note')).to eq('test_note') end end context 'with invalid text' do it 'returns nil' do expect(Testcase.note('invalid text')).to eq(nil) end end end end ================================================ FILE: spec/rails_helper.rb ================================================ # This file is copied to spec/ when you run 'rails generate rspec:install' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require 'spec_helper' require 'rspec/rails' require 'factory_girl_rails' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end # in _spec.rb will both be required and run as specs, causing the specs to be # run twice. It is recommended that you do not name files matching this glob to # end with _spec.rb. You can configure this pattern with the --pattern # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. # # The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } # Checks for pending migration and applies them before tests are run. # If you are not using ActiveRecord, you can remove this line. ActiveRecord::Migration.maintain_test_schema! RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and # `post` in specs under `spec/controllers`. # # You can disable this behaviour by removing the line below, and instead # explicitly tag your specs with their type, e.g.: # # RSpec.describe UsersController, :type => :controller do # # ... # end # # The different available types are documented in the features, such as in # https://relishapp.com/rspec/rspec-rails/docs config.infer_spec_type_from_file_location! # Filter lines from Rails gems in backtraces. config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") config.include FactoryGirl::Syntax::Methods end ================================================ FILE: spec/spec_helper.rb ================================================ # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause # this file to always be loaded, without a need to explicitly require it in any # files. # # Given that it is always loaded, you are encouraged to keep this file as # light-weight as possible. Requiring heavyweight dependencies from this file # will add to the boot time of your test suite on EVERY test run, even for an # individual file that may not need all of that loaded. Instead, consider making # a separate helper file that requires the additional dependencies and performs # the additional setup, and require it from the spec files that actually need # it. # # The `.rspec` file also contains a few flags that are not defaults but that # users commonly want. # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer. config.expect_with :rspec do |expectations| # This option will default to `true` in RSpec 4. It makes the `description` # and `failure_message` of custom matchers include text for helper methods # defined using `chain`, e.g.: # be_bigger_than(2).and_smaller_than(4).description # # => "be bigger than 2 and smaller than 4" # ...rather than: # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true end # rspec-mocks config goes here. You can use an alternate test double # library (such as bogus or mocha) by changing the `mock_with` option here. config.mock_with :rspec do |mocks| # Prevents you from mocking or stubbing a method that does not exist on # a real object. This is generally recommended, and will default to # `true` in RSpec 4. mocks.verify_partial_doubles = true end # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. =begin # These two settings work together to allow you to limit a spec run # to individual examples or groups you care about by tagging them with # `:focus` metadata. When nothing is tagged with `:focus`, all examples # get run. config.filter_run :focus config.run_all_when_everything_filtered = true # Allows RSpec to persist some state between runs in order to support # the `--only-failures` and `--next-failure` CLI options. We recommend # you configure your source control system to ignore this file. config.example_status_persistence_file_path = "spec/examples.txt" # Limits the available syntax to the non-monkey patched syntax that is # recommended. For more details, see: # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode config.disable_monkey_patching! # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an # individual spec file. if config.files_to_run.one? # Use the documentation formatter for detailed output, # unless a formatter has already been configured # (e.g. via a command-line flag). config.default_formatter = 'doc' end # Print the 10 slowest examples and example groups at the # end of the spec run, to help surface which specs are running # particularly slow. config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = :random # Seed global randomization in this process using the `--seed` CLI option. # Setting this allows you to use `--seed` to deterministically reproduce # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed =end end ================================================ FILE: vendor/assets/javascripts/.keep ================================================ ================================================ FILE: vendor/assets/javascripts/jquery.readyselector.js ================================================ (function ($) { var ready = $.fn.ready; $.fn.ready = function (fn) { if (this.context === undefined) { // The $().ready(fn) case. ready(fn); } else if (this.selector) { ready($.proxy(function(){ $(this.selector, this.context).each(fn); }, this)); } else { ready($.proxy(function(){ $(this).each(fn); }, this)); } } })(jQuery); ================================================ FILE: vendor/assets/stylesheets/.keep ================================================