import { MarkdownEditorComponent, EditMode } from './markdown-editor.component'; import { MarkdownSymbol } from '../../../types/common.types'; import { Mock } from 'ts-mocks'; import Spy = jasmine.Spy; describe("MarkdownEditorComponent", () => { var component: MarkdownEditorComponent; var textarea: Mock; var documentMock: Mock; var $windowMock: Mock; beforeEach(() => { textarea = new Mock(); documentMock = new Mock(); $windowMock = new Mock(); const $documentMock: any = [documentMock.Object]; component = new MarkdownEditorComponent($documentMock, $windowMock.Object, 'chrome'); component.textarea = textarea.Object; }); describe("onBeforeUnload", () => { it("returns false to alert user about losing current changes", () => { component.changeEditMode("write"); const allow: boolean = component.onBeforeUnload(); expect(allow).toBe(false); }); }); describe("ngOnDestroy", () => { it("removes 'beforeunload' event listener", () => { $windowMock.setup(mock => mock.onbeforeunload).is(() => 1); component.ngOnDestroy(); expect($windowMock.Object.onbeforeunload.call(this)).toEqual(null); }); }); describe("changeEditMode", () => { it("sets component's edit mode to given mode", () => { const editMode: EditMode = "preview"; component.changeEditMode(editMode); expect(component.currentEditMode).toEqual(editMode); }); }); describe("insertSymbol", () => { var event: {symbol: MarkdownSymbol}; var markdownSymbols: {type: MarkdownSymbol, characters: string, shiftBy: number}[]; var innerText: string; beforeEach(() => { event = {symbol: 'heading1'}; innerText = "Here is some text"; markdownSymbols = [ {type: 'heading1', characters: '# ', shiftBy: 2}, {type: 'heading2', characters: '## ', shiftBy: 3}, {type: 'heading3', characters: '### ', shiftBy: 4}, {type: 'bold', characters: '****', shiftBy: 2}, {type: 'italics', characters: '__', shiftBy: 1}, {type: 'bulleted-list', characters: '- ', shiftBy: 2}, {type: 'numbered-list', characters: '1. ', shiftBy: 3}, {type: 'quote', characters: '> ', shiftBy: 2}, {type: 'link', characters: '[](url)', shiftBy: 1}, {type: 'code', characters: '``', shiftBy: 1}, ]; textarea.setup(mock => mock.focus); textarea.setup(mock => mock.substr).is((start, end) => ''); textarea.setup(mock => mock.val).is((value?) => innerText); textarea.setup(mock => mock.prop).is((prop) => { switch (prop) { case "selectionStart": return 0; case "selectionEnd": return 0; } }); documentMock.setup(mock => mock.execCommand).is((commandID, showUI, value) => false); }); it("focuses on markdown textarea", () => { component.insertSymbol(event); expect(textarea.Object.focus).toHaveBeenCalled(); }); it("inserts correct characters for given symbol at cursor position", () => { markdownSymbols.forEach((symbol) => { event.symbol = symbol.type; component.insertSymbol(event); expect((documentMock.Object.execCommand).calls.argsFor(0)[0]).toEqual('insertText'); expect((documentMock.Object.execCommand).calls.argsFor(0)[1]).toBe(false); expect((documentMock.Object.execCommand).calls.argsFor(0)[2]).toEqual(symbol.characters); (documentMock.Object.execCommand).calls.reset(); }); }); it("splices highlighted selection between inserted characters instead of deleting them", () => { markdownSymbols.slice(0, 1).forEach((symbol) => { textarea.setup(mock => mock.prop).is((prop) => { switch (prop) { case "selectionStart": return 0; case "selectionEnd": return innerText.length; } }); event.symbol = symbol.type; component.insertSymbol(event); expect((documentMock.Object.execCommand).calls.argsFor(0)[0]).toEqual('insertText'); expect((documentMock.Object.execCommand).calls.argsFor(0)[1]).toBe(false); expect((documentMock.Object.execCommand).calls.argsFor(0)[2]).toEqual(`${symbol.characters.slice(0, symbol.shiftBy)}${innerText}${symbol.characters.slice(symbol.shiftBy, symbol.characters.length)}`); (documentMock.Object.execCommand).calls.reset(); }); }); it("moves cursor to correct position for given symbol", () => { markdownSymbols.forEach((symbol) => { event.symbol = symbol.type; component.insertSymbol(event); expect((textarea.Object.prop).calls.argsFor(2)[0]).toEqual('selectionStart'); expect((textarea.Object.prop).calls.argsFor(2)[1]).toEqual(symbol.shiftBy); expect((textarea.Object.prop).calls.argsFor(3)[0]).toEqual('selectionEnd'); expect((textarea.Object.prop).calls.argsFor(3)[1]).toEqual(symbol.shiftBy); (textarea.Object.prop).calls.reset(); }); }); }); describe("saveChanges", () => { beforeEach(() => { component.content = "# Some markdown content"; }); it("emits output event with changed content", (done) => { component.save.subscribe((event: {editedContent: string}) => { expect(event.editedContent).toEqual(component.content); done(); }); component.saveChanges(); }); }); describe("discardChanges", () => { it("prompts user to confirm discarding changes", () => { const confirmSpy: Spy = $windowMock.setup(mock => mock.confirm).is((message) => false).Spy; component.discardChanges(); expect(confirmSpy.calls.argsFor(0)[0]).toEqual(`Are you sure you want to discard your changes?`); }); it("emits output event with no content if user confirms discarding changes", (done) => { $windowMock.setup(mock => mock.confirm).is((message) => true); component.discard.subscribe((event: {}) => { expect(event).toEqual({}); done(); }); component.discardChanges(); }); it("does not emit output event if user declines confirmation of discarding changes", (done) => { $windowMock.setup(mock => mock.confirm).is((message) => false); component.discard.subscribe((event: {}) => { fail(`Should not emit output event`); done(); }); component.discardChanges(); done(); }); }); });