Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -3,27 +3,23 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { Application } from '../../application';
export function setup() {
describe('Editor', () => {
before(function () {
this.app.suiteName = 'Editor';
});
it('shows correct quick outline', async function () {
const app = this.app as SpectronApplication;
const app = this.app as Application;
await app.workbench.quickopen.openFile('www');
await app.workbench.editor.openOutline();
await app.workbench.quickopen.openQuickOutline();
await app.workbench.quickopen.waitForQuickOpenElements(names => names.length >= 6);
});
it(`finds 'All References' to 'app'`, async function () {
const app = this.app as SpectronApplication;
const app = this.app as Application;
await app.workbench.quickopen.openFile('www');
const references = await app.workbench.editor.findReferences('app', 7);
const references = await app.workbench.editor.findReferences('www', 'app', 7);
await references.waitForReferencesCountInTitle(3);
await references.waitForReferencesCount(3);
@@ -31,11 +27,10 @@ export function setup() {
});
it(`renames local 'app' variable`, async function () {
const app = this.app as SpectronApplication;
const app = this.app as Application;
await app.workbench.quickopen.openFile('www');
await app.workbench.editor.rename('www', 7, 'app', 'newApp');
await app.workbench.editor.waitForEditorContents('www', contents => contents.indexOf('newApp') > -1);
await app.screenCapturer.capture('Rename result');
});
// it('folds/unfolds the code correctly', async function () {
@@ -55,19 +50,19 @@ export function setup() {
// });
it(`verifies that 'Go To Definition' works`, async function () {
const app = this.app as SpectronApplication;
const app = this.app as Application;
await app.workbench.quickopen.openFile('app.js');
await app.workbench.editor.gotoDefinition('express', 11);
await app.workbench.editor.gotoDefinition('app.js', 'express', 11);
await app.workbench.waitForActiveTab('index.d.ts');
await app.workbench.editors.waitForActiveTab('index.d.ts');
});
it(`verifies that 'Peek Definition' works`, async function () {
const app = this.app as SpectronApplication;
const app = this.app as Application;
await app.workbench.quickopen.openFile('app.js');
const peek = await app.workbench.editor.peekDefinition('express', 11);
const peek = await app.workbench.editor.peekDefinition('app.js', 'express', 11);
await peek.waitForFile('index.d.ts');
});

View File

@@ -3,192 +3,131 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { QuickOutline } from './quickoutline';
import { References } from './peek';
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
const RENAME_BOX = '.monaco-editor .monaco-editor.rename-box';
const RENAME_INPUT = `${RENAME_BOX} .rename-input`;
const EDITOR = filename => `.monaco-editor[data-uri$="${filename}"]`;
const VIEW_LINES = filename => `${EDITOR(filename)} .view-lines`;
const LINE_NUMBERS = filename => `${EDITOR(filename)} .margin .margin-view-overlays .line-numbers`;
export class Editor {
private static readonly VIEW_LINES = '.monaco-editor .view-lines';
private static readonly LINE_NUMBERS = '.monaco-editor .margin .margin-view-overlays .line-numbers';
private static readonly FOLDING_EXPANDED = '.monaco-editor .margin .margin-view-overlays>:nth-child(${INDEX}) .folding';
private static readonly FOLDING_COLLAPSED = `${Editor.FOLDING_EXPANDED}.collapsed`;
constructor(private spectron: SpectronApplication) {
}
constructor(private code: Code, private commands: Commands) { }
async openOutline(): Promise<QuickOutline> {
const outline = new QuickOutline(this.spectron);
await outline.open();
return outline;
}
async findReferences(term: string, line: number): Promise<References> {
await this.clickOnTerm(term, line);
await this.spectron.workbench.quickopen.runCommand('Find All References');
const references = new References(this.spectron);
async findReferences(filename: string, term: string, line: number): Promise<References> {
await this.clickOnTerm(filename, term, line);
await this.commands.runCommand('Find All References');
const references = new References(this.code);
await references.waitUntilOpen();
return references;
}
async rename(filename: string, line: number, from: string, to: string): Promise<void> {
await this.clickOnTerm(from, line);
await this.spectron.workbench.quickopen.runCommand('Rename Symbol');
await this.clickOnTerm(filename, from, line);
await this.commands.runCommand('Rename Symbol');
await this.spectron.client.waitForActiveElement(RENAME_INPUT);
await this.spectron.client.setValue(RENAME_INPUT, to);
await this.code.waitForActiveElement(RENAME_INPUT);
await this.code.waitForSetValue(RENAME_INPUT, to);
await this.spectron.client.keys(['Enter', 'NULL']);
await this.code.dispatchKeybinding('enter');
}
async gotoDefinition(term: string, line: number): Promise<void> {
await this.clickOnTerm(term, line);
await this.spectron.workbench.quickopen.runCommand('Go to Definition');
async gotoDefinition(filename: string, term: string, line: number): Promise<void> {
await this.clickOnTerm(filename, term, line);
await this.commands.runCommand('Go to Definition');
}
async peekDefinition(term: string, line: number): Promise<References> {
await this.clickOnTerm(term, line);
await this.spectron.workbench.quickopen.runCommand('Peek Definition');
const peek = new References(this.spectron);
async peekDefinition(filename: string, term: string, line: number): Promise<References> {
await this.clickOnTerm(filename, term, line);
await this.commands.runCommand('Peek Definition');
const peek = new References(this.code);
await peek.waitUntilOpen();
return peek;
}
async waitForHighlightingLine(line: number): Promise<void> {
const currentLineIndex = await this.getViewLineIndex(line);
async waitForHighlightingLine(filename: string, line: number): Promise<void> {
const currentLineIndex = await this.getViewLineIndex(filename, line);
if (currentLineIndex) {
await this.spectron.client.waitForElement(`.monaco-editor .view-overlays>:nth-child(${currentLineIndex}) .current-line`);
await this.code.waitForElement(`.monaco-editor .view-overlays>:nth-child(${currentLineIndex}) .current-line`);
return;
}
throw new Error('Cannot find line ' + line);
}
async getSelector(term: string, line: number): Promise<string> {
const lineIndex = await this.getViewLineIndex(line);
const classNames = await this.spectron.client.waitFor(() => this.getClassSelectors(term, lineIndex), classNames => classNames && !!classNames.length, 'Getting class names for editor lines');
return `${Editor.VIEW_LINES}>:nth-child(${lineIndex}) span span.${classNames[0]}`;
private async getSelector(filename: string, term: string, line: number): Promise<string> {
const lineIndex = await this.getViewLineIndex(filename, line);
const classNames = await this.getClassSelectors(filename, term, lineIndex);
return `${VIEW_LINES(filename)}>:nth-child(${lineIndex}) span span.${classNames[0]}`;
}
async foldAtLine(line: number): Promise<any> {
const lineIndex = await this.getViewLineIndex(line);
await this.spectron.client.waitAndClick(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
await this.spectron.client.waitForElement(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
async foldAtLine(filename: string, line: number): Promise<any> {
const lineIndex = await this.getViewLineIndex(filename, line);
await this.code.waitAndClick(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
await this.code.waitForElement(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
}
async unfoldAtLine(line: number): Promise<any> {
const lineIndex = await this.getViewLineIndex(line);
await this.spectron.client.waitAndClick(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
await this.spectron.client.waitForElement(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
async unfoldAtLine(filename: string, line: number): Promise<any> {
const lineIndex = await this.getViewLineIndex(filename, line);
await this.code.waitAndClick(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
await this.code.waitForElement(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
}
async waitUntilHidden(line: number): Promise<void> {
await this.spectron.client.waitFor<number>(() => this.getViewLineIndexWithoutWait(line), lineNumber => lineNumber === undefined, 'Waiting until line number is hidden');
private async clickOnTerm(filename: string, term: string, line: number): Promise<void> {
const selector = await this.getSelector(filename, term, line);
await this.code.waitAndClick(selector);
}
async waitUntilShown(line: number): Promise<void> {
await this.getViewLineIndex(line);
}
async waitForEditorFocus(filename: string, lineNumber: number, selectorPrefix = ''): Promise<void> {
const editor = [selectorPrefix || '', EDITOR(filename)].join(' ');
const line = `${editor} .view-lines > .view-line:nth-child(${lineNumber})`;
const textarea = `${editor} textarea`;
async clickOnTerm(term: string, line: number): Promise<void> {
const selector = await this.getSelector(term, line);
await this.spectron.client.waitAndClick(selector);
await this.code.waitAndClick(line, 0, 0);
await this.code.waitForActiveElement(textarea);
}
async waitForTypeInEditor(filename: string, text: string, selectorPrefix = ''): Promise<any> {
const editor = [
selectorPrefix || '',
`.monaco-editor[data-uri$="${filename}"]`
].join(' ');
const editor = [selectorPrefix || '', EDITOR(filename)].join(' ');
await this.spectron.client.element(editor);
await this.code.waitForElement(editor);
const textarea = `${editor} textarea`;
await this.spectron.client.waitForActiveElement(textarea);
await this.code.waitForActiveElement(textarea);
// https://github.com/Microsoft/vscode/issues/34203#issuecomment-334441786
await this.spectron.client.spectron.client.selectorExecute(textarea, (elements, text) => {
const textarea = (Array.isArray(elements) ? elements : [elements])[0] as HTMLTextAreaElement;
const start = textarea.selectionStart;
const newStart = start + text.length;
const value = textarea.value;
const newValue = value.substr(0, start) + text + value.substr(start);
textarea.value = newValue;
textarea.setSelectionRange(newStart, newStart);
const event = new Event('input', { 'bubbles': true, 'cancelable': true });
textarea.dispatchEvent(event);
}, text);
await this.code.waitForTypeInEditor(textarea, text);
await this.waitForEditorContents(filename, c => c.indexOf(text) > -1, selectorPrefix);
}
async waitForEditorContents(filename: string, accept: (contents: string) => boolean, selectorPrefix = ''): Promise<any> {
const selector = [
selectorPrefix || '',
`.monaco-editor[data-uri$="${filename}"] .view-lines`
].join(' ');
return this.spectron.client.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' ')));
const selector = [selectorPrefix || '', `${EDITOR(filename)} .view-lines`].join(' ');
return this.code.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' ')));
}
async waitForActiveEditor(filename: string): Promise<any> {
const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`;
return this.spectron.client.waitForActiveElement(selector);
private async getClassSelectors(filename: string, term: string, viewline: number): Promise<string[]> {
const elements = await this.code.waitForElements(`${VIEW_LINES(filename)}>:nth-child(${viewline}) span span`, false, els => els.some(el => el.textContent === term));
const { className } = elements.filter(r => r.textContent === term)[0];
return className.split(/\s/g);
}
// async waitForActiveEditorFirstLineText(filename: string): Promise<string> {
// const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`;
// const result = await this.spectron.client.waitFor(
// () => this.spectron.client.spectron.client.execute(s => {
// if (!document.activeElement.matches(s)) {
// return undefined;
// }
private async getViewLineIndex(filename: string, line: number): Promise<number> {
const elements = await this.code.waitForElements(LINE_NUMBERS(filename), false, els => {
return els.some(el => el.textContent === `${line}`);
});
// let element: Element | null = document.activeElement;
// while (element && !/monaco-editor/.test(element.className) && element !== document.body) {
// element = element.parentElement;
// }
// if (element && /monaco-editor/.test(element.className)) {
// const firstLine = element.querySelector('.view-lines span span:nth-child(1)');
// if (firstLine) {
// return (firstLine.textContent || '').replace(/\u00a0/g, ' '); // DAMN
// }
// }
// return undefined;
// }, selector),
// r => typeof r.value === 'string',
// `wait for active editor first line: ${selector}`
// );
// return result.value;
// }
private async getClassSelectors(term: string, viewline: number): Promise<string[]> {
const result: { text: string, className: string }[] = await this.spectron.webclient.selectorExecute(`${Editor.VIEW_LINES}>:nth-child(${viewline}) span span`,
elements => (Array.isArray(elements) ? elements : [elements])
.map(element => ({ text: element.textContent, className: element.className })));
return result.filter(r => r.text === term).map(({ className }) => className);
}
private async getViewLineIndex(line: number): Promise<number> {
return await this.spectron.client.waitFor<number>(() => this.getViewLineIndexWithoutWait(line), void 0, 'Getting line index');
}
private async getViewLineIndexWithoutWait(line: number): Promise<number | undefined> {
const lineNumbers = await this.spectron.webclient.selectorExecute(Editor.LINE_NUMBERS,
elements => (Array.isArray(elements) ? elements : [elements]).map(element => element.textContent));
for (let index = 0; index < lineNumbers.length; index++) {
if (lineNumbers[index] === `${line}`) {
for (let index = 0; index < elements.length; index++) {
if (elements[index].textContent === `${line}`) {
return index + 1;
}
}
return undefined;
throw new Error('Line not found');
}
}

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Commands } from '../workbench/workbench';
import { Code } from '../../vscode/code';
export class Editors {
constructor(private code: Code, private commands: Commands) { }
async saveOpenedFile(): Promise<any> {
await this.commands.runCommand('workbench.action.files.save');
}
async selectTab(tabName: string, untitled: boolean = false): Promise<void> {
await this.code.waitAndClick(`.tabs-container div.tab[aria-label="${tabName}, tab"]`);
await this.waitForEditorFocus(tabName, untitled);
}
async waitForActiveEditor(filename: string): Promise<any> {
const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`;
return this.code.waitForActiveElement(selector);
}
async waitForEditorFocus(fileName: string, untitled: boolean = false): Promise<void> {
await this.waitForActiveTab(fileName);
await this.waitForActiveEditor(fileName);
}
async waitForActiveTab(fileName: string, isDirty: boolean = false): Promise<void> {
await this.code.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][aria-label="${fileName}, tab"]`);
}
async waitForTab(fileName: string, isDirty: boolean = false): Promise<void> {
await this.code.waitForElement(`.tabs-container div.tab${isDirty ? '.dirty' : ''}[aria-label="${fileName}, tab"]`);
}
async newUntitledFile(): Promise<void> {
await this.commands.runCommand('workbench.action.files.newUntitledFile');
await this.waitForEditorFocus('Untitled-1', true);
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { Code } from '../../vscode/code';
export class References {
@@ -12,30 +12,41 @@ export class References {
private static readonly REFERENCES_TITLE_COUNT = `${References.REFERENCES_WIDGET} .head .peekview-title .meta`;
private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-tree-row .reference`;
constructor(private spectron: SpectronApplication) {
constructor(private code: Code) { }
async waitUntilOpen(): Promise<void> {
await this.code.waitForElement(References.REFERENCES_WIDGET);
}
public async waitUntilOpen(): Promise<void> {
await this.spectron.client.waitForElement(References.REFERENCES_WIDGET);
}
public async waitForReferencesCountInTitle(count: number): Promise<void> {
await this.spectron.client.waitForText(References.REFERENCES_TITLE_COUNT, void 0, titleCount => {
async waitForReferencesCountInTitle(count: number): Promise<void> {
await this.code.waitForTextContent(References.REFERENCES_TITLE_COUNT, void 0, titleCount => {
const matches = titleCount.match(/\d+/);
return matches ? parseInt(matches[0]) === count : false;
});
}
public async waitForReferencesCount(count: number): Promise<void> {
await this.spectron.client.waitForElements(References.REFERENCES, result => result && result.length === count);
async waitForReferencesCount(count: number): Promise<void> {
await this.code.waitForElements(References.REFERENCES, false, result => result && result.length === count);
}
public async waitForFile(file: string): Promise<void> {
await this.spectron.client.waitForText(References.REFERENCES_TITLE_FILE_NAME, file);
async waitForFile(file: string): Promise<void> {
await this.code.waitForTextContent(References.REFERENCES_TITLE_FILE_NAME, file);
}
public async close(): Promise<void> {
await this.spectron.client.keys(['Escape', 'NULL']);
await this.spectron.client.waitForElement(References.REFERENCES_WIDGET, element => !element);
async close(): Promise<void> {
// Sometimes someone else eats up the `Escape` key
let count = 0;
while (true) {
await this.code.dispatchKeybinding('escape');
try {
await this.code.waitForElement(References.REFERENCES_WIDGET, el => !el, 10);
return;
} catch (err) {
if (++count > 5) {
throw err;
}
}
}
}
}

View File

@@ -1,28 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { QuickOpen } from '../quickopen/quickopen';
export class QuickOutline extends QuickOpen {
constructor(spectron: SpectronApplication) {
super(spectron);
}
public async open(): Promise<void> {
await this.spectron.client.waitFor(async () => {
await this.spectron.runCommand('workbench.action.gotoSymbol');
const entry = await this.spectron.client.element('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row .quick-open-entry');
if (entry) {
const text = await this.spectron.client.getText('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row .quick-open-entry .monaco-icon-label .label-name .monaco-highlighted-label span');
if (text !== 'No symbol information for the file') {
return entry;
}
}
await this.closeQuickOpen();
}, undefined, 'Opening Outline');
}
}