import {sleep} from './helpers/thread'; import VscodeListenerHelper from './helpers/listeners/vscode_listener'; import {VSCodeInterface} from '../src/models/ext_models'; import { Workspace, Env, Window, Disposable, TextDocument, TextDocumentContentChangeEvent, TextDocumentChangeEvent, } from './helpers/models/vscode'; const expect = require('chai').expect; describe('vscode editor events tests', () => { const workspace: Workspace = new Workspace(); const env: Env = new Env(); const window: Window = new Window(); const disposable: Disposable = new Disposable(); const iface: VSCodeInterface = { disposable, window, workspace, env }; const vscodeListener: VscodeListenerHelper = new VscodeListenerHelper(iface); afterEach(async () => { await activeUnfocusedEvent(); vscodeListener.changeWatch.reset(); }); context('when the plugin is intialized', () => { it('will subscribe to events', () => { expect(vscodeListener.getSubscriptions().length).to.eq(6); }); }); context('when document changes are triggered', () => { it('will receive the text document open event', async () => { const event: TextDocument = buildTextDocument('open.txt'); vscodeListener.onOpenHandlerHelper(event); expect(vscodeListener.changeWatch.receivedOpenEvent).to.eq(true); }); it('will receive the text document change event for a single delete', async () => { const textDocument: TextDocument = buildTextDocument('singledelete.txt'); const textDocChangeEvent: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent.rangeLength = 1; sendTextDocChangeEvent(textDocChangeEvent, textDocument); expect(vscodeListener.changeWatch.receivedSingleDeleteEvent).to.eq(true); }); it('will receive the text document change event for a single add', async () => { const textDocument: TextDocument = buildTextDocument('singleadd.txt'); const textDocChangeEvent: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent.text = 'a'; sendTextDocChangeEvent(textDocChangeEvent, textDocument); expect(vscodeListener.changeWatch.receivedSingleCharacterAddEvent).to.eq(true); }); it('will receive the text document change event for text replacement', async () => { const textDocument: TextDocument = buildTextDocument('replacement.txt'); const textDocChangeEvent: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent.rangeLength = 1; textDocChangeEvent.text = 'a'; sendTextDocChangeEvent(textDocChangeEvent, textDocument); expect(vscodeListener.changeWatch.receivedReplacementEvent).to.eq(true); }); it('will receive the text document change event for multi character add', async () => { const textDocument: TextDocument = buildTextDocument('multiadd.txt'); const textDocChangeEvent: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent.text = 'abc123'; sendTextDocChangeEvent(textDocChangeEvent, textDocument); expect(vscodeListener.changeWatch.receivedMultiAddEvent).to.eq(true); }); it('will receive the text document change event for multi delete', async () => { const textDocument: TextDocument = buildTextDocument('multidelete.txt'); const textDocChangeEvent: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent.rangeLength = 10; sendTextDocChangeEvent(textDocChangeEvent, textDocument); expect(vscodeListener.changeWatch.receivedMultiDeleteEvent).to.eq(true); }); }); context('when window state is unfocused', async () => { it('will not emit an unfocused event', async () => { await inactiveUnfocusedEvent(); expect(vscodeListener.changeWatch.receivedUnfocusEvent).to.eq(false); }); it('will publish the kpm payload', async () => { await buildKpmData(); expect(vscodeListener.changeWatch.kpm_payload.docs_changed).to.not.be.undefined; }); it('will have the correct characters added', async () => { await buildKpmData(); expect(vscodeListener.changeWatch.getKpmStats().charactersAdded).to.eq(28); }); it('will have the ai approximate characters added', async () => { await buildAiAutoCompleteKpmData(); expect(vscodeListener.changeWatch.getKpmStats().aiCharactersAdded).to.eq(40); }); it('will have the indent characters added', async () => { await buildIndentKpmData(); expect(vscodeListener.changeWatch.getKpmStats().aiCharactersAdded).to.eq(0); expect(vscodeListener.changeWatch.getKpmStats().autoIndents).to.eq(1); }); it('will have the correct kpm data for multiple keystroke types', async () => { await buildMultiKeystrokeTypesKpmData(); expect(vscodeListener.changeWatch.getKpmStats().charactersAdded).to.eq(28); expect(vscodeListener.changeWatch.getKpmStats().keystrokes).to.eq(5); expect(vscodeListener.changeWatch.getKpmStats().charactersDeleted).to.eq(1); expect(vscodeListener.changeWatch.getKpmStats().autoIndents).to.eq(1); expect(vscodeListener.changeWatch.getKpmStats().aiCharactersAdded).to.eq(40); }); it('will have the correct keystrokes', async () => { await buildKpmData(); expect(vscodeListener.changeWatch.getKpmStats().keystrokes).to.eq(3); }); it('will have the correct characters deleted', async () => { await buildKpmData(); expect(vscodeListener.changeWatch.getKpmStats().charactersDeleted).to.eq(1); }); it('will have the correct number of files changed', async () => { await buildKpmData(); expect(Object.keys(vscodeListener.changeWatch.kpm_payload.docs_changed).length).to.eq(3); }); it('will not send a payload with zero keystrokes', async () => { await buildEmptyKpmData(); expect(vscodeListener.changeWatch.getKpmStats().keystrokes).to.eq(0); expect(vscodeListener.changeWatch.receivedKpmEventCounter).to.eq(0); }); it('will receive the kpm event', async () => { await buildKpmData(); activeUnfocusedEvent(); activeUnfocusedEvent(); expect(vscodeListener.changeWatch.receivedKpmEventCounter).to.eq(1); }); it('will filter out the zero keystrokes payload', async () => { await buildPartialEmptyKpmData(); expect(Object.keys(vscodeListener.changeWatch.kpm_payload.docs_changed).length).to.eq(1); }); it('will process a jupyter notebooks file payload', async () => { const textDocs: any = await buildJupyterKpmData(); expect(vscodeListener.changeWatch.getKpmStats().charactersAdded).to.eq(textDocs.docEvent.text.replace(/\s/g, '').length); }); it('will have the start and end milliseconds', async () => { await buildKpmData(); expect(vscodeListener.changeWatch.hasValidStartAndEnd()).to.eq(true); }); it('will set the end time chronologially', async () => { await buildMultiFileChanges(); expect(vscodeListener.changeWatch.hasValidStartAndEnd()).to.eq(true); }); }); context('when payload processing completes', () => { it('will have an undefined project mapping', async () => { await buildKpmData(); expect(vscodeListener.getProjectChangeInfo()).to.be.undefined; }); }); context('when a file is saved', () => { it('will emit the save event', async () => { const event: TextDocument = buildTextDocument('save.txt'); vscodeListener.onDidSaveHelper(event); await sleep(100); expect(vscodeListener.changeWatch.receivedSaveEvent).to.eq(true); }); it('will receive the project change information', async () => { const event: TextDocument = buildTextDocument('save.txt'); vscodeListener.onDidSaveHelper(event); await sleep(100); expect(vscodeListener.changeWatch.kpm_payload.docs_changed['save.txt']).to.exist; }) }); function sendTextDocChangeEvent(textDocChangeEvent: TextDocumentContentChangeEvent, textDocument: TextDocument) { const event: TextDocumentChangeEvent = new TextDocumentChangeEvent(); event.contentChanges = [textDocChangeEvent]; event.document = textDocument; vscodeListener.onEventHandlerHelper(event); } function buildTextDocument(fileName: string) { const textDocument: TextDocument = new TextDocument(fileName); textDocument.lineCount = 200; return textDocument; } async function buildJupyterKpmData() { const textDocument: TextDocument = buildTextDocument('analysis.ipynb'); const textDocChangeEvent: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent.text = 'Lorem ipsum dolor sit amet'; textDocChangeEvent.rangeLength = 10; sendTextDocChangeEvent(textDocChangeEvent, textDocument); await activeUnfocusedEvent(); return {docEvent: textDocChangeEvent}; } async function buildEmptyKpmData() { const textDocument: TextDocument = buildTextDocument('emptykpm.txt'); const textDocChangeEvent: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); sendTextDocChangeEvent(textDocChangeEvent, textDocument); await activeUnfocusedEvent(); } async function buildPartialEmptyKpmData() { const textDocument1: TextDocument = buildTextDocument('fileopen.txt'); const textDocChangeEvent1: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); sendTextDocChangeEvent(textDocChangeEvent1, textDocument1); const textDocument2: TextDocument = buildTextDocument('multiadd2.txt'); const textDocChangeEvent2: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent2.text = 'Lorem ipsum dolor sit amet'; sendTextDocChangeEvent(textDocChangeEvent2, textDocument2); await activeUnfocusedEvent(); } async function buildMultiFileChanges() { const textDocument1: TextDocument = buildTextDocument('filechange1.txt'); vscodeListener.onOpenHandlerHelper(textDocument1); const textDocChangeEvent1: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent1.text = 'Lorem ipsum dolor sit amet'; sendTextDocChangeEvent(textDocChangeEvent1, textDocument1); await sleep(1000); const textDocument2: TextDocument = buildTextDocument('filechange2.txt'); vscodeListener.onOpenHandlerHelper(textDocument2); const textDocChangeEvent2: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent2.text = 'Lorem ipsum dolor sit amet'; sendTextDocChangeEvent(textDocChangeEvent2, textDocument2); await activeUnfocusedEvent(); } async function buildKpmData() { const textDocument1: TextDocument = buildTextDocument('multiadd1.txt'); const textDocChangeEvent1: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent1.text = 'abc123'; textDocChangeEvent1.rangeLength = 1; sendTextDocChangeEvent(textDocChangeEvent1, textDocument1); const textDocument2: TextDocument = buildTextDocument('multiadd2.txt'); const textDocChangeEvent2: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent2.text = 'Lorem ipsum dolor sit amet'; textDocChangeEvent2.rangeLength = 1; sendTextDocChangeEvent(textDocChangeEvent2, textDocument2); const textDocument3: TextDocument = buildTextDocument('singledelete.txt'); const textDocChangeEvent3: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent3.rangeLength = 1; sendTextDocChangeEvent(textDocChangeEvent3, textDocument3); await activeUnfocusedEvent(); return {docEvent1: textDocChangeEvent1, docEvent2: textDocChangeEvent2, docEvent3: textDocChangeEvent3}; } async function buildAiAutoCompleteKpmData() { const textDocument1: TextDocument = buildTextDocument('ai_approximate1.txt'); const textDocChangeEvent1: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent1.text = ' workspaceEntity.project_name = "workspace"'; textDocChangeEvent1.rangeLength = 0; sendTextDocChangeEvent(textDocChangeEvent1, textDocument1); return {docEvent1: textDocChangeEvent1}; } async function buildIndentKpmData() { const textDocument1: TextDocument = buildTextDocument('indent_file.txt'); const textDocChangeEvent1: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent1.text = ' '; textDocChangeEvent1.rangeLength = 0; sendTextDocChangeEvent(textDocChangeEvent1, textDocument1); return {docEvent1: textDocChangeEvent1}; } async function buildMultiKeystrokeTypesKpmData() { const textDocument1: TextDocument = buildTextDocument('multiadd1.txt'); const textDocChangeEvent1: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent1.text = 'abc123'; textDocChangeEvent1.rangeLength = 1; sendTextDocChangeEvent(textDocChangeEvent1, textDocument1); const textDocument2: TextDocument = buildTextDocument('multiadd2.txt'); const textDocChangeEvent2: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent2.text = 'Lorem ipsum dolor sit amet'; textDocChangeEvent2.rangeLength = 1; sendTextDocChangeEvent(textDocChangeEvent2, textDocument2); const textDocument3: TextDocument = buildTextDocument('singledelete.txt'); const textDocChangeEvent3: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent3.rangeLength = 1; sendTextDocChangeEvent(textDocChangeEvent3, textDocument3); const textDocument4: TextDocument = buildTextDocument('indent_file.txt'); const textDocChangeEvent4: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent4.text = ' '; textDocChangeEvent4.rangeLength = 0; sendTextDocChangeEvent(textDocChangeEvent4, textDocument4); const textDocument5: TextDocument = buildTextDocument('ai_approximate1.txt'); const textDocChangeEvent5: TextDocumentContentChangeEvent = new TextDocumentContentChangeEvent(); textDocChangeEvent5.text = ' workspaceEntity.project_name = "workspace"'; textDocChangeEvent5.rangeLength = 0; sendTextDocChangeEvent(textDocChangeEvent5, textDocument5); await activeUnfocusedEvent(); return {docEvent1: textDocChangeEvent1, docEvent2: textDocChangeEvent2, docEvent3: textDocChangeEvent3}; } async function activeUnfocusedEvent() { vscodeListener.onDidChangeWindowStateHelper(true /* isActive */, false /* isFocused */); } async function inactiveUnfocusedEvent() { vscodeListener.onDidChangeWindowStateHelper(false/* isActive */, false /* isFocused */); } });