Commit 13804aba authored by Rémy Coutable's avatar Rémy Coutable

Continue implementation of the license template selector and /licenses API endpoint

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parent 073c3d15
...@@ -31,6 +31,8 @@ v 8.7.0 (unreleased) ...@@ -31,6 +31,8 @@ v 8.7.0 (unreleased)
- Fix a bug whith trailing slash in teamcity_url (Charles May) - Fix a bug whith trailing slash in teamcity_url (Charles May)
- Allow back dating on issues when created or updated through the API - Allow back dating on issues when created or updated through the API
- Allow back dating on issue notes when created through the API - Allow back dating on issue notes when created through the API
- Propose license template when creating a new LICENSE file
- API: Expose /licenses and /licenses/:key
- Fix avatar stretching by providing a cropping feature - Fix avatar stretching by providing a cropping feature
- API: Expose `subscribed` for issues and merge requests (Robert Schilling) - API: Expose `subscribed` for issues and merge requests (Robert Schilling)
- Allow SAML to handle external users based on user's information !3530 - Allow SAML to handle external users based on user's information !3530
......
...@@ -93,17 +93,15 @@ ...@@ -93,17 +93,15 @@
).done (projects) -> ).done (projects) ->
callback(projects) callback(projects)
# Return text for specific license # Return text for a specific license
licenseText: (key, fullname, callback) -> licenseText: (key, data, callback) ->
url = Api.buildUrl(Api.license_path) url = Api.buildUrl(Api.license_path).replace(':key', key)
url = url.replace(':key', key)
$.ajax( $.ajax(
url: url url: url
data: data: data
fullname: fullname ).done (license) ->
).done (projects) -> callback(license)
callback(projects)
buildUrl: (url) -> buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root? url = gon.relative_url_root + url if gon.relative_url_root?
......
class @BlobLicenseSelector class @BlobLicenseSelector
licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i
constructor: (editor)-> constructor: (editor) ->
self = this @$licenseSelector = $('.js-license-selector')
@licenseSelector = $('.js-license-selector') $fileNameInput = $('#file_name')
@toggleLicenseSelector($('#file_name').val())
$('#file_name').on 'input', -> initialFileNameValue = if $fileNameInput.length
self.toggleLicenseSelector($(this).val()) $fileNameInput.val()
else if $('.editor-file-name').length
$('.editor-file-name').text().trim()
$('select.license-select').select2( @toggleLicenseSelector(initialFileNameValue)
width: 'resolve'
dropdownAutoWidth: true if $fileNameInput
placeholder: 'Choose a license template' $fileNameInput.on 'keyup blur', (e) =>
).on 'change', (e) -> @toggleLicenseSelector($(e.target).val())
Api.licenseText $(this).val(), $(this).data('fullname'), (data) ->
editor.setValue(data, -1) $('select.license-select').on 'change', (e) ->
data =
project: $(this).data('project')
fullname: $(this).data('fullname')
Api.licenseText $(this).val(), data, (license) ->
editor.setValue(license.content, -1)
toggleLicenseSelector: (fileName) => toggleLicenseSelector: (fileName) =>
if @licenseRegex.test(fileName) if @licenseRegex.test(fileName)
@licenseSelector.show() @$licenseSelector.show()
else else
@licenseSelector.hide() @$licenseSelector.hide()
class @EditBlob class @EditBlob
constructor: (assets_path, mode)-> constructor: (assets_path, ace_mode = null) ->
ace.config.set "modePath", assets_path + '/ace' ace.config.set "modePath", "#{assets_path}/ace"
ace.config.loadModule "ace/ext/searchbox" ace.config.loadModule "ace/ext/searchbox"
if mode @editor = ace.edit("editor")
ace_mode = mode @editor.focus()
editor = ace.edit("editor") @editor.getSession().setMode "ace/mode/#{ace_mode}" if ace_mode
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
# Before a form submission, move the content from the Ace editor into the # Before a form submission, move the content from the Ace editor into the
# submitted textarea # submitted textarea
$('form').submit -> $('form').submit =>
$("#file-content").val(editor.getValue()) $("#file-content").val(@editor.getValue())
@initModePanesAndLinks()
new BlobLicenseSelector(@editor)
editModePanes = $(".js-edit-mode-pane") initModePanesAndLinks: ->
editModeLinks = $(".js-edit-mode a") @$editModePanes = $(".js-edit-mode-pane")
editModeLinks.click (event) -> @$editModeLinks = $(".js-edit-mode a")
event.preventDefault() @$editModeLinks.click @editModeLinkClickHandler
currentLink = $(this)
paneId = currentLink.attr("href")
currentPane = editModePanes.filter(paneId)
editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
editModePanes.hide()
if paneId is "#preview"
currentPane.fadeIn 200
$.post currentLink.data("preview-url"),
content: editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
return
else editModeLinkClickHandler: (event) =>
currentPane.fadeIn 200 event.preventDefault()
editor.focus() currentLink = $(event.target)
return paneId = currentLink.attr("href")
currentPane = @$editModePanes.filter(paneId)
@$editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
@$editModePanes.hide()
currentPane.fadeIn 200
if paneId is "#preview"
$.post currentLink.data("preview-url"),
content: @editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
editor: -> else
return @editor @editor.focus()
class @NewBlob
constructor: (assets_path, mode)->
ace.config.set "modePath", assets_path + '/ace'
ace.config.loadModule "ace/ext/searchbox"
if mode
ace_mode = mode
editor = ace.edit("editor")
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
# Before a form submission, move the content from the Ace editor into the
# submitted textarea
$('form').submit ->
$("#file-content").val(editor.getValue())
editor: ->
return @editor
...@@ -122,15 +122,6 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -122,15 +122,6 @@ class Projects::BlobController < Projects::ApplicationController
end end
def editor_variables def editor_variables
@licenses = {
'Popular' => Licensee::License.all(featured: true).map!{ |license| [license.name, license.key] },
'Other' => Licensee::License.all(featured: false).map!{ |license| [license.name, license.key] }
}
unless @repository.empty?
@current_license_key = Licensee.license(@repository.path).try(:key)
end
@target_branch = params[:target_branch] @target_branch = params[:target_branch]
@file_path = @file_path =
......
...@@ -173,4 +173,15 @@ module BlobHelper ...@@ -173,4 +173,15 @@ module BlobHelper
response.etag = @blob.id response.etag = @blob.id
!stale !stale
end end
def licenses_for_select
return @licenses_for_select if defined?(@licenses_for_select)
licenses = Licensee::License.all
@licenses_for_select = {
Popular: licenses.select(&:featured).map { |license| [license.name, license.key] },
Other: licenses.reject(&:featured).map { |license| [license.name, license.key] }
}
end
end end
...@@ -216,40 +216,14 @@ module ProjectsHelper ...@@ -216,40 +216,14 @@ module ProjectsHelper
end end
end end
def add_contribution_guide_path(project) def add_special_file_path(project, file_name:, commit_message: nil)
if project && !project.repository.contribution_guide namespace_project_new_blob_path(
namespace_project_new_blob_path( project.namespace,
project.namespace, project,
project, project.default_branch || 'master',
project.default_branch, file_name: file_name,
file_name: "CONTRIBUTING.md", commit_message: commit_message || "Add #{file_name.downcase}"
commit_message: "Add contribution guide" )
)
end
end
def add_changelog_path(project)
if project && !project.repository.changelog
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "CHANGELOG",
commit_message: "Add changelog"
)
end
end
def add_license_path(project)
if project && !project.repository.license
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "LICENSE",
commit_message: "Add license"
)
end
end end
def contribution_guide_path(project) def contribution_guide_path(project)
...@@ -272,7 +246,7 @@ module ProjectsHelper ...@@ -272,7 +246,7 @@ module ProjectsHelper
end end
def license_path(project) def license_path(project)
filename_path(project, :license) filename_path(project, :license_blob)
end end
def version_path(project) def version_path(project)
...@@ -342,6 +316,12 @@ module ProjectsHelper ...@@ -342,6 +316,12 @@ module ProjectsHelper
@ref || @repository.try(:root_ref) @ref || @repository.try(:root_ref)
end end
def license_short_name(project)
license = Licensee::License.new(project.repository.license_key)
license.nickname || license.name
end
private private
def filename_path(project, filename) def filename_path(project, filename)
......
...@@ -228,7 +228,8 @@ class Repository ...@@ -228,7 +228,8 @@ class Repository
def cache_keys def cache_keys
%i(size branch_names tag_names commit_count %i(size branch_names tag_names commit_count
readme version contribution_guide changelog license) readme version contribution_guide changelog
license_blob license_key)
end end
def build_cache def build_cache
...@@ -461,27 +462,21 @@ class Repository ...@@ -461,27 +462,21 @@ class Repository
end end
end end
def license def license_blob
cache.fetch(:license) do return nil if !exists? || empty?
licenses = tree(:head).blobs.find_all do |file|
file.name =~ /\A(copying|license|licence)/i
end
preferences = [
/\Alicen[sc]e\z/i, # LICENSE, LICENCE
/\Alicen[sc]e\./i, # LICENSE.md, LICENSE.txt
/\Acopying\z/i, # COPYING
/\Acopying\.(?!lesser)/i, # COPYING.txt
/Acopying.lesser/i # COPYING.LESSER
]
license = nil cache.fetch(:license_blob) do
preferences.each do |r| if licensee_project.license
license = licenses.find { |l| l.name =~ r } blob_at_branch(root_ref, licensee_project.matched_file.filename)
break if license
end end
end
end
license def license_key
return nil if !exists? || empty?
cache.fetch(:license_key) do
licensee_project.license.try(:key) || 'no-license'
end end
end end
...@@ -925,4 +920,8 @@ class Repository ...@@ -925,4 +920,8 @@ class Repository
def cache def cache
@cache ||= RepositoryCache.new(path_with_namespace) @cache ||= RepositoryCache.new(path_with_namespace)
end end
def licensee_project
@licensee_project ||= Licensee.project(path)
end
end end
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- else - else
.gray-content-block.second-block.center .gray-content-block.second-block.center
%h3.page-title %h3.page-title
This project does not have README yet This project does not have a README yet
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
%p %p
A A
...@@ -18,5 +18,5 @@ ...@@ -18,5 +18,5 @@
distributed with computer software, forming part of its documentation. distributed with computer software, forming part of its documentation.
%p %p
We recommend you to We recommend you to
= link_to "add README", new_readme_path, class: 'underlined-link' = link_to "add a README", new_readme_path, class: 'underlined-link'
file to the repository and GitLab will render it here instead of this message. file to the repository and GitLab will render it here instead of this message.
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.pull-right .pull-right
.license-selector.js-license-selector.hide .license-selector.js-license-selector.hide
= select_tag :license_type, grouped_options_for_select(@licenses, @current_license_key), include_blank: true, class: 'select2 license-select', data: { fullname: @repository.project.creator.name } = select_tag :license_type, grouped_options_for_select(licenses_for_select, @project.repository.license_key), include_blank: true, class: 'select2 license-select', data: {placeholder: 'Choose a license template', project: @project.name, fullname: @project.namespace.human_name}
.encoding-selector .encoding-selector
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2' = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
...@@ -26,8 +26,3 @@ ...@@ -26,8 +26,3 @@
.center .center
%h2 %h2
%i.icon-spinner.icon-spin %i.icon-spinner.icon-spin
:javascript
window.onload = function() {
new BlobLicenseSelector(blob.editor)
}
...@@ -14,5 +14,5 @@ ...@@ -14,5 +14,5 @@
cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) cancel_path: namespace_project_tree_path(@project.namespace, @project, @id)
:javascript :javascript
blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null) blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}")
new NewCommitForm($('.js-new-blob-form')) new NewCommitForm($('.js-new-blob-form'))
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
%p %p
If you already have files you can push them using command line instructions below. If you already have files you can push them using command line instructions below.
%p %p
Otherwise you can start with adding Otherwise you can start with adding a
= link_to "README", new_readme_path, class: 'underlined-link' = link_to "README", new_readme_path, class: 'underlined-link'
or or a
= link_to "LICENSE", new_license_path, class: 'underlined-link' = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link'
file to this project. file to this project.
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
......
...@@ -36,9 +36,9 @@ ...@@ -36,9 +36,9 @@
%li %li
= link_to 'Changelog', changelog_path(@project) = link_to 'Changelog', changelog_path(@project)
- if @repository.license - if @repository.license_blob
%li %li
= link_to 'License', license_path(@project) = link_to license_short_name(@project), license_path(@project)
- if @repository.contribution_guide - if @repository.contribution_guide
%li %li
...@@ -47,15 +47,15 @@ ...@@ -47,15 +47,15 @@
- if current_user && can_push_branch?(@project, @project.default_branch) - if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog - unless @repository.changelog
%li.missing %li.missing
= link_to add_changelog_path(@project) do = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
Add Changelog Add Changelog
- unless @repository.license - unless @repository.license_blob
%li.missing %li.missing
= link_to add_license_path(@project) do = link_to add_special_file_path(@project, file_name: 'LICENSE') do
Add License Add License
- unless @repository.contribution_guide - unless @repository.contribution_guide
%li.missing %li.missing
= link_to add_contribution_guide_path(@project) do = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
Add Contribution guide Add Contribution guide
- if @repository.commit - if @repository.commit
......
...@@ -33,6 +33,7 @@ following locations: ...@@ -33,6 +33,7 @@ following locations:
- [Build triggers](build_triggers.md) - [Build triggers](build_triggers.md)
- [Build Variables](build_variables.md) - [Build Variables](build_variables.md)
- [Runners](runners.md) - [Runners](runners.md)
- [Licenses](licenses.md)
## Authentication ## Authentication
......
# Licenses
## List license templates
Get all license templates.
```
GET /licenses
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `popular` | boolean | no | If passed, returns only popular licenses |
```bash
curl https://gitlab.example.com/api/v3/licenses?popular=1
```
Example response:
```json
[
{
"key": "apache-2.0",
"name": "Apache License 2.0",
"nickname": null,
"featured": true,
"html_url": "http://choosealicense.com/licenses/apache-2.0/",
"source_url": "http://www.apache.org/licenses/LICENSE-2.0.html",
"description": "A permissive license that also provides an express grant of patent rights from contributors to users.",
"conditions": [
"include-copyright",
"document-changes"
],
"permissions": [
"commercial-use",
"modifications",
"distribution",
"patent-use",
"private-use"
],
"limitations": [
"trademark-use",
"no-liability"
],
"content": " Apache License\n Version 2.0, January 2004\n [...]"
},
{
"key": "gpl-3.0",
"name": "GNU General Public License v3.0",
"nickname": "GNU GPLv3",
"featured": true,
"html_url": "http://choosealicense.com/licenses/gpl-3.0/",
"source_url": "http://www.gnu.org/licenses/gpl-3.0.txt",
"description": "The GNU GPL is the most widely used free software license and has a strong copyleft requirement. When distributing derived works, the source code of the work must be made available under the same license.",
"conditions": [
"include-copyright",
"document-changes",
"disclose-source",
"same-license"
],
"permissions": [
"commercial-use",
"modifications",
"distribution",
"patent-use",
"private-use"
],
"limitations": [
"no-liability"
],
"content": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n [...]"
},
{
"key": "mit",
"name": "MIT License",
"nickname": null,
"featured": true,
"html_url": "http://choosealicense.com/licenses/mit/",
"source_url": "http://opensource.org/licenses/MIT",
"description": "A permissive license that is short and to the point. It lets people do anything with your code with proper attribution and without warranty.",
"conditions": [
"include-copyright"
],
"permissions": [
"commercial-use",
"modifications",
"distribution",
"private-use"
],
"limitations": [
"no-liability"
],
"content": "The MIT License (MIT)\n\nCopyright (c) [year] [fullname]\n [...]"
}
]
```
## Single license template
Get a single license template. You can pass parameters to replace the license
placeholder.
```
GET /licenses/:key
```
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
| `key` | string | yes | The key of the license template |
| `project` | string | no | The copyrighted project name |
| `fullname` | string | no | The full-name of the copyright holder |
>**Note:**
If you omit the `fullname` parameter but authenticate your request, the name of
the authenticated user will be used to replace the copyright holder placeholder.
```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/licenses/mit?project=My+Cool+Project
```
Example response:
```json
{
"key": "mit",
"name": "MIT License",
"nickname": null,
"featured": true,
"html_url": "http://choosealicense.com/licenses/mit/",
"source_url": "http://opensource.org/licenses/MIT",
"description": "A permissive license that is short and to the point. It lets people do anything with your code with proper attribution and without warranty.",
"conditions": [
"include-copyright"
],
"permissions": [
"commercial-use",
"modifications",
"distribution",
"private-use"
],
"limitations": [
"no-liability"
],
"content": "The MIT License (MIT)\n\nCopyright (c) 2016 John Doe\n [...]"
}
```
...@@ -439,5 +439,16 @@ module API ...@@ -439,5 +439,16 @@ module API
class Variable < Grape::Entity class Variable < Grape::Entity
expose :key, :value expose :key, :value
end end
class License < Grape::Entity
expose :key, :name, :nickname, :featured
expose :url, as: :html_url
expose(:source_url) { |license| license.meta['source'] }
expose(:description) { |license| license.meta['description'] }
expose(:conditions) { |license| license.meta['required'] }
expose(:permissions) { |license| license.meta['permitted'] }
expose(:limitations) { |license| license.meta['forbidden'] }
expose :content
end
end end
end end
module API module API
# Licenses API # Licenses API
class Licenses < Grape::API class Licenses < Grape::API
YEAR_TEMPLATE_REGEX = /(\[|<|{)(year|yyyy)(\]|>|})/ PROJECT_TEMPLATE_REGEX =
FULLNAME_TEMPLATE_REGEX = /\[fullname\]/ /[\<\{\[]
(project|description|
one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
[\>\}\]]/xi
YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i
FULLNAME_TEMPLATE_REGEX =
/[\<\{\[]
(fullname|name\sof\s(author|copyright\sowner))
[\>\}\]]/xi
# Get the list of the available license templates
#
# Parameters:
# popular - Filter licenses to only the popular ones
#
# Example Request:
# GET /licenses
# GET /licenses?popular=1
get 'licenses' do
options = {
featured: params[:popular].present? ? true : nil
}
present Licensee::License.all(options), with: Entities::License
end
# Get text for specific license # Get text for specific license
# #
# Parameters: # Parameters:
# key (required) - The key of a license # key (required) - The key of a license
# fullname - Reository owner fullname # project - Copyrighted project name
# fullname - Full name of copyright holder
#
# Example Request: # Example Request:
# GET /licenses/mit # GET /licenses/mit
get 'licenses/:key', requirements: { key: /[\w.-]*/ } do #
env['api.format'] = :txt get 'licenses/:key', requirements: { key: /[\w\.-]+/ } do
license = Licensee::License.find(params[:key]).try(:text) required_attributes! [:key]
not_found!('License') unless Licensee::License.find(params[:key])
# We create a fresh Licensee::License object since we'll modify its
# content in place below.
license = Licensee::License.new(params[:key])
license.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s)
license.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present?
fullname = params[:fullname].presence || current_user.try(:name)
license.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname
if license present license, with: Entities::License
license
.gsub(YEAR_TEMPLATE_REGEX, Time.now.year.to_s)
.gsub(FULLNAME_TEMPLATE_REGEX, params[:fullname])
else
error!('License not found', 404)
end
end end
end end
end end
require 'spec_helper'
feature 'creates a license file', feature: true, js: true do
include Select2Helper
let(:project_master) { create(:user) }
let(:project) { create(:project) }
background do
project.repository.remove_file(project_master, 'LICENSE', 'Remove LICENSE', 'master')
project.team << [project_master, :master]
login_as(project_master)
visit namespace_project_path(project.namespace, project)
end
scenario 'project master creates a license file manually from a template' do
visit namespace_project_tree_path(project.namespace, project, project.repository.root_ref)
find('.add-to-tree').click
click_link 'New file'
fill_in :file_name, with: 'LICENSE'
expect(page).to have_selector('.license-selector')
select2('mit', from: '#license_type')
file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)')
expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit Changes'
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)')
expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
end
scenario 'project master creates a license file from the "Add license" link' do
click_link 'Add License'
expect(current_path).to eq(
namespace_project_new_blob_path(project.namespace, project, 'master'))
expect(find('#file_name').value).to eq('LICENSE')
expect(page).to have_selector('.license-selector')
select2('mit', from: '#license_type')
file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)')
expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit Changes'
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)')
expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
end
end
require 'spec_helper'
feature 'creates a license file in empty project', feature: true, js: true do
include Select2Helper
let(:project_master) { create(:user) }
let(:project) { create(:project_empty_repo) }
background do
project.team << [project_master, :master]
login_as(project_master)
visit namespace_project_path(project.namespace, project)
end
scenario 'project master creates a license file from a template' do
click_on 'LICENSE'
expect(current_path).to eq(
namespace_project_new_blob_path(project.namespace, project, 'master'))
expect(find('#file_name').value).to eq('LICENSE')
expect(page).to have_selector('.license-selector')
select2('mit', from: '#license_type')
file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)')
expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit Changes'
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)')
expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
end
end
...@@ -135,22 +135,69 @@ describe Repository, models: true do ...@@ -135,22 +135,69 @@ describe Repository, models: true do
end end
describe "#license" do describe '#license_blob' do
before do before do
repository.send(:cache).expire(:license) repository.send(:cache).expire(:license_blob)
repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master')
end end
it 'test selection preference' do it 'looks in the root_ref only' do
files = [TestBlob.new('file'), TestBlob.new('license'), TestBlob.new('copying')] repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'markdown')
expect(repository.tree).to receive(:blobs).and_return(files) repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'markdown', false)
expect(repository.license_blob).to be_nil
end
it 'favors license file with no extension' do
repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'master', false)
repository.commit_file(user, 'LICENSE.md', Licensee::License.new('mit').content, 'Add LICENSE.md', 'master', false)
expect(repository.license_blob.name).to eq('LICENSE')
end
it 'favors .md file to .txt' do
repository.commit_file(user, 'LICENSE.md', Licensee::License.new('mit').content, 'Add LICENSE.md', 'master', false)
repository.commit_file(user, 'LICENSE.txt', Licensee::License.new('mit').content, 'Add LICENSE.txt', 'master', false)
expect(repository.license_blob.name).to eq('LICENSE.md')
end
it 'favors LICENCE to LICENSE' do
repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'master', false)
repository.commit_file(user, 'LICENCE', Licensee::License.new('mit').content, 'Add LICENCE', 'master', false)
expect(repository.license_blob.name).to eq('LICENCE')
end
it 'favors LICENSE to COPYING' do
repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'master', false)
repository.commit_file(user, 'COPYING', Licensee::License.new('mit').content, 'Add COPYING', 'master', false)
expect(repository.license_blob.name).to eq('LICENSE')
end
it 'favors LICENCE to COPYING' do
repository.commit_file(user, 'LICENCE', Licensee::License.new('mit').content, 'Add LICENCE', 'master', false)
repository.commit_file(user, 'COPYING', Licensee::License.new('mit').content, 'Add COPYING', 'master', false)
expect(repository.license_blob.name).to eq('LICENCE')
end
end
describe '#license_key' do
before do
repository.send(:cache).expire(:license_key)
repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master')
end
expect(repository.license.name).to eq('license') it 'returns "no-license" when no license is detected' do
expect(repository.license_key).to eq('no-license')
end end
it 'also accepts licence instead of license' do it 'returns the license key' do
expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('licence')]) repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'master', false)
expect(repository.license.name).to eq('licence') expect(repository.license_key).to eq('mit')
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe API::API, api: true do describe API::Licenses, api: true do
include ApiHelpers include ApiHelpers
describe 'GET /licenses/:key' do
before(:each) do describe 'Entity' do
get api("/licenses/#{license_type}?fullname=Anton") before { get api('/licenses/mit') }
it { expect(json_response['key']).to eq('mit') }
it { expect(json_response['name']).to eq('MIT License') }
it { expect(json_response['nickname']).to be_nil }
it { expect(json_response['featured']).to be true }
it { expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') }
it { expect(json_response['source_url']).to eq('http://opensource.org/licenses/MIT') }
it { expect(json_response['description']).to include('A permissive license that is short and to the point.') }
it { expect(json_response['conditions']).to eq(%w[include-copyright]) }
it { expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) }
it { expect(json_response['limitations']).to eq(%w[no-liability]) }
it { expect(json_response['content']).to include('The MIT License (MIT)') }
end
describe 'GET /licenses' do
it 'returns a list of available license templates' do
get api('/licenses')
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(15)
expect(json_response.first['key']).to eq('agpl-3.0')
end end
context 'for mit license name' do describe 'the popular parameter' do
let(:license_type){ 'mit' } context 'with popular=1' do
it 'returns a list of available popular license templates' do
get api('/licenses?popular=1')
it 'returns MIT license text and replases template values' do expect(response.status).to eq(200)
expect(response.body).to include('Copyright (c) 2016 Anton') expect(json_response).to be_an Array
expect(response.body).to include('Copyright (c) 2016') expect(json_response.size).to eq(3)
expect(json_response.first['key']).to eq('apache-2.0')
end
end end
end end
end
context 'for gnu license name' do describe 'GET /licenses/:key' do
let(:license_type){ 'gpl-3.0' } context 'with :project and :fullname given' do
before do
get api("/licenses/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}")
end
context 'for the mit license' do
let(:license_type) { 'mit' }
it 'returns GNU license text and replases template values' do it 'returns the license text' do
expect(response.body).to include('GNU GENERAL PUBLIC LICENSE') expect(json_response['content']).to include('The MIT License (MIT)')
expect(response.body).to include('Copyright (C) 2016') end
it 'replaces placeholder values' do
expect(json_response['content']).to include('Copyright (c) 2016 Anton')
end
end end
end
context 'for apache license name' do context 'for the agpl-3.0 license' do
let(:license_type){ 'apache-2.0' } let(:license_type) { 'agpl-3.0' }
it 'returns Apache license text and replases template values' do it 'returns the license text' do
expect(response.body).to include('Apache License') expect(json_response['content']).to include('GNU AFFERO GENERAL PUBLIC LICENSE')
expect(response.body).to include('Copyright 2016') end
it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project')
expect(json_response['content']).to include('Copyright (C) 2016 Anton')
end
end
context 'for the gpl-3.0 license' do
let(:license_type) { 'gpl-3.0' }
it 'returns the license text' do
expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
end
it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project')
expect(json_response['content']).to include('Copyright (C) 2016 Anton')
end
end
context 'for the gpl-2.0 license' do
let(:license_type) { 'gpl-2.0' }
it 'returns the license text' do
expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
end
it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project')
expect(json_response['content']).to include('Copyright (C) 2016 Anton')
end
end
context 'for the apache-2.0 license' do
let(:license_type) { 'apache-2.0' }
it 'returns the license text' do
expect(json_response['content']).to include('Apache License')
end
it 'replaces placeholder values' do
expect(json_response['content']).to include('Copyright 2016 Anton')
end
end
context 'for an uknown license' do
let(:license_type) { 'muth-over9000' }
it 'returns a 404' do
expect(response.status).to eq(404)
end
end end
end end
context 'for mythic license name' do context 'with no :fullname given' do
let(:license_type){ 'muth-over9000' } context 'with an authenticated user' do
let(:user) { create(:user) }
it 'replaces the copyright owner placeholder with the name of the current user' do
get api('/licenses/mit', user)
it 'returns string with error' do expect(json_response['content']).to include("Copyright (c) 2016 #{user.name}")
expect(response).to have_http_status(404) end
expect(response.body).to eq 'License not found'
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment