From dece32271b90274effb62ee46b0c93d2cd121d4d Mon Sep 17 00:00:00 2001 From: Matthew Ross Date: Thu, 5 Jul 2018 09:05:37 -0400 Subject: [PATCH] Fix unit tests for new context menu behaviors --- src/app/board/board.component.ts | 63 ++++++++------- src/app/board/task/task.component.ts | 8 ++ .../context-menu-item.component.ts | 2 +- test/app/board/board.component.spec.ts | 5 +- test/app/board/task/task.component.spec.ts | 56 ++++++-------- .../user-admin/user-admin.component.spec.ts | 1 + .../context-menu/context-menu-item.spec.ts | 76 +++++++++++++++++++ 7 files changed, 143 insertions(+), 68 deletions(-) create mode 100644 test/app/shared/context-menu/context-menu-item.spec.ts diff --git a/src/app/board/board.component.ts b/src/app/board/board.component.ts index b50b963..17bebe9 100644 --- a/src/app/board/board.component.ts +++ b/src/app/board/board.component.ts @@ -30,6 +30,7 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit { private subs: Array; private hideFiltered: boolean; + private everyOtherDrop: boolean; public categoryFilter: number; public userFilter: number; @@ -132,46 +133,31 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit { } }); - let makeCall = false; - this.dragula.dropModel.subscribe(async (value: any) => { - makeCall = !makeCall; - - if (!makeCall) { - return; - } - + this.dragula.dropModel.subscribe((value: any) => { let taskId = +value[1].id, toColumnId = +value[2].parentNode.id, fromColumnId = +value[3].parentNode.id; - if (toColumnId === fromColumnId) { - fromColumnId = -1; + if (toColumnId !== fromColumnId) { + this.changeTaskColumn(taskId, toColumnId); + return; } - let updateList = []; + this.everyOtherDrop = !this.everyOtherDrop; + if (this.everyOtherDrop) { + for (var i = 0, len = this.activeBoard.columns.length; i < len; ++i) { + let column = this.activeBoard.columns[i]; - this.activeBoard.columns.forEach(column => { - if (column.id === toColumnId || column.id === fromColumnId) { - let position = 1, - taskToUpdate: Task; - - column.tasks.forEach(task => { - task.column_id = column.id; - task.position = position; - - position++; - }); - - updateList.push(column); + if (column.id === toColumnId) { + let pos = 0; + this.activeBoard.columns[i].tasks.forEach(task => { + task.position = pos; + pos++; + }); + this.boardService.updateColumn(column).subscribe(); + break; + } } - }); - - const update = async (column) => { - this.boardService.updateColumn(column).subscribe(); - } - - for (let i = 0, len = updateList.length; i < len; ++i) { - await update(updateList[i]); } }); } @@ -321,5 +307,18 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit { this.noBoardsMessage = this.strings.boards_noBoardsMessageAdmin; } } + + private changeTaskColumn(taskId: number, toColumnId: number) { + let column = this.activeBoard.columns + .find(col => col.id === toColumnId); + let task = column.tasks.find(ta => ta.id === taskId); + + if (task) { + task.column_id = toColumnId; + + this.boardService.updateTask(task).subscribe(); + } + } + } diff --git a/src/app/board/task/task.component.ts b/src/app/board/task/task.component.ts index 4e108d0..22a3e65 100644 --- a/src/app/board/task/task.component.ts +++ b/src/app/board/task/task.component.ts @@ -79,6 +79,10 @@ export class TaskDisplay implements OnInit { } private convertTaskDescription() { + if (!this.taskData || !this.taskData.description) { + return; + } + let data = this.boardService.convertMarkdown( this.taskData.description, this.markedCallback, true ); @@ -241,6 +245,10 @@ export class TaskDisplay implements OnInit { // Needs anonymous function for proper `this` context. private markedCallback = (error: any, text: string) => { + if (!this.activeBoard.issue_trackers) { + return; + } + this.activeBoard.issue_trackers.forEach(tracker => { let re = new RegExp(tracker.regex, 'ig'); let replacements = new Array(); diff --git a/src/app/shared/context-menu/context-menu-item.component.ts b/src/app/shared/context-menu/context-menu-item.component.ts index 502cce9..7b7f099 100644 --- a/src/app/shared/context-menu/context-menu-item.component.ts +++ b/src/app/shared/context-menu/context-menu-item.component.ts @@ -21,7 +21,7 @@ export class ContextMenuItem { @Input() isCustomEvent: boolean; - constructor(private el: ElementRef) { + constructor(public el: ElementRef) { const elem = el.nativeElement; elem.onclick = (event) => { diff --git a/test/app/board/board.component.spec.ts b/test/app/board/board.component.spec.ts index 363248c..02c4cf1 100644 --- a/test/app/board/board.component.spec.ts +++ b/test/app/board/board.component.spec.ts @@ -100,7 +100,6 @@ describe('BoardDisplay', () => { component.ngAfterContentInit(); expect((component.dragula).opts.moves).toEqual(jasmine.any(Function)); - expect(component.activeBoard.columns[0].tasks[0].position).toEqual(1); const test = (component.dragula).opts.moves(null, null, { classList: { contains: () => false } @@ -182,7 +181,7 @@ describe('BoardDisplay', () => { component.boards = [{}]; expect(component.noBoards()).toEqual(false); - }) + }); it('updates the active board from a service', () => { component.boardService.updateActiveBoard(null); @@ -195,7 +194,7 @@ describe('BoardDisplay', () => { component.auth.updateUser({ security_level: 1 }); expect(component.activeUser).toEqual(jasmine.any(Object)); - }) + }); }); diff --git a/test/app/board/task/task.component.spec.ts b/test/app/board/task/task.component.spec.ts index 9022ba9..51ce558 100644 --- a/test/app/board/task/task.component.spec.ts +++ b/test/app/board/task/task.component.spec.ts @@ -1,4 +1,4 @@ -import { TestBed, ComponentFixture } from '@angular/core/testing' +import { TestBed, ComponentFixture } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ElementRef } from '@angular/core'; @@ -10,7 +10,6 @@ import { AuthService, StringsService, Constants, - ContextMenuService, ModalService, NotificationsService } from '../../../../src/app/shared/services'; @@ -21,6 +20,15 @@ describe('TaskDisplay', () => { let component: TaskDisplay, fixture: ComponentFixture; + const eventMock = { + target: { + tagName: 'SELECT', + parentElement: { + parentElement: { click: () => {} } + } + } + }; + beforeEach(() => { TestBed.configureTestingModule({ imports: [ @@ -52,8 +60,6 @@ describe('TaskDisplay', () => { }); it('implements OnInit', () => { - component.ngOnInit(); - component.taskData = { id: 1, description: '' }; component.activeBoard = { id: 1, columns: [{ id: 1, name: 'test' }] }; component.ngOnInit(); @@ -83,9 +89,8 @@ describe('TaskDisplay', () => { ]; component.ngOnInit(); - const actual = component.getTaskDescription(); - - expect(actual).toEqual('

Make this HTML

\n'); + expect(component.taskData.html['changingThisBreaksApplicationSecurity']) + .toEqual('

Make this HTML

\n'); }); it('handles checklists in markdown', () => { @@ -93,9 +98,9 @@ describe('TaskDisplay', () => { component.taskData.description = ' - [x] One\n - [ ] Two'; component.ngOnInit(); - const actual = component.getTaskDescription(); - expect(actual).toEqual('
    \n
  • ' + - 'One
  • \n
  • Two
  • \n
\n'); + expect(component.taskData.html['changingThisBreaksApplicationSecurity']) + .toEqual('
    \n
  • One
  • \n' + + '
  • Two
  • \n
\n'); }); it('adds attributes to links in markdown', () => { @@ -103,8 +108,8 @@ describe('TaskDisplay', () => { component.taskData.description = '[link](google.com)'; component.ngOnInit(); - const actual = component.getTaskDescription(); - expect(actual).toContain('target="tb_external" rel="noreferrer"'); + expect(component.taskData.html['changingThisBreaksApplicationSecurity']) + .toContain('target="tb_external" rel="noreferrer"'); }); it('provides a custom style for percentage of task completed', () => { @@ -112,7 +117,7 @@ describe('TaskDisplay', () => { const actual = component.getPercentStyle(); - expect((actual).changingThisBreaksApplicationSecurity) + expect((actual)['changingThisBreaksApplicationSecurity']) .toContain('width: 50%;'); }); @@ -150,7 +155,7 @@ describe('TaskDisplay', () => { document.body.appendChild(select); component.taskData = { id: 1 }; - component.changeTaskColumn(); + component.changeTaskColumn(eventMock); const secondOpt = document.createElement('option'); @@ -166,23 +171,10 @@ describe('TaskDisplay', () => { }) }; }; - component.changeTaskColumn(); + component.changeTaskColumn(eventMock); expect(component.taskData.column_id).toEqual(1); }); - it('updates context menu on boards changes', () => { - component.activeBoard = { name: 'test', columns: [] }; - component.strings = { - boards_copyTaskTo: 'Copy To', - boards_moveTaskTo: 'Move To' - }; - component.taskData = { id: 1, description: '', due_date: '1/1/2018' }; - - component.boards = [{ id: 1, name: 'one' }, { id: 2, name: 'test' }]; - - // expect(component.contextMenuItems.length).toEqual(10); - }); - it('calls a service to copy a task to another board', () => { const sel = document.createElement('select'), opt = document.createElement('option'); @@ -209,13 +201,13 @@ describe('TaskDisplay', () => { return { subscribe: fn => fn({ status: 'success' }) }; }; - component.copyTaskToBoard(); + component.copyTaskToBoard(eventMock); (component.boardService.addTask) = () => { return { subscribe: fn => fn({ status: 'asdf', alerts: [{}] }) }; }; - component.copyTaskToBoard(); + component.copyTaskToBoard(eventMock); expect(emitted).toEqual(true); }); @@ -246,13 +238,13 @@ describe('TaskDisplay', () => { return { subscribe: fn => fn({ status: 'success' }) }; }; - component.moveTaskToBoard(); + component.moveTaskToBoard(eventMock); (component.boardService.updateTask) = () => { return { subscribe: fn => fn({ status: 'asdf', alerts: [{}] }) }; }; - component.moveTaskToBoard(); + component.moveTaskToBoard(eventMock); expect(emitted).toEqual(true); }); diff --git a/test/app/settings/user-admin/user-admin.component.spec.ts b/test/app/settings/user-admin/user-admin.component.spec.ts index 0a164cc..22f1116 100644 --- a/test/app/settings/user-admin/user-admin.component.spec.ts +++ b/test/app/settings/user-admin/user-admin.component.spec.ts @@ -145,6 +145,7 @@ describe('UserAdmin', () => { called = true; } }; }; + (component.modal).isOpen = () => true; component.modalProps = { prefix: true, diff --git a/test/app/shared/context-menu/context-menu-item.spec.ts b/test/app/shared/context-menu/context-menu-item.spec.ts new file mode 100644 index 0000000..1aa8309 --- /dev/null +++ b/test/app/shared/context-menu/context-menu-item.spec.ts @@ -0,0 +1,76 @@ +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { Component, ViewChild } from '@angular/core'; + +import { + ContextMenuItem +} from '../../../../src/app/shared/context-menu/context-menu-item.component'; + +@Component({ + selector: 'host-component', + template: `` +}) +class TestHostComponent { + @ViewChild(ContextMenuItem) + public menuItem: ContextMenuItem; +} + +describe('ContextMenuItem', () => { + let hostComponent: TestHostComponent, + hostFixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ ContextMenuItem, TestHostComponent ] + }).compileComponents(); + }); + + beforeEach(() => { + hostFixture = TestBed.createComponent(TestHostComponent); + hostComponent = hostFixture.componentInstance; + hostFixture.detectChanges(); + }); + + it('can be constructed', () => { + expect(hostComponent).toBeTruthy(); + }); + + it('handles clicks on the element', () => { + let pdCalled = false, + spCalled = false; + + const evt = { + preventDefault: () => { + pdCalled = true; + }, + stopPropagation: () => { + spCalled = true; + } + }; + + hostComponent.menuItem.el.nativeElement.onclick(evt); + + expect(pdCalled).toEqual(true); + expect(spCalled).toEqual(true); + }); + + it('handles context menu clicks on the element', () => { + let pdCalled = false, + spCalled = false; + + const evt = { + preventDefault: () => { + pdCalled = true; + }, + stopPropagation: () => { + spCalled = true; + } + }; + + hostComponent.menuItem.el.nativeElement.oncontextmenu(evt); + + expect(pdCalled).toEqual(true); + expect(spCalled).toEqual(true); + }); + +}); +