//= require jquery.number.min
//= require npm-dependencies
//= require mithril/stream/stream
+//= require awesomplete
//= require_tree .
Es6ObjectAssign.polyfill()
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.SimpleInput = {
+ view: function(vnode) {
+ return m("input.form-control", {
+ style: {
+ width: '100%',
+ },
+ type: 'text',
+ placeholder: 'Add ' + vnode.attrs.placeholder,
+ value: vnode.attrs.value,
+ onchange: function() {
+ if (this.value != '') {
+ vnode.attrs.value(this.value)
+ }
+ },
+ }, vnode.attrs.value)
+ },
+}
+
+window.SelectOrAutocomplete = {
+ view: function(vnode) {
+ return m("input.form-control", {
+ style: {
+ width: '100%'
+ },
+ type: 'text',
+ value: vnode.attrs.value,
+ placeholder: (vnode.attrs.create ? 'Add or select ': 'Select ') + vnode.attrs.placeholder,
+ }, vnode.attrs.value)
+ },
+ oncreate: function(vnode) {
+ vnode.state.awesomplete = new Awesomplete(vnode.dom, {
+ list: vnode.attrs.options,
+ minChars: 0,
+ maxItems: 1000000,
+ autoFirst: true,
+ sort: false,
+ })
+ vnode.state.create = vnode.attrs.create
+ vnode.state.options = vnode.attrs.options
+ // Option is selected from the list.
+ $(vnode.dom).on('awesomplete-selectcomplete', function(event) {
+ vnode.attrs.value(this.value)
+ })
+ $(vnode.dom).on('change', function(event) {
+ if (!vnode.state.create && !(this.value in vnode.state.options)) {
+ this.value = vnode.attrs.value()
+ } else {
+ if (vnode.attrs.value() !== this.value) {
+ vnode.attrs.value(this.value)
+ }
+ }
+ })
+ $(vnode.dom).on('focusin', function(event) {
+ if (this.value === '') {
+ vnode.state.awesomplete.evaluate()
+ vnode.state.awesomplete.open()
+ }
+ })
+ },
+ onupdate: function(vnode) {
+ vnode.state.awesomplete.list = vnode.attrs.options
+ vnode.state.create = vnode.attrs.create
+ vnode.state.options = vnode.attrs.options
+ },
+}
+
+window.TagEditorRow = {
+ view: function(vnode) {
+ var nameOpts = Object.keys(vnode.attrs.vocabulary().tags)
+ var valueOpts = []
+ var inputComponent = SelectOrAutocomplete
+ if (nameOpts.length === 0) {
+ // If there's not vocabulary defined, switch to a simple input field
+ inputComponent = SimpleInput
+ } else {
+ // Name options list
+ if (vnode.attrs.name() != '' && !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)) {
+ nameOpts.push(vnode.attrs.name())
+ }
+ // Value options list
+ if (vnode.attrs.name() in vnode.attrs.vocabulary().tags &&
+ 'values' in vnode.attrs.vocabulary().tags[vnode.attrs.name()]) {
+ valueOpts = vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
+ }
+ }
+ return m("tr", [
+ // Erase tag
+ m("td", [
+ vnode.attrs.editMode &&
+ m('div.text-center', m('a.btn.btn-default.btn-sm', {
+ style: {
+ align: 'center'
+ },
+ onclick: function(e) { vnode.attrs.removeTag() }
+ }, m('i.fa.fa-fw.fa-trash-o')))
+ ]),
+ // Tag key
+ m("td", [
+ vnode.attrs.editMode ?
+ m("div", {key: 'key'}, [
+ m(inputComponent, {
+ options: nameOpts,
+ value: vnode.attrs.name,
+ // Allow any tag name unless "strict" is set to true.
+ create: !vnode.attrs.vocabulary().strict,
+ placeholder: 'key',
+ })
+ ])
+ : vnode.attrs.name
+ ]),
+ // Tag value
+ m("td", [
+ vnode.attrs.editMode ?
+ m("div", {key: 'value'}, [
+ m(inputComponent, {
+ options: valueOpts,
+ value: vnode.attrs.value,
+ placeholder: 'value',
+ // Allow any value on tags not listed on the vocabulary.
+ // Allow any value on tags without values, or the ones
+ // that aren't explicitly declared to be strict.
+ create: !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)
+ || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
+ || vnode.attrs.vocabulary().tags[vnode.attrs.name()].values.length === 0
+ || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].strict,
+ })
+ ])
+ : vnode.attrs.value
+ ])
+ ])
+ }
+}
+
+window.TagEditorTable = {
+ view: function(vnode) {
+ return m("table.table.table-condensed.table-justforlayout", [
+ m("colgroup", [
+ m("col", {width:"5%"}),
+ m("col", {width:"25%"}),
+ m("col", {width:"70%"}),
+ ]),
+ m("thead", [
+ m("tr", [
+ m("th"),
+ m("th", "Key"),
+ m("th", "Value"),
+ ])
+ ]),
+ m("tbody", [
+ vnode.attrs.tags.length > 0
+ ? vnode.attrs.tags.map(function(tag, idx) {
+ return m(TagEditorRow, {
+ key: tag.rowKey,
+ removeTag: function() {
+ vnode.attrs.tags.splice(idx, 1)
+ vnode.attrs.dirty(true)
+ },
+ editMode: vnode.attrs.editMode,
+ name: tag.name,
+ value: tag.value,
+ vocabulary: vnode.attrs.vocabulary
+ })
+ })
+ : m("tr", m("td[colspan=3]", m("center", "Loading tags...")))
+ ]),
+ ])
+ }
+}
+
+var uniqueID = 1
+
+window.TagEditorApp = {
+ appendTag: function(vnode, name, value) {
+ var tag = {name: m.stream(name), value: m.stream(value), rowKey: uniqueID++}
+ vnode.state.tags.push(tag)
+ // Set dirty flag when any of name/value changes to non empty string
+ tag.name.map(function() { vnode.state.dirty(true) })
+ tag.value.map(function() { vnode.state.dirty(true) })
+ tag.name.map(m.redraw)
+ },
+ oninit: function(vnode) {
+ vnode.state.sessionDB = new SessionDB()
+ // Get vocabulary
+ vnode.state.vocabulary = m.stream({"strict":false, "tags":{}})
+ var vocabularyTimestamp = parseInt(Date.now() / 300000) // Bust cache every 5 minutes
+ m.request('/vocabulary.json?v=' + vocabularyTimestamp).then(vnode.state.vocabulary)
+ vnode.state.editMode = vnode.attrs.targetEditable
+ vnode.state.tags = []
+ vnode.state.dirty = m.stream(false)
+ vnode.state.dirty.map(m.redraw)
+ vnode.state.objPath = '/arvados/v1/'+vnode.attrs.targetController+'/'+vnode.attrs.targetUuid
+ // Get tags
+ vnode.state.sessionDB.request(
+ vnode.state.sessionDB.loadLocal(),
+ '/arvados/v1/'+vnode.attrs.targetController,
+ {
+ data: {
+ filters: JSON.stringify([['uuid', '=', vnode.attrs.targetUuid]]),
+ select: JSON.stringify(['properties'])
+ },
+ }).then(function(obj) {
+ if (obj.items.length == 1) {
+ o = obj.items[0]
+ Object.keys(o.properties).forEach(function(k) {
+ vnode.state.appendTag(vnode, k, o.properties[k])
+ })
+ if (vnode.state.editMode) {
+ vnode.state.appendTag(vnode, '', '')
+ }
+ // Data synced with server, so dirty state should be false
+ vnode.state.dirty(false)
+ // Add new tag row when the last one is completed
+ vnode.state.dirty.map(function() {
+ if (!vnode.state.editMode) { return }
+ lastTag = vnode.state.tags.slice(-1).pop()
+ if (lastTag === undefined || (lastTag.name() !== '' || lastTag.value() !== '')) {
+ vnode.state.appendTag(vnode, '', '')
+ }
+ })
+ }
+ }
+ )
+ },
+ view: function(vnode) {
+ return [
+ vnode.state.editMode &&
+ m("div.pull-left", [
+ m("a.btn.btn-primary.btn-sm"+(vnode.state.dirty() ? '' : '.disabled'), {
+ style: {
+ margin: '10px 0px'
+ },
+ onclick: function(e) {
+ var tags = {}
+ vnode.state.tags.forEach(function(t) {
+ // Only ignore tags with empty key
+ if (t.name() != '') {
+ tags[t.name()] = t.value()
+ }
+ })
+ vnode.state.sessionDB.request(
+ vnode.state.sessionDB.loadLocal(),
+ vnode.state.objPath, {
+ method: "PUT",
+ data: {properties: JSON.stringify(tags)}
+ }
+ ).then(function(v) {
+ vnode.state.dirty(false)
+ })
+ }
+ }, vnode.state.dirty() ? ' Save changes ' : ' Saved ')
+ ]),
+ // Tags table
+ m(TagEditorTable, {
+ editMode: vnode.state.editMode,
+ tags: vnode.state.tags,
+ vocabulary: vnode.state.vocabulary,
+ dirty: vnode.state.dirty
+ })
+ ]
+ },
+}
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-jQuery(function($){
- $(document).
- on('click', '.collection-tag-save, .collection-tag-cancel', function(event) {
- $('.edit-collection-tags').removeClass('disabled');
- $('#edit-collection-tags').attr("title", "Edit tags");
- $('.collection-tag-add').addClass('hide');
- $('.collection-tag-remove').addClass('hide');
- $('.collection-tag-save').addClass('hide');
- $('.collection-tag-cancel').addClass('hide');
- $('.collection-tag-field').prop("contenteditable", false);
- }).
- on('click', '.edit-collection-tags', function(event) {
- $('.edit-collection-tags').addClass('disabled');
- $('#edit-collection-tags').attr("title", "");
- $('.collection-tag-add').removeClass('hide');
- $('.collection-tag-remove').removeClass('hide');
- $('.collection-tag-save').removeClass('hide');
- $('.collection-tag-cancel').removeClass('hide');
- $('.collection-tag-field').prop("contenteditable", true);
- $('div').remove('.collection-tags-status-label');
- }).
- on('click', '.collection-tag-save', function(event) {
- var tag_data = {};
- var has_tags = false;
-
- var $tags = $(".collection-tags-table");
- $tags.find('tr').each(function (i, el) {
- var $tds = $(this).find('td');
- var $key = $tds.eq(1).text();
- if ($key && $key.trim().length > 0) {
- has_tags = true;
- tag_data[$key.trim()] = $tds.eq(2).text().trim();
- }
- });
-
- var to_send;
- if (has_tags == false) {
- to_send = {tag_data: "empty"}
- } else {
- to_send = {tag_data: tag_data}
- }
-
- $.ajax($(location).attr('pathname')+'/save_tags', {
- type: 'POST',
- data: to_send
- }).success(function(data, status, jqxhr) {
- $('.collection-tags-status').append('<div class="collection-tags-status-label alert alert-success"><p class="contain-align-left">Saved successfully.</p></div>');
- }).fail(function(jqxhr, status, error) {
- $('.collection-tags-status').append('<div class="collection-tags-status-label alert alert-danger"><p class="contain-align-left">We are sorry. There was an error saving tags. Please try again.</p></div>');
- });
- }).
- on('click', '.collection-tag-cancel', function(event) {
- $.ajax($(location).attr('pathname')+'/tags', {
- type: 'GET'
- });
- }).
- on('click', '.collection-tag-remove', function(event) {
- $(this).parents('tr').detach();
- }).
- on('click', '.collection-tag-add', function(event) {
- var $collection_tags = $(this).closest('.collection-tags-container');
- var $clone = $collection_tags.find('tr.hide').clone(true).removeClass('hide');
- $collection_tags.find('table').append($clone);
- }).
- on('keypress', '.collection-tag-field', function(event){
- return event.which != 13;
- });
-});
$(document).on('ready arv:pane:loaded', function() {
$('[data-mount-mithril]').each(function() {
- m.mount(this, window[$(this).data('mount-mithril')])
+ var data = $(this).data()
+ m.mount(this, {view: function () {return m(window[data.mountMithril], data)}})
})
})
})
return sessions
},
+ loadLocal: function() {
+ var sessions = db.loadActive()
+ var s = false
+ Object.values(sessions).forEach(function(session) {
+ if (session.isFromRails) {
+ s = session
+ return
+ }
+ })
+ return s
+ },
save: function(k, v) {
var sessions = db.loadAll()
sessions[k] = v
// Guess workbench.{apihostport} is a Workbench... unless
// the host part of apihostport is an IPv4 or [IPv6]
// address.
- if (!session.baseURL.match('://(\\[|\\d+\\.\\d+\\.\\d+\\.\\d+[:/])'))
+ if (!session.baseURL.match('://(\\[|\\d+\\.\\d+\\.\\d+\\.\\d+[:/])')) {
var wbUrl = session.baseURL.replace('://', '://workbench.')
// Remove the trailing slash, if it's there.
return wbUrl.slice(-1) == '/' ? wbUrl.slice(0, -1) : wbUrl
+ }
return null
},
// Return a m.stream that will get fulfilled with the
*= require bootstrap
*= require bootstrap3-editable/bootstrap-editable
*= require morris
+ *= require awesomplete
*= require_tree .
*/
{
width: 98%!important;
}
+
+/* Needed for awesomplete to play nice with bootstrap */
+div.awesomplete {
+ display: block;
+}
+/* Makes awesomplete listings to be scrollable */
+.awesomplete > ul {
+ max-height: 410px;
+ overflow-y: auto;
+}
\ No newline at end of file
end
end
- def tags
- render
- end
-
- def save_tags
- tags_param = params['tag_data']
- if tags_param
- if tags_param.is_a?(String) && tags_param == "empty"
- tags = {}
- else
- tags = tags_param
- end
- end
-
- if tags
- if @object.update_attributes properties: tags
- @saved_tags = true
- render
- else
- self.render_error status: 422
- end
- end
- end
-
protected
def find_usable_token(token_list)
+++ /dev/null
-<%# Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: AGPL-3.0 %>
-
-<%
- tags = object.properties
-%>
- <% if tags.andand.is_a?(Hash) %>
- <% tags.each do |k, v| %>
- <tr class="collection-tag-<%=k%>">
- <td>
- <% if object.editable? %>
- <i class="glyphicon glyphicon-remove collection-tag-remove hide" style="cursor: pointer;"></i>
- <% end %>
- </td>
- <td class="collection-tag-field collection-tag-field-key">
- <%= k %>
- </td>
- <td class="collection-tag-field collection-tag-field-value">
- <%= v %>
- </td>
- </tr>
- <% end %>
- <% end %>
-
- <% if @object.editable? %>
- <!-- A hidden row to add new tag -->
- <tr class="collection-tag-hidden hide">
- <td>
- <i class="glyphicon glyphicon-remove collection-tag-remove hide" style="cursor: pointer"></i>
- </td>
- <td class="collection-tag-field collection-tag-field-key"></td>
- <td class="collection-tag-field collection-tag-field-value"></td>
- </tr>
- <% end %>
SPDX-License-Identifier: AGPL-3.0 %>
-<%
- object = @object unless object
-%>
+ <div class="arv-log-refresh-control"
+ data-load-throttle="86486400000" <%# 1001 nights (in milliseconds) %>
+ ></div>
<div class="collection-tags-container" style="padding-left:2em;padding-right:2em;">
- <% if object.editable? %>
- <p title="Edit tags" id="edit-collection-tags">
- <a class="btn btn-primary edit-collection-tags">Edit</a>
- </p>
- <% end %>
-
- <table class="table table-condensed table-fixedlayout collection-tags-table" border="1">
- <colgroup>
- <col width="5%" />
- <col width="25%" />
- <col width="70%" />
- </colgroup>
-
- <thead>
- <tr>
- <th></th>
- <th>Key</th>
- <th>Value</th>
- </tr>
- </thead>
-
- <tbody class="collection-tag-rows">
- <%= render partial: 'show_tag_rows', locals: {object: object} %>
- </tbody>
- </table>
- <div>
- <% if object.editable? %>
- <div class="pull-left">
- <a class="btn btn-primary btn-sm collection-tag-add hide"><i class="glyphicon glyphicon-plus"></i> Add new tag </a>
- </div>
- <div class="pull-right">
- <%= link_to(save_tags_collection_path, {class: 'btn btn-sm btn-primary collection-tag-save hide', :remote => true, method: 'post', return_to: request.url}) do %>
- Save
- <% end %>
- <%= link_to(tags_collection_path, {class: 'btn btn-sm btn-primary collection-tag-cancel hide', :remote => true, method: 'get', return_to: request.url}) do %>
- Cancel
- <% end %>
- </div>
-
- <div><div class="collection-tags-status"/></div></div>
- <% end %>
- </div>
+ <div data-mount-mithril="TagEditorApp" data-target-controller="<%= controller_name %>" data-target-uuid="<%= @object.uuid %>" data-target-editable="<%= @object.editable? %>"></div>
</div>
+
\ No newline at end of file
+++ /dev/null
-<%# Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: AGPL-3.0 %>
-
-<% if @saved_tags %>
-$(".collection-tag-rows").html("<%= escape_javascript(render partial: 'show_tag_rows', locals: {object: @object}) %>");
-<% end %>
+++ /dev/null
-<%# Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: AGPL-3.0 %>
-
-$(".collection-tag-rows").html("<%= escape_javascript(render partial: 'show_tag_rows', locals: {object: @object}) %>");
# Browserify is required.
npm 'browserify', require: false
+npm 'jquery'
+npm 'awesomplete'
npm 'mithril'
npm 'es6-object-assign'
--- /dev/null
+{
+ "strict": false,
+ "tags": {
+ "fruit": {
+ "values": ["pineapple", "tomato", "orange", "banana", "advocado", "lemon", "apple", "peach", "strawberry"],
+ "strict": true
+ },
+ "animal": {
+ "values": ["human", "dog", "elephant", "eagle"],
+ "strict": false
+ },
+ "color": {
+ "values": ["yellow", "red", "magenta", "green"],
+ "strict": false
+ },
+ "text": {},
+ "category": {
+ "values": ["experimental", "development", "production"]
+ },
+ "comments": {},
+ "importance": {
+ "values": ["critical", "important", "low priority"]
+ },
+ "size": {
+ "values": ["x-small", "small", "medium", "large", "x-large"]
+ },
+ "country": {
+ "values": ["Afghanistan","Ă…land Islands","Albania","Algeria","American Samoa","AndorrA","Angola","Anguilla","Antarctica","Antigua and Barbuda","Argentina","Armenia","Aruba","Australia","Austria","Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados","Belarus","Belgium","Belize","Benin","Bermuda","Bhutan","Bolivia","Bosnia and Herzegovina","Botswana","Bouvet Island","Brazil","British Indian Ocean Territory","Brunei Darussalam","Bulgaria","Burkina Faso","Burundi","Cambodia","Cameroon","Canada","Cape Verde","Cayman Islands","Central African Republic","Chad","Chile","China","Christmas Island","Cocos (Keeling) Islands","Colombia","Comoros","Congo","Congo, The Democratic Republic of the","Cook Islands","Costa Rica","Cote D'Ivoire","Croatia","Cuba","Cyprus","Czech Republic","Denmark","Djibouti","Dominica","Dominican Republic","Ecuador","Egypt","El Salvador","Equatorial Guinea","Eritrea","Estonia","Ethiopia","Falkland Islands (Malvinas)","Faroe Islands","Fiji","Finland","France","French Guiana","French Polynesia","French Southern Territories","Gabon","Gambia","Georgia","Germany","Ghana","Gibraltar","Greece","Greenland","Grenada","Guadeloupe","Guam","Guatemala","Guernsey","Guinea","Guinea-Bissau","Guyana","Haiti","Heard Island and Mcdonald Islands","Holy See (Vatican City State)","Honduras","Hong Kong","Hungary","Iceland","India","Indonesia","Iran, Islamic Republic Of","Iraq","Ireland","Isle of Man","Israel","Italy","Jamaica","Japan","Jersey","Jordan","Kazakhstan","Kenya","Kiribati","Korea, Democratic People'S Republic of","Korea, Republic of","Kuwait","Kyrgyzstan","Lao People'S Democratic Republic","Latvia","Lebanon","Lesotho","Liberia","Libyan Arab Jamahiriya","Liechtenstein","Lithuania","Luxembourg","Macao","Macedonia, The Former Yugoslav Republic of","Madagascar","Malawi","Malaysia","Maldives","Mali","Malta","Marshall Islands","Martinique","Mauritania","Mauritius","Mayotte","Mexico","Micronesia, Federated States of","Moldova, Republic of","Monaco","Mongolia","Montserrat","Morocco","Mozambique","Myanmar","Namibia","Nauru","Nepal","Netherlands","Netherlands Antilles","New Caledonia","New Zealand","Nicaragua","Niger","Nigeria","Niue","Norfolk Island","Northern Mariana Islands","Norway","Oman","Pakistan","Palau","Palestinian Territory, Occupied","Panama","Papua New Guinea","Paraguay","Peru","Philippines","Pitcairn","Poland","Portugal","Puerto Rico","Qatar","Reunion","Romania","Russian Federation","RWANDA","Saint Helena","Saint Kitts and Nevis","Saint Lucia","Saint Pierre and Miquelon","Saint Vincent and the Grenadines","Samoa","San Marino","Sao Tome and Principe","Saudi Arabia","Senegal","Serbia and Montenegro","Seychelles","Sierra Leone","Singapore","Slovakia","Slovenia","Solomon Islands","Somalia","South Africa","South Georgia and the South Sandwich Islands","Spain","Sri Lanka","Sudan","Suriname","Svalbard and Jan Mayen","Swaziland","Sweden","Switzerland","Syrian Arab Republic","Taiwan, Province of China","Tajikistan","Tanzania, United Republic of","Thailand","Timor-Leste","Togo","Tokelau","Tonga","Trinidad and Tobago","Tunisia","Turkey","Turkmenistan","Turks and Caicos Islands","Tuvalu","Uganda","Ukraine","United Arab Emirates","United Kingdom","United States","United States Minor Outlying Islands","Uruguay","Uzbekistan","Vanuatu","Venezuela","Viet Nam","Virgin Islands, British","Virgin Islands, U.S.","Wallis and Futuna","Western Sahara","Yemen","Zambia","Zimbabwe"],
+ "strict": true
+ }
+ }
+}
\ No newline at end of file
assert_response 422
assert_includes json_response['errors'], 'Duplicate file path'
end
-
- [
- [:active, true],
- [:spectator, false],
- ].each do |user, editable|
- test "tags tab #{editable ? 'shows' : 'does not show'} edit button to #{user}" do
- use_token user
-
- get :tags, {
- id: api_fixture('collections')['collection_with_tags_owned_by_active']['uuid'],
- format: :js,
- }, session_for(user)
-
- assert_response :success
-
- found = 0
- response.body.scan /<i[^>]+>/ do |remove_icon|
- remove_icon.scan(/\ collection-tag-remove(.*?)\"/).each do |i,|
- found += 1
- end
- end
-
- if editable
- assert_equal(3, found) # two from the tags + 1 from the hidden "add tag" row
- else
- assert_equal(0, found)
- end
- end
- end
-
- test "save_tags and verify that 'other' properties are retained" do
- use_token :active
-
- collection = api_fixture('collections')['collection_with_tags_owned_by_active']
-
- new_tags = {"new_tag1" => "new_tag1_value",
- "new_tag2" => "new_tag2_value"}
-
- post :save_tags, {
- id: collection['uuid'],
- tag_data: new_tags,
- format: :js,
- }, session_for(:active)
-
- assert_response :success
- assert_equal true, response.body.include?("new_tag1")
- assert_equal true, response.body.include?("new_tag1_value")
- assert_equal true, response.body.include?("new_tag2")
- assert_equal true, response.body.include?("new_tag2_value")
- assert_equal false, response.body.include?("existing tag 1")
- assert_equal false, response.body.include?("value for existing tag 1")
-
- updated_tags = Collection.find(collection['uuid']).properties
- assert_equal true, updated_tags.keys.include?(:'new_tag1')
- assert_equal new_tags['new_tag1'], updated_tags[:'new_tag1']
- assert_equal true, updated_tags.keys.include?(:'new_tag2')
- assert_equal new_tags['new_tag2'], updated_tags[:'new_tag2']
- assert_equal false, updated_tags.keys.include?(:'existing tag 1')
- assert_equal false, updated_tags.keys.include?(:'existing tag 2')
- end
end
first('.lock-collection-btn').click
accept_alert
end
-
- test "collection tags tab" do
- visit page_with_token('active', '/collections/zzzzz-4zz18-bv31uwvy3neko21')
-
- click_link 'Tags'
- wait_for_ajax
-
- # verify initial state
- assert_selector 'a', text: 'Edit'
- assert_no_selector 'a', text: 'Add new tag'
- assert_no_selector 'a', text: 'Save'
- assert_no_selector 'a', text: 'Cancel'
-
- # Verify controls in edit mode
- first('.edit-collection-tags').click
- assert_selector 'a.disabled', text: 'Edit'
- assert_selector 'a', text: 'Add new tag'
- assert_selector 'a', text: 'Save'
- assert_selector 'a', text: 'Cancel'
-
- # add two tags
- first('.glyphicon-plus').click
- first('.collection-tag-field-key').click
- first('.collection-tag-field-key').set('key 1')
- first('.collection-tag-field-value').click
- first('.collection-tag-field-value').set('value 1')
-
- first('.glyphicon-plus').click
- editable_key_fields = page.all('.collection-tag-field-key')
- editable_key_fields[1].click
- editable_key_fields[1].set('key 2')
- editable_val_fields = page.all('.collection-tag-field-value')
- editable_val_fields[1].click
- editable_val_fields[1].set('value 2')
-
- click_on 'Save'
- wait_for_ajax
-
- # added tags; verify
- assert_text 'key 1'
- assert_text 'value 1'
- assert_text 'key 2'
- assert_text 'value 2'
- assert_selector 'a', text: 'Edit'
- assert_no_selector 'a', text: 'Save'
-
- # remove first tag
- first('.edit-collection-tags').click
- assert_not_nil first('.glyphicon-remove')
- first('.glyphicon-remove').click
- click_on 'Save'
- wait_for_ajax
-
- assert_text 'key 2'
- assert_text 'value 2'
- assert_no_text 'key 1'
- assert_no_text 'value 1'
- assert_selector 'a', text: 'Edit'
-
- # Click on cancel and verify
- first('.edit-collection-tags').click
- first('.collection-tag-field-key').click
- first('.collection-tag-field-key').set('this key wont stick')
- first('.collection-tag-field-value').click
- first('.collection-tag-field-value').set('this value wont stick')
-
- click_on 'Cancel'
- wait_for_ajax
-
- assert_text 'key 2'
- assert_text 'value 2'
- assert_no_text 'this key wont stick'
- assert_no_text 'this value wont stick'
-
- # remove all tags
- first('.edit-collection-tags').click
- first('.glyphicon-remove').click
- click_on 'Save'
- wait_for_ajax
-
- assert_selector 'a', text: 'Edit'
- assert_no_text 'key 2'
- assert_no_text 'value 2'
- end
end