Merge remote-tracking branch 'origin/dev' into angular/migrate-hal-resources

pull/6263/head
Oliver Günther 7 years ago
commit fddd089ab6
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 3
      frontend/app/angular4-modules.ts
  2. 29
      frontend/app/components/common/autocomplete/lazyloaded/lazyloaded-autocompleter.ts
  3. 2
      frontend/app/components/datetime/timezone.service.ng2.test.ts
  4. 1
      frontend/app/components/projects/project-menu-autocomplete/project-menu-autocomplete.component.ts
  5. 2
      frontend/app/modules/hal/http/openproject-header-interceptor.ts
  6. 33
      spec/features/projects/project_autocomplete_spec.rb
  7. 200
      spec/features/work_packages/tabs/activity_revisions_spec.rb

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {LOCALE_ID, NgModule} from '@angular/core';
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {UpgradeModule} from '@angular/upgrade/static';
import {FormsModule} from '@angular/forms';
@ -224,7 +224,6 @@ import {TimezoneService} from 'core-components/datetime/timezone.service';
OpenprojectHalModule
],
providers: [
{ provide: LOCALE_ID, useValue: I18n.locale },
GonRef,
HideSectionService,
upgradeServiceWithToken('$rootScope', $rootScopeToken),

@ -56,11 +56,38 @@ export abstract class ILazyAutocompleterBridge<T> {
private fuzzySearch(items:IAutocompleteItem<T>[], term:string) {
if (term === '') {
return items;
} else if (term.length >= 3) {
const literalMatches = this.literalSearch(items, term);
if (literalMatches.length > 0) {
return literalMatches as any;
}
}
return this.fuseInstance.search(term);
}
/**
* Filters the given list of items so that only items whose label contains
* the exact search term (case insensitive).
*
* @param items Items to be searched
* @param term Search term
* @return The subset of the given items matching the search term.
*/
private literalSearch(items:IAutocompleteItem<T>[], term:string) {
const results:IAutocompleteItem<T>[] = [];
const str:string = term.toLowerCase();
items.forEach(e => {
if (e.label.toLowerCase().indexOf(str) !== -1) {
results.push(e);
}
});
return results;
}
/**
* Allows to augment the set of matched items (e.g., to add hierarchy).
* @param {IAutocompleteItem<T>[]} items
@ -91,7 +118,7 @@ export abstract class ILazyAutocompleterBridge<T> {
tokenize: false,
threshold: 0.2,
location: 0,
distance: 100,
distance: 10000, // allow the term to appear anywhere
maxPatternLength: 16,
minMatchCharLength: 2,
keys: ['label']

@ -94,9 +94,7 @@ describe('TimezoneService', function () {
});
it('has local time zone', function () {
var time = timezoneService.parseDate(DATE);
expect(timezoneService.ConfigurationService.timezone()).to.equal('America/Vancouver');
expect(time.utcOffset()).to.equal(60);
});
});
});

@ -196,7 +196,6 @@ export class ProjectMenuAutocompleteController extends ILazyAutocompleterBridge<
*/
protected augmentedResultSet(items:ProjectAutocompleteItem[], matched:ProjectAutocompleteItem[]) {
const matches = matched.map(el => el.object.identifier);
console.log(matches);
const matchedParents = _.flatten(matched.map(el => el.object.parents));
const results:ProjectAutocompleteItem[] = [];

@ -4,7 +4,7 @@ import {
HttpHandler,
HttpRequest,
} from '@angular/common/http';
import {Observable} from 'rxjs/observable';
import {Observable} from 'rxjs';
export class OpenProjectHeaderInterceptor implements HttpInterceptor {
intercept(req:HttpRequest<any>, next:HttpHandler):Observable<HttpEvent<any>> {

@ -51,6 +51,21 @@ describe 'Projects autocomplete page', type: :feature, js: true do
identifier: 'plain-project-2')
end
let!(:other_projects) do
names = [
"Very long project name with term at the END",
"INK14 - Foo",
"INK15 - Bar",
"INK16 - Baz"
]
names.map do |name|
identifier = name.gsub(/[ \-]+/, "-").downcase
FactoryGirl.create :project, name: name, identifier: identifier
end
end
let(:top_menu) { ::Components::Projects::TopMenu.new }
before do
@ -93,7 +108,23 @@ describe 'Projects autocomplete page', type: :feature, js: true do
within(top_menu.search_results) do
expect(page).to have_selector('.ui-state-disabled .ui-menu-item-wrapper', text: '<strong>foobar</strong>')
expect(page).to have_selector('.ui-menu-item-wrapper.ui-state-active', text: '» Plain other project')
expect(page).to have_selector('.ui-menu-item-wrapper', text: 'Plain project')
end
# find terms at the end of project names
top_menu.search 'END'
within(top_menu.search_results) do
expect(page).to have_selector(
'.ui-menu-item-wrapper',
text: 'Very long project name with term at the END'
)
end
# Find literal matches exclusively if present
top_menu.search 'INK15'
within(top_menu.search_results) do
expect(page).to have_selector('.ui-menu-item-wrapper', text: 'INK15 - Bar')
expect(page).to have_no_selector('.ui-menu-item-wrapper', text: 'INK14 - Foo')
expect(page).to have_no_selector('.ui-menu-item-wrapper', text: 'INK16 - Baz')
end
# Visit a project

@ -0,0 +1,200 @@
require 'spec_helper'
require 'features/work_packages/work_packages_page'
require 'support/work_packages/work_package_field'
describe 'Activity tab', js: true, selenium: true do
def alter_work_package_at(work_package, attributes:, at:, user: User.current)
work_package.update_attributes(attributes.merge({ updated_at: at }))
note_journal = work_package.journals.last
note_journal.update_attributes(created_at: at, user: attributes[:user])
end
let(:project) { FactoryGirl.create :project_with_types, is_public: true }
let!(:work_package) {
work_package = FactoryGirl.create(:work_package,
project: project,
created_at: 5.days.ago.to_date.to_s(:db),
subject: initial_subject,
journal_notes: initial_comment)
note_journal = work_package.journals.last
note_journal.update_attributes(created_at: 5.days.ago.to_date.to_s)
work_package
}
let(:initial_subject) { 'My Subject' }
let(:initial_comment) { 'First comment on this wp.' }
let(:comments_in_reverse) { false }
let(:activity_tab) { ::Components::WorkPackages::Activities.new(work_package) }
let(:initial_note) {
work_package.journals[0]
}
let!(:note_1) {
attributes = { subject: 'New subject', description: 'Some not so long description.' }
alter_work_package_at(work_package,
attributes: attributes,
at: 3.days.ago.to_date.to_s(:db),
user: user)
work_package.journals.last
}
let!(:note_2) {
attributes = { journal_notes: 'Another comment by a different user' }
alter_work_package_at(work_package,
attributes: attributes,
at: 1.days.ago.to_date.to_s(:db),
user: FactoryGirl.create(:admin))
work_package.journals.last
}
before do
login_as(user)
allow(user.pref).to receive(:warn_on_leaving_unsaved?).and_return(false)
allow(user.pref).to receive(:comments_sorting).and_return(comments_in_reverse ? 'desc' : 'asc')
allow(user.pref).to receive(:comments_in_reverse_order?).and_return(comments_in_reverse)
end
shared_examples 'shows activities in order' do
let(:journals) {
journals = [initial_note, note_1, note_2]
journals
}
it 'shows activities in ascending order' do
journals.each_with_index do |journal, idx|
actual_index =
if comments_in_reverse
journals.length - idx
else
idx + 1
end
date_selector = ".work-package-details-activities-activity:nth-of-type(#{actual_index}) " +
'.activity-date'
# Do not use :long format to match the printed date without double spaces
# on the first 9 days of the month
expect(page).to have_selector(date_selector,
text: journal.created_at.to_date.strftime("%B %-d, %Y"))
activity = page.find("#activity-#{idx + 1}")
if journal.id != note_1.id
expect(activity).to have_selector('.user', text: journal.user.name)
expect(activity).to have_selector('.user-comment > .message', text: journal.notes, visible: :all)
end
if activity == note_1
expect(activity).to have_selector('.work-package-details-activities-messages .message',
count: 2)
expect(activity).to have_selector('.message',
text: "Subject changed from #{initial_subject} " \
"to #{journal.data.subject}")
end
end
end
end
shared_examples 'activity tab' do
before do
work_package_page.visit_tab! 'activity'
work_package_page.ensure_page_loaded
expect(page).to have_selector('.user-comment > .message',
text: initial_comment)
end
context 'with permission' do
let(:role) {
FactoryGirl.create(:role, permissions: [:view_work_packages,
:add_work_package_notes])
}
let(:user) {
FactoryGirl.create(:user,
member_in_project: project,
member_through_role: role)
}
context 'with ascending comments' do
let(:comments_in_reverse) { false }
it_behaves_like 'shows activities in order'
end
context 'with reversed comments' do
let(:comments_in_reverse) { true }
it_behaves_like 'shows activities in order'
end
it 'can toggle between activities and comments-only' do
expect(page).to have_selector('.work-package-details-activities-activity-contents', count: 3)
expect(page).to have_selector('.user-comment > .message', text: note_2.notes)
# Show only comments
find('.activity-comments--toggler').click
# It should remove the middle
expect(page).to have_selector('.work-package-details-activities-activity-contents', count: 2)
expect(page).to have_selector('.user-comment > .message', text: initial_comment)
expect(page).to have_selector('.user-comment > .message', text: note_2.notes)
# Show all again
find('.activity-comments--toggler').click
expect(page).to have_selector('.work-package-details-activities-activity-contents', count: 3)
end
it 'can quote a previous comment' do
activity_tab.hover_action('1', :quote)
field = WorkPackageEditorField.new work_package_page,
'comment',
selector: '.work-packages--activity--add-comment'
expect(field.editing?).to be true
# Add our comment
quote = field.input_element[:value]
expect(quote).to include("> #{initial_comment}")
quote << "\nthis is some remark under a quote"
field.input_element.set(quote)
field.submit_by_click
expect(page).to have_selector('.user-comment > .message', count: 3)
expect(page).to have_selector('.user-comment > .message blockquote')
end
end
context 'with no permission' do
let(:role) {
FactoryGirl.create(:role, permissions: [:view_work_packages])
}
let(:user) {
FactoryGirl.create(:user,
member_in_project: project,
member_through_role: role)
}
it 'shows the activities, but does not allow commenting' do
expect(page).not_to have_selector('.work-packages--activity--add-comment', visible: true)
end
end
end
context 'split screen' do
let(:work_package_page) { Pages::SplitWorkPackage.new(work_package, project) }
it_behaves_like 'activity tab'
end
context 'full screen' do
let(:work_package_page) { Pages::FullWorkPackage.new(work_package) }
it_behaves_like 'activity tab'
end
end
Loading…
Cancel
Save