Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/components/skills-typeahead-result.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default Component.extend({

hasSkill: computed('skill', function() {
let { skill, skillsList } = getProperties(this, 'skill', 'skillsList');
return skillsList.contains(skill);
return skillsList.includes(skill);
}),
selected: alias('skill.selected'),

Expand Down
84 changes: 61 additions & 23 deletions app/controllers/project/tasks/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,64 @@ import { isNonValidationError } from 'code-corps-ember/utils/error-utils';
const {
Controller,
get,
RSVP,
set
} = Ember;

export default Controller.extend({
unsavedTaskSkills: [],
actions: {
/**
* saveTask - action
*
* Triggered when user clicks 'save' on the new task form.
* Adds task to the correct task list, which is the inbox task list.
* Saves task and, on success, transitions to the task route.
* On failure, calls the save error handler.
*
* @param {DS.Model} task The task to be saved.
*/
saveTask - action

Triggered when user clicks 'save' on the new task form.
Adds task to the correct task list, which is the inbox task list.
Saves task and, on success, transitions to the task route.
On failure, calls the save error handler.

@param {DS.Model} task The task to be saved.
*/
async saveTask(task) {
let project = get(task, 'project');
let inboxTaskList = await this._getInboxTaskList(project);

set(task, 'taskList', inboxTaskList);

return task.save()
.then((task) => this._saveSkills(task))
.then((task) => this.transitionToRoute('project.tasks.task', get(task, 'number')))
.catch((payload) => this._handleTaskSaveError(payload));
},

deselectSkill(skill) {
let unsavedTaskSkills = get(this, 'unsavedTaskSkills');
unsavedTaskSkills.removeObject(skill);
},

toggleSkill(skill) {
let unsavedTaskSkills = get(this, 'unsavedTaskSkills');
if (unsavedTaskSkills.includes(skill)) {
unsavedTaskSkills.removeObject(skill);
} else {
unsavedTaskSkills.pushObject(skill);
}
}
},

_createTaskSkill(skill, task) {
let store = get(this, 'store');
return store.createRecord('task-skill', { skill, task }).save();
},

/**
* _getInboxTaskList - Private method
*
* Returns a promise, which, when resolved, holds the task list for the specified
* project, which is marked as an inbox task list.
* @param {DS.Model} project The currently loaded project.
* @return {DS.Model} The inbox task list for the specified project.
*/
_getInboxTaskList - Private function

Returns a promise, which, when resolved, holds the task list for the
specified project, which is marked as an inbox task list.

@param {DS.Model} project The currently loaded project.
@return {DS.Model} The inbox task list for the specified project.
*/
async _getInboxTaskList(project) {
let taskLists = await get(project, 'taskLists');
let inboxes = taskLists.filterBy('inbox', true);
Expand All @@ -47,16 +70,31 @@ export default Controller.extend({
},

/**
* _handleTaskSaveError - Private function
*
* Sets the controller `error` property if the error payloed is anything other
* than a validation error
*
* @param {DS.AdapterError} payload The payload to check.
*/
_handleTaskSaveError - Private function

Sets the controller `error` property if the error payloed is anything
other than a validation error

@param {DS.AdapterError} payload The payload to check.
*/
_handleTaskSaveError(payload) {
if (isNonValidationError(payload)) {
set(this, 'error', payload);
}
},

/**
_saveSkills - Private function

Saves any of the skills added during the creation of the task.

@param {DS.Model} task The task being created.
@return {RSVP.Promise} promise that is fulfilled when all `promises`
have been fulfilled, or rejected if any of them become rejected.
*/
async _saveSkills(task) {
let unsavedTaskSkills = get(this, 'unsavedTaskSkills');
let promises = unsavedTaskSkills.map((skill) => this._createTaskSkill(skill, task));
return RSVP.all(promises).then(() => task);
}
});
1 change: 1 addition & 0 deletions app/routes/project/tasks/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
let task = store.createRecord('task', { project, taskType, user });

set(controller, 'task', task);
set(controller, 'unsavedTaskSkills', []);
},

actions: {
Expand Down
6 changes: 3 additions & 3 deletions app/services/project-skills-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ export default Service.extend({
return store.createRecord('project-skill', { project, skill }).save();
},

contains(skill) {
includes(skill) {
let projectSkills = get(this, 'projectSkills');
return recordsList.contains(projectSkills, skill);
return recordsList.includes(projectSkills, skill);
},

find(skill) {
Expand All @@ -44,7 +44,7 @@ export default Service.extend({

toggle(skill) {
let projectSkills = get(this, 'projectSkills');
if (recordsList.contains(projectSkills, skill)) {
if (recordsList.includes(projectSkills, skill)) {
return this.remove(skill);
} else {
return this.add(skill);
Expand Down
6 changes: 3 additions & 3 deletions app/services/task-skills-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ export default Service.extend({
return store.createRecord('task-skill', { task, skill }).save();
},

contains(skill) {
includes(skill) {
let taskSkills = get(this, 'taskSkills');
return recordsList.contains(taskSkills, skill);
return recordsList.includes(taskSkills, skill);
},

find(skill) {
Expand All @@ -44,7 +44,7 @@ export default Service.extend({

toggle(skill) {
let taskSkills = get(this, 'taskSkills');
if (recordsList.contains(taskSkills, skill)) {
if (recordsList.includes(taskSkills, skill)) {
return this.remove(skill);
} else {
return this.add(skill);
Expand Down
6 changes: 3 additions & 3 deletions app/services/user-skills-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export default Service.extend({
return store.createRecord('user-skill', { user, skill }).save();
},

contains(skill) {
includes(skill) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like renaming to includes here.

let userSkills = get(this, 'userSkills');
return recordsList.contains(userSkills, skill);
return recordsList.includes(userSkills, skill);
},

find(skill) {
Expand All @@ -41,7 +41,7 @@ export default Service.extend({

toggle(skill) {
let userSkills = get(this, 'userSkills');
if (recordsList.contains(userSkills, skill)) {
if (recordsList.includes(userSkills, skill)) {
return this.remove(skill);
} else {
return this.add(skill);
Expand Down
26 changes: 26 additions & 0 deletions app/templates/project/tasks/new.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,31 @@
{{/if}}
</div>
<div class="task-new-sidebar">
<div class="task-sidebar-section">
<div class="task-sidebar-section__header">
<h2>Skills</h2>
</div>

<div class="task-skills-list centered">
{{#each unsavedTaskSkills as |taskSkill|}}
{{skill-button
alwaysShowX=true
iconBefore=false
isLoading=taskSkill.isLoading
remove=(action 'deselectSkill' taskSkill)
size="small"
skill=taskSkill
}}
{{/each}}
</div>

<div class="skills-typeahead-container">
{{skills-typeahead
centered=true
selectSkill=(action 'toggleSkill')
skillsList=unsavedTaskSkills
}}
</div>
</div>
</div>
</div>
2 changes: 1 addition & 1 deletion app/utils/records-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default {
@return {Boolean} `true` if found, otherwise `false`
@public
*/
contains(records, target) {
includes(records, target) {
if (records) {
return records.any((found) => {
let targetId = get(target, 'id');
Expand Down
56 changes: 56 additions & 0 deletions tests/acceptance/task-creation-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,59 @@ test('Navigation is aborted if user answers negatively to prompt', function(asse
stub.restore();
});
});

test('Skills can be assigned to task during creation', function(assert) {
assert.expect(6);

let user = server.schema.users.create({ username: 'test_user' });

let project = createProjectWithSluggedRoute();
let { organization } = project;
project.createTaskList({ inbox: true });

authenticateSession(this.application, { user_id: user.id });

projectTasksIndexPage.visit({ organization: organization.slug, project: project.slug });

andThen(() => {
projectTasksIndexPage.clickNewTask();
});

let skill = server.create('skill', { title: 'Ruby' });

andThen(() => {
assert.equal(currentRouteName(), 'project.tasks.new', 'Button takes us to the proper route');
assert.equal(find('[name=task-type]').val(), 'issue', 'Has the right default task type');

projectTasksNewPage.taskTitle('A task title')
.taskMarkdown('A task body')
.taskType('idea');
});

// NOTE: We need to be doing this async, so the code is ugly
// Possibly switching to await/async might make it nicer
andThen(() => {
// find skill
projectTasksNewPage.skillsTypeahead.fillIn('ru');
});

andThen(() => {
// add skill
projectTasksNewPage.skillsTypeahead.inputItems(0).click();
});

andThen(() => {
projectTasksNewPage.clickSubmit();
});

andThen(() => {
assert.equal(server.schema.tasks.all().models.length, 1, 'A task has been created');
assert.equal(server.schema.taskSkills.all().models.length, 1, 'A single task skill has been created.');

let [task] = server.schema.tasks.all().models;
let [taskSkill] = server.schema.taskSkills.all().models;

assert.equal(taskSkill.taskId, task.id, 'The correct task was assigned');
assert.equal(taskSkill.skillId, skill.id, 'The correct skill was assigned');
});
});
2 changes: 1 addition & 1 deletion tests/integration/components/skill-list-item-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ moduleForComponent('skill-list-item', 'Integration | Component | skill list item
},
beforeEach() {
stubService(this, 'user-skills-list', {
contains() {
includes() {
return true;
},
find(skill) {
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/components/skill-list-items-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ moduleForComponent('skill-list-items', 'Integration | Component | skill list ite
integration: true,
beforeEach() {
stubService(this, 'user-skills-list', {
contains(queriedSkill) {
includes(queriedSkill) {
return queriedSkill === skills[1];
},
find(queriedSkill) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let skill = Object.create({
});

let userSkillsList = {
contains() {
includes() {
return true;
},
find(queriedSkill) {
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/components/skills-typeahead-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let mockStore = {
};

let mockListService = {
contains(queriedSkill) {
includes(queriedSkill) {
return queriedSkill === skills[1];
},
find(queriedSkill) {
Expand Down
13 changes: 13 additions & 0 deletions tests/pages/project/tasks/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import {
collection,
create,
fillable,
text,
visitable
} from 'ember-cli-page-object';

import projectMenu from 'code-corps-ember/tests/pages/components/project-menu';
import skillsTypeahead from 'code-corps-ember/tests/pages/components/skills-typeahead';

export default create({
clickPreviewTask: clickable('.preview'),
Expand All @@ -26,5 +28,16 @@ export default create({
scope: '.body-preview'
},

skillsTypeahead,

taskSkillsList: collection({
scope: '.task-skills-list',
itemScope: 'button',
item: {
text: text(),
click: clickable()
}
}),

visit: visitable(':organization/:project/tasks/new')
});
10 changes: 5 additions & 5 deletions tests/unit/utils/records-list-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { module, test } from 'qunit';

const { isEmpty, Object } = Ember;

module('Unit | Utility | skills-list');
module('Unit | Utility | records-list');

let projectSkill = Object.create({
belongsTo(relationshipName) {
Expand Down Expand Up @@ -53,13 +53,13 @@ test('find returns no match correctly', function(assert) {
assert.ok(isEmpty(result));
});

test('contains returns true when there is a match', function(assert) {
test('includes returns true when there is a match', function(assert) {
let projectSkills = [projectSkill];
let result = recordsList.contains(projectSkills, skill);
let result = recordsList.includes(projectSkills, skill);
assert.ok(result);
});

test('contains returns false when there is no match', function(assert) {
let result = recordsList.contains([], skill);
test('includes returns false when there is no match', function(assert) {
let result = recordsList.includes([], skill);
assert.notOk(result);
});