mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-27 17:23:21 -05:00
Callout Dialog Fixes + WYSIWYG Improvements for Insert Link (#14494)
* wip * Works in all edit modes * Default value set * wip * preventdefault * cleanup, add tests * markup -> markdown * Ensure selection is persisted for WYSIWYG * Add simple dialog tests and some PR feedback * floating promise * PR comments, formatted markdown refactor * Change escaping logic + PR comments * PR feedback
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { MarkdownTextTransformer, MarkdownButtonType } from 'sql/workbench/contrib/notebook/browser/markdownToolbarActions';
|
||||
import { MarkdownTextTransformer, MarkdownButtonType, insertFormattedMarkdown } from 'sql/workbench/contrib/notebook/browser/markdownToolbarActions';
|
||||
import { NotebookService } from 'sql/workbench/services/notebook/browser/notebookServiceImpl';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { TestLifecycleService, TestEnvironmentService, TestAccessibilityService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
@@ -73,7 +73,7 @@ suite('MarkdownTextTransformer', () => {
|
||||
|
||||
cellModel = new CellModel(undefined, undefined, mockNotebookService.object);
|
||||
notebookEditor = new NotebookEditorStub({ cellGuid: cellModel.cellGuid, instantiationService: instantiationService });
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel, instantiationService, notebookEditor);
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel, notebookEditor);
|
||||
mockNotebookService.setup(s => s.findNotebookEditor(TypeMoq.It.isAny())).returns(() => notebookEditor);
|
||||
|
||||
let editor = notebookEditor.cellEditors[0].getEditor();
|
||||
@@ -114,10 +114,12 @@ suite('MarkdownTextTransformer', () => {
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING2, '');
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING3, '### ', true);
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING3, '');
|
||||
await testPreviouslyTransformedWithNoSelection(MarkdownButtonType.LINK_PREVIEW, '[test](./URL)', true);
|
||||
});
|
||||
|
||||
test('Transform text with one word selected', async () => {
|
||||
await testWithSingleWordSelected(MarkdownButtonType.CODE, '```\nWORD\n```');
|
||||
await testPreviouslyTransformedWithSingleWordSelected(MarkdownButtonType.LINK_PREVIEW, '[SampleURL](https://aka.ms)');
|
||||
});
|
||||
|
||||
test('Transform text with multiple words selected', async () => {
|
||||
@@ -148,7 +150,7 @@ suite('MarkdownTextTransformer', () => {
|
||||
test('Ensure notebook editor returns expected object', async () => {
|
||||
assert.deepEqual(notebookEditor, markdownTextTransformer.notebookEditor, 'Notebook editor does not match expected value');
|
||||
// Set markdown text transformer to not have a notebook editor passed in
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel, instantiationService);
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel);
|
||||
assert.equal(markdownTextTransformer.notebookEditor, undefined, 'No notebook editor should be returned');
|
||||
// Even after text is attempted to be transformed, there should be no editor, and therefore nothing on the text model
|
||||
await markdownTextTransformer.transformText(MarkdownButtonType.BOLD);
|
||||
@@ -164,6 +166,15 @@ suite('MarkdownTextTransformer', () => {
|
||||
assert.equal(textModel.getValue(), expectedValue, `${MarkdownButtonType[type]} with no selection failed (setValue ${setValue})`);
|
||||
}
|
||||
|
||||
|
||||
async function testPreviouslyTransformedWithNoSelection(type: MarkdownButtonType, expectedValue: string, setValue = false): Promise<void> {
|
||||
if (setValue) {
|
||||
textModel.setValue('');
|
||||
}
|
||||
await insertFormattedMarkdown('[test](./URL)', widget);
|
||||
assert.equal(textModel.getValue(), expectedValue, `${MarkdownButtonType[type]} with no selection and previously transformed md failed (setValue ${setValue})`);
|
||||
}
|
||||
|
||||
async function testWithSingleWordSelected(type: MarkdownButtonType, expectedValue: string): Promise<void> {
|
||||
let value = 'WORD';
|
||||
textModel.setValue(value);
|
||||
@@ -183,6 +194,18 @@ suite('MarkdownTextTransformer', () => {
|
||||
assert.equal(textModel.getValue(), value, `Undo operation for ${MarkdownButtonType[type]} with single word selection failed`);
|
||||
}
|
||||
|
||||
async function testPreviouslyTransformedWithSingleWordSelected(type: MarkdownButtonType, expectedValue: string): Promise<void> {
|
||||
let value = 'WORD';
|
||||
textModel.setValue(value);
|
||||
|
||||
// Test transformation (adding text)
|
||||
widget.setSelection({ startColumn: 1, startLineNumber: 1, endColumn: value.length + 1, endLineNumber: 1 });
|
||||
assert.equal(textModel.getValueInRange(widget.getSelection()), value, 'Expected selection is not found');
|
||||
await insertFormattedMarkdown('[SampleURL](https://aka.ms)', widget);
|
||||
const textModelValue = textModel.getValue();
|
||||
assert.equal(textModelValue, expectedValue, `${MarkdownButtonType[type]} with single word selection and previously transformed md failed`);
|
||||
}
|
||||
|
||||
async function testWithMultipleWordsSelected(type: MarkdownButtonType, expectedValue: string): Promise<void> {
|
||||
let value = 'Multi Words';
|
||||
textModel.setValue(value);
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { ILinkCalloutDialogOptions, LinkCalloutDialog } from 'sql/workbench/contrib/notebook/browser/calloutDialog/linkCalloutDialog';
|
||||
import { TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { escapeLabel, escapeUrl } from 'sql/workbench/contrib/notebook/browser/calloutDialog/common/utils';
|
||||
|
||||
suite('Link Callout Dialog', function (): void {
|
||||
let layoutService: ILayoutService;
|
||||
let themeService: IThemeService;
|
||||
let telemetryService: IAdsTelemetryService;
|
||||
let contextKeyService: IContextKeyService;
|
||||
|
||||
setup(() => {
|
||||
layoutService = new TestLayoutService();
|
||||
themeService = new TestThemeService();
|
||||
telemetryService = new NullAdsTelemetryService();
|
||||
contextKeyService = new MockContextKeyService();
|
||||
});
|
||||
|
||||
test('Should return empty markdown on cancel', async function (): Promise<void> {
|
||||
let linkCalloutDialog = new LinkCalloutDialog('Title', undefined, 'defaultLabel',
|
||||
undefined, themeService, layoutService, telemetryService, contextKeyService, undefined, undefined, undefined);
|
||||
linkCalloutDialog.render();
|
||||
|
||||
let deferred = new Deferred<ILinkCalloutDialogOptions>();
|
||||
// When I first open the callout dialog
|
||||
linkCalloutDialog.open().then(value => {
|
||||
deferred.resolve(value);
|
||||
});
|
||||
// And cancel the dialog
|
||||
linkCalloutDialog.cancel();
|
||||
let result = await deferred.promise;
|
||||
|
||||
assert.equal(result.insertUnescapedLinkLabel, 'defaultLabel', 'Label not returned correctly');
|
||||
assert.equal(result.insertUnescapedLinkUrl, undefined, 'URL not returned correctly');
|
||||
assert.equal(result.insertEscapedMarkdown, '', 'Markdown not returned correctly');
|
||||
});
|
||||
|
||||
test('Should return expected values on insert', async function (): Promise<void> {
|
||||
const defaultLabel = 'defaultLabel';
|
||||
const sampleUrl = 'https://www.aka.ms/azuredatastudio';
|
||||
let linkCalloutDialog = new LinkCalloutDialog('Title', undefined, defaultLabel,
|
||||
undefined, themeService, layoutService, telemetryService, contextKeyService, undefined, undefined, undefined);
|
||||
linkCalloutDialog.render();
|
||||
|
||||
let deferred = new Deferred<ILinkCalloutDialogOptions>();
|
||||
// When I first open the callout dialog
|
||||
linkCalloutDialog.open().then(value => {
|
||||
deferred.resolve(value);
|
||||
});
|
||||
|
||||
linkCalloutDialog.url = sampleUrl;
|
||||
|
||||
// And insert the dialog
|
||||
linkCalloutDialog.insert();
|
||||
let result = await deferred.promise;
|
||||
assert.equal(result.insertUnescapedLinkLabel, defaultLabel, 'Label not returned correctly');
|
||||
assert.equal(result.insertUnescapedLinkUrl, sampleUrl, 'URL not returned correctly');
|
||||
assert.equal(result.insertEscapedMarkdown, `[${defaultLabel}](${sampleUrl})`, 'Markdown not returned correctly');
|
||||
});
|
||||
|
||||
test('Should return expected values on insert when escape necessary', async function (): Promise<void> {
|
||||
const defaultLabel = 'default[]Label';
|
||||
const sampleUrl = 'https://www.aka.ms/azuredatastudio()';
|
||||
let linkCalloutDialog = new LinkCalloutDialog('Title', undefined, defaultLabel,
|
||||
undefined, themeService, layoutService, telemetryService, contextKeyService, undefined, undefined, undefined);
|
||||
linkCalloutDialog.render();
|
||||
|
||||
let deferred = new Deferred<ILinkCalloutDialogOptions>();
|
||||
// When I first open the callout dialog
|
||||
linkCalloutDialog.open().then(value => {
|
||||
deferred.resolve(value);
|
||||
});
|
||||
|
||||
linkCalloutDialog.url = sampleUrl;
|
||||
|
||||
// And insert the dialog
|
||||
linkCalloutDialog.insert();
|
||||
let result = await deferred.promise;
|
||||
assert.equal(result.insertUnescapedLinkLabel, defaultLabel, 'Label not returned correctly');
|
||||
assert.equal(result.insertUnescapedLinkUrl, sampleUrl, 'URL not returned correctly');
|
||||
assert.equal(result.insertEscapedMarkdown, '[default\[\]Label](https://www.aka.ms/azuredatastudio%28%29)', 'Markdown not returned correctly');
|
||||
});
|
||||
|
||||
test('Label escape', function (): void {
|
||||
assert.equal(escapeLabel('TestLabel'), 'TestLabel', 'Basic escape label test failed');
|
||||
assert.equal(escapeLabel('Test[]Label'), 'Test\[\]Label', 'Label test square brackets failed');
|
||||
assert.equal(escapeLabel('<>&[]'), '<>&\[\]', 'Label test known escaped characters failed');
|
||||
assert.equal(escapeLabel('<>&[]()'), '<>&\[\]()', 'Label test all escaped characters failed');
|
||||
});
|
||||
|
||||
test('URL escape', function (): void {
|
||||
assert.equal(escapeUrl('TestURL'), 'TestURL', 'Basic escape URL test failed');
|
||||
assert.equal(escapeUrl('Test()URL'), 'Test%28%29URL', 'URL test square brackets failed');
|
||||
assert.equal(escapeUrl('<>&()'), '<>&%28%29', 'URL test known escaped characters failed');
|
||||
assert.equal(escapeUrl('<>&()[]'), '<>&%28%29[]', 'URL test all escaped characters failed');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user