implement work package child creation

pull/3997/head
Jens Ulferts 9 years ago
parent 9668e7e99e
commit 4e2f3a1b7a
  1. 1
      config/locales/js-en.yml
  2. 20
      frontend/app/components/work-packages/controllers/wp-new.controller.js
  3. 2
      frontend/app/components/work-packages/directives/wp-create-form/wp-full-create-form.directive.html
  4. 11
      frontend/app/components/work-packages/services/work-package.service.js
  5. 2
      frontend/app/routing.js
  6. 4
      spec/factories/issue_priority_factory.rb
  7. 129
      spec/features/work_packages/create_child_spec.rb
  8. 25
      spec/support/pages/full_work_package.rb
  9. 24
      spec/support/pages/full_work_package_create.rb

@ -365,6 +365,7 @@ en:
message_successful_bulk_delete: Successfully deleted work packages.
create:
header: 'New Work Package'
header_with_parent: 'New work package (Child of %{type} #%{id})'
no_results:
title: No work packages to display
description_html: |

@ -63,6 +63,17 @@ function WorkPackageNewController($scope,
vm.notifyCreation = function() {
NotificationsService.addSuccess(I18n.t('js.notice_successful_create'));
};
vm.getHeading = function() {
if (vm.parentWorkPackage !== undefined) {
return I18n.t('js.work_packages.create.header_with_parent',
{ type: vm.parentWorkPackage.embedded.type.props.name,
id: vm.parentWorkPackage.props.id });
}
else {
return I18n.t('js.work_packages.create.header');
}
};
vm.goBack = function() {
var args = ['^'],
prevState = $rootScope.previousState;
@ -112,7 +123,14 @@ function WorkPackageNewController($scope,
EditableFieldsState.forcedEditState = true;
EditableFieldsState.editAll.state = true;
if ($stateParams.copiedFromWorkPackageId) {
if ($stateParams.parent_id) {
vm.loaderPromise = WorkPackageService.getWorkPackage($stateParams.parent_id)
.then(function(workPackage) {
vm.parentWorkPackage = workPackage;
return WorkPackageService.initializeWorkPackageWithParent(workPackage);
});
}
else if ($stateParams.copiedFromWorkPackageId) {
vm.loaderPromise = WorkPackageService.getWorkPackage($stateParams.copiedFromWorkPackageId)
.then(function(workPackage) {
return WorkPackageService.initializeWorkPackageFromCopy(workPackage);

@ -30,7 +30,7 @@
<div cg-busy="vm.loaderPromise">
<div ng-if="vm.workPackage">
<h2>{{ ::I18n.t('js.work_packages.create.header') }}</h2>
<h2>{{ vm.getHeading() }}</h2>
<div class="work-packages--create--title">
<work-package-field field-name="'subject'" tabindex="0"></work-package-field>

@ -122,6 +122,17 @@ function WorkPackageService($http, PathHelper, WorkPackagesHelper, HALAPIResourc
return WorkPackageService.initializeWorkPackage(projectIdentifier, initialData);
},
initializeWorkPackageWithParent: function(parentWorkPackage) {
var projectIdentifier = parentWorkPackage.embedded.project.props.identifier;
var initialData = {
parentId: String(parentWorkPackage.props.id)
};
return WorkPackageService.initializeWorkPackage(projectIdentifier, initialData);
},
getWorkPackage: function(id) {
var path = PathHelper.apiV3WorkPackagePath(id),
resource = HALAPIResource.setup(path);

@ -83,7 +83,7 @@ angular.module('openproject')
})
.state('work-packages.new', {
url: '/{projects}/{projectPath}/work_packages/new?type',
url: '/{projects}/{projectPath}/work_packages/new?type&parent_id',
templateUrl: '/components/routes/partials/work-packages.new.html',
controllerAs: 'vm',
reloadOnSearch: false

@ -30,4 +30,8 @@ FactoryGirl.define do
factory :issue_priority do
sequence(:name) { |n| "IssuePriority #{n}" }
end
factory :default_priority, parent: :issue_priority do
is_default true
end
end

@ -0,0 +1,129 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
RSpec.feature 'Work package create children', js: true, selenium: true do
let(:user) do
FactoryGirl.create(:user,
member_in_project: project,
member_through_role: create_role)
end
let(:work_flow) do
FactoryGirl.create(:workflow,
role: create_role,
type_id: original_work_package.type_id,
old_status: original_work_package.status,
new_status: FactoryGirl.create(:status))
end
let(:create_role) do
FactoryGirl.create(:role,
permissions: [:view_work_packages,
:add_work_packages,
:edit_work_packages,
:manage_subtasks])
end
let(:project) { FactoryGirl.create(:project) }
let(:original_work_package) do
FactoryGirl.build(:work_package,
project: project,
assigned_to: assignee,
responsible: responsible,
fixed_version: version,
priority: default_priority,
author: author,
status: default_status)
end
let(:default_priority) do
FactoryGirl.build(:default_priority)
end
let(:default_status) do
FactoryGirl.build(:default_status)
end
let(:role) { FactoryGirl.build(:role, permissions: [:view_work_packages]) }
let(:assignee) do
FactoryGirl.build(:user,
firstname: 'An',
lastname: 'assignee',
member_in_project: project,
member_through_role: role)
end
let(:responsible) do
FactoryGirl.build(:user,
firstname: 'The',
lastname: 'responsible',
member_in_project: project,
member_through_role: role)
end
let(:author) do
FactoryGirl.build(:user,
firstname: 'The',
lastname: 'author',
member_in_project: project,
member_through_role: role)
end
let(:version) do
FactoryGirl.build(:version,
project: project)
end
before do
login_as(user)
allow(user.pref).to receive(:warn_on_leaving_unsaved?).and_return(false)
original_work_package.save!
work_flow.save!
end
scenario 'on fullscreen page' do
original_work_package_page = Pages::FullWorkPackage.new(original_work_package)
child_work_package_page = original_work_package_page.add_child
child_work_package_page.expect_current_path
child_work_package_page.expect_heading
child_work_package_page.update_attributes Subject: 'Child work package'
child_work_package_page.save!
expect(page).to have_selector('.notification-box--content',
text: I18n.t('js.notice_successful_create'))
child_work_package = WorkPackage.order(created_at: 'desc').first
expect(child_work_package).to_not eql original_work_package
child_work_package_page = Pages::FullWorkPackage.new(child_work_package)
child_work_package_page.expect_subject
child_work_package_page.expect_current_path
child_work_package_page.expect_parent(original_work_package)
end
end

@ -72,8 +72,29 @@ module Pages
expect(page).to have_selector(container + ' .user', text: user.name)
end
def expect_parent(parent = nil)
parent ||= work_package.parent
expect(parent).to_not be_nil
visit_tab!('relations')
expect(page).to have_selector(".relation[title=#{I18n.t('js.relation_labels.parent')}] a",
text: "##{parent.id} #{parent.subject}")
end
def add_child
visit_tab!('relations')
page.find('.relation a', text: I18n.t('js.relation_labels.children')).click
click_button I18n.t('js.relation_buttons.add_child')
Pages::FullWorkPackageCreate.new(parent_work_package: work_package)
end
def visit_copy!
page = FullWorkPackageCreate.new(work_package)
page = FullWorkPackageCreate.new(original_work_package: work_package)
page.visit!
page
@ -85,7 +106,7 @@ module Pages
find('.work-packages--show-view')
end
def path(tab='activity')
def path(tab = 'activity')
work_package_path(work_package.id, tab)
end
end

@ -30,17 +30,29 @@ require 'support/pages/page'
module Pages
class FullWorkPackageCreate < Page
attr_reader :work_package
attr_reader :original_work_package,
:parent_work_package
def initialize(work_package = nil)
def initialize(original_work_package: nil, parent_work_package: nil)
# in case of copy, the original work package can be provided
@work_package = work_package
@original_work_package = original_work_package
@parent_work_package = parent_work_package
end
def expect_fully_loaded
expect(page).to have_field(I18n.t('js.work_packages.properties.subject'))
end
def expect_heading
if parent_work_package
expect(page).to have_selector('h2', text: I18n.t('js.work_packages.create.header_with_parent',
type: parent_work_package.type,
id: parent_work_package.id))
else
expect(page).to have_selector('h2', text: I18n.t('js.work_packages.create.header'))
end
end
def update_attributes(attribute_map)
# Only designed for text fields for now
attribute_map.each do |label, value|
@ -59,7 +71,11 @@ module Pages
end
def path
work_package_path(work_package) + '/copy' if work_package
if original_work_package
work_package_path(work_package) + '/copy'
elsif parent_work_package
new_project_work_packages_path(parent_work_package.project.identifier)
end
end
end
end

Loading…
Cancel
Save