Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
555
lib/vscode/extensions/emmet/src/test/abbreviationAction.test.ts
Normal file
555
lib/vscode/extensions/emmet/src/test/abbreviationAction.test.ts
Normal file
@ -0,0 +1,555 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection, workspace, CancellationTokenSource, CompletionTriggerKind, ConfigurationTarget } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { expandEmmetAbbreviation } from '../abbreviationActions';
|
||||
import { DefaultCompletionItemProvider } from '../defaultCompletionProvider';
|
||||
|
||||
const completionProvider = new DefaultCompletionItemProvider();
|
||||
|
||||
const htmlContents = `
|
||||
<body class="header">
|
||||
<ul class="nav main">
|
||||
<li class="item1">img</li>
|
||||
<li class="item2">hithere</li>
|
||||
ul>li
|
||||
ul>li*2
|
||||
ul>li.item$*2
|
||||
ul>li.item$@44*2
|
||||
<div i
|
||||
</ul>
|
||||
<style>
|
||||
.boo {
|
||||
display: dn; m10
|
||||
}
|
||||
</style>
|
||||
<span></span>
|
||||
(ul>li.item$)*2
|
||||
(ul>li.item$)*2+span
|
||||
(div>dl>(dt+dd)*2)
|
||||
<script type="text/html">
|
||||
span.hello
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
span.bye
|
||||
</script>
|
||||
</body>
|
||||
`;
|
||||
|
||||
suite('Tests for Expand Abbreviations (HTML)', () => {
|
||||
const oldValueForExcludeLanguages = workspace.getConfiguration('emmet').inspect('excludeLanguages');
|
||||
const oldValueForInlcudeLanguages = workspace.getConfiguration('emmet').inspect('includeLanguages');
|
||||
teardown(() => {
|
||||
// close all editors
|
||||
return closeAllEditors;
|
||||
});
|
||||
|
||||
test('Expand snippets (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(3, 23, 3, 23), 'img', '<img src=\"\" alt=\"\">');
|
||||
});
|
||||
|
||||
test('Expand snippets in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(3, 23, 3, 23), 'img', '<img src=\"\" alt=\"\">');
|
||||
});
|
||||
|
||||
test('Expand snippets when no parent node (HTML)', () => {
|
||||
return withRandomFileEditor('img', 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 3, 0, 3);
|
||||
await expandEmmetAbbreviation(null);
|
||||
assert.strictEqual(editor.document.getText(), '<img src=\"\" alt=\"\">');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand snippets when no parent node in completion list (HTML)', () => {
|
||||
return withRandomFileEditor('img', 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 3, 0, 3);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise) {
|
||||
assert.strictEqual(!completionPromise, false, `Got unexpected undefined instead of a completion promise`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const completionList = await completionPromise;
|
||||
assert.strictEqual(completionList && completionList.items && completionList.items.length > 0, true);
|
||||
if (completionList) {
|
||||
assert.strictEqual(completionList.items[0].label, 'img');
|
||||
assert.strictEqual(((<string>completionList.items[0].documentation) || '').replace(/\|/g, ''), '<img src=\"\" alt=\"\">');
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(5, 25, 5, 25), 'ul>li', '<ul>\n\t\t\t<li></li>\n\t\t</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(5, 25, 5, 25), 'ul>li', '<ul>\n\t<li></li>\n</ul>');
|
||||
});
|
||||
|
||||
test('Expand text that is neither an abbreviation nor a snippet to tags (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(4, 20, 4, 27), 'hithere', '<hithere></hithere>');
|
||||
});
|
||||
|
||||
test('Do not Expand text that is neither an abbreviation nor a snippet to tags in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(4, 20, 4, 27), 'hithere', '<hithere></hithere>', true);
|
||||
});
|
||||
|
||||
test('Expand abbreviation with repeaters (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(6, 27, 6, 27), 'ul>li*2', '<ul>\n\t\t\t<li></li>\n\t\t\t<li></li>\n\t\t</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with repeaters in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(6, 27, 6, 27), 'ul>li*2', '<ul>\n\t<li></li>\n\t<li></li>\n</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(7, 33, 7, 33), 'ul>li.item$*2', '<ul>\n\t\t\t<li class="item1"></li>\n\t\t\t<li class="item2"></li>\n\t\t</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(7, 33, 7, 33), 'ul>li.item$*2', '<ul>\n\t<li class="item1"></li>\n\t<li class="item2"></li>\n</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters with offset (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(8, 36, 8, 36), 'ul>li.item$@44*2', '<ul>\n\t\t\t<li class="item44"></li>\n\t\t\t<li class="item45"></li>\n\t\t</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters with offset in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(8, 36, 8, 36), 'ul>li.item$@44*2', '<ul>\n\t<li class="item44"></li>\n\t<li class="item45"></li>\n</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters in groups (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(17, 16, 17, 16), '(ul>li.item$)*2', '<ul>\n\t\t<li class="item1"></li>\n\t</ul>\n\t<ul>\n\t\t<li class="item2"></li>\n\t</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters in groups in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(17, 16, 17, 16), '(ul>li.item$)*2', '<ul>\n\t<li class="item1"></li>\n</ul>\n<ul>\n\t<li class="item2"></li>\n</ul>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters in groups with sibling in the end (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(18, 21, 18, 21), '(ul>li.item$)*2+span', '<ul>\n\t\t<li class="item1"></li>\n\t</ul>\n\t<ul>\n\t\t<li class="item2"></li>\n\t</ul>\n\t<span></span>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with numbered repeaters in groups with sibling in the end in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(18, 21, 18, 21), '(ul>li.item$)*2+span', '<ul>\n\t<li class="item1"></li>\n</ul>\n<ul>\n\t<li class="item2"></li>\n</ul>\n<span></span>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with nested groups (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(19, 19, 19, 19), '(div>dl>(dt+dd)*2)', '<div>\n\t\t<dl>\n\t\t\t<dt></dt>\n\t\t\t<dd></dd>\n\t\t\t<dt></dt>\n\t\t\t<dd></dd>\n\t\t</dl>\n\t</div>');
|
||||
});
|
||||
|
||||
test('Expand abbreviation with nested groups in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(19, 19, 19, 19), '(div>dl>(dt+dd)*2)', '<div>\n\t<dl>\n\t\t<dt></dt>\n\t\t<dd></dd>\n\t\t<dt></dt>\n\t\t<dd></dd>\n\t</dl>\n</div>');
|
||||
});
|
||||
|
||||
test('Expand tag that is opened, but not closed (HTML)', () => {
|
||||
return testExpandAbbreviation('html', new Selection(9, 6, 9, 6), '<div', '<div></div>');
|
||||
});
|
||||
|
||||
test('Do not Expand tag that is opened, but not closed in completion list (HTML)', () => {
|
||||
return testHtmlCompletionProvider(new Selection(9, 6, 9, 6), '<div', '<div></div>', true);
|
||||
});
|
||||
|
||||
test('No expanding text inside open tag (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(2, 4, 2, 4);
|
||||
await expandEmmetAbbreviation(null);
|
||||
assert.strictEqual(editor.document.getText(), htmlContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text inside open tag in completion list (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', (editor, _doc) => {
|
||||
editor.selection = new Selection(2, 4, 2, 4);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text inside open tag when there is no closing tag (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(9, 8, 9, 8);
|
||||
await expandEmmetAbbreviation(null);
|
||||
assert.strictEqual(editor.document.getText(), htmlContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text inside open tag when there is no closing tag in completion list (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', (editor, _doc) => {
|
||||
editor.selection = new Selection(9, 8, 9, 8);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text inside open tag when there is no closing tag when there is no parent node (HTML)', () => {
|
||||
const fileContents = '<img s';
|
||||
return withRandomFileEditor(fileContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 6, 0, 6);
|
||||
await expandEmmetAbbreviation(null);
|
||||
assert.strictEqual(editor.document.getText(), fileContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text in completion list inside open tag when there is no closing tag when there is no parent node (HTML)', () => {
|
||||
const fileContents = '<img s';
|
||||
return withRandomFileEditor(fileContents, 'html', (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 6, 0, 6);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand css when inside style tag (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(13, 16, 13, 19);
|
||||
const expandPromise = expandEmmetAbbreviation({ language: 'css' });
|
||||
if (!expandPromise) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
await expandPromise;
|
||||
assert.strictEqual(editor.document.getText(), htmlContents.replace('m10', 'margin: 10px;'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand css when inside style tag in completion list (HTML)', () => {
|
||||
const abbreviation = 'm10';
|
||||
const expandedText = 'margin: 10px;';
|
||||
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(13, 16, 13, 19);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding m10`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const completionList = await completionPromise;
|
||||
if (!completionList || !completionList.items || !completionList.items.length) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding m10`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
|
||||
assert.strictEqual(((<string>emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
assert.strictEqual(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text inside style tag if position is not for property name (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(13, 14, 13, 14);
|
||||
await expandEmmetAbbreviation(null);
|
||||
assert.strictEqual(editor.document.getText(), htmlContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand css when inside style attribute (HTML)', () => {
|
||||
const styleAttributeContent = '<div style="m10" class="hello"></div>';
|
||||
return withRandomFileEditor(styleAttributeContent, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 15, 0, 15);
|
||||
const expandPromise = expandEmmetAbbreviation(null);
|
||||
if (!expandPromise) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
await expandPromise;
|
||||
assert.strictEqual(editor.document.getText(), styleAttributeContent.replace('m10', 'margin: 10px;'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand css when inside style attribute in completion list (HTML)', () => {
|
||||
const abbreviation = 'm10';
|
||||
const expandedText = 'margin: 10px;';
|
||||
|
||||
return withRandomFileEditor('<div style="m10" class="hello"></div>', 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 15, 0, 15);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding m10`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const completionList = await completionPromise;
|
||||
if (!completionList || !completionList.items || !completionList.items.length) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding m10`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
|
||||
assert.strictEqual(((<string>emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
assert.strictEqual(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand html when inside script tag with html type (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(21, 12, 21, 12);
|
||||
const expandPromise = expandEmmetAbbreviation(null);
|
||||
if (!expandPromise) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
await expandPromise;
|
||||
assert.strictEqual(editor.document.getText(), htmlContents.replace('span.hello', '<span class="hello"></span>'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand html in completion list when inside script tag with html type (HTML)', () => {
|
||||
const abbreviation = 'span.hello';
|
||||
const expandedText = '<span class="hello"></span>';
|
||||
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(21, 12, 21, 12);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding span.hello`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const completionList = await completionPromise;
|
||||
if (!completionList || !completionList.items || !completionList.items.length) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding span.hello`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.strictEqual(emmetCompletionItem.label, abbreviation, `Label of completion item doesnt match.`);
|
||||
assert.strictEqual(((<string>emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text inside script tag with javascript type (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(24, 12, 24, 12);
|
||||
await expandEmmetAbbreviation(null);
|
||||
assert.strictEqual(editor.document.getText(), htmlContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text in completion list inside script tag with javascript type (HTML)', () => {
|
||||
return withRandomFileEditor(htmlContents, 'html', (editor, _doc) => {
|
||||
editor.selection = new Selection(24, 12, 24, 12);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand html when inside script tag with javascript type if js is mapped to html (HTML)', async () => {
|
||||
await workspace.getConfiguration('emmet').update('includeLanguages', { 'javascript': 'html' }, ConfigurationTarget.Global);
|
||||
await withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(24, 10, 24, 10);
|
||||
const expandPromise = expandEmmetAbbreviation(null);
|
||||
if (!expandPromise) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
await expandPromise;
|
||||
assert.strictEqual(editor.document.getText(), htmlContents.replace('span.bye', '<span class="bye"></span>'));
|
||||
});
|
||||
return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForInlcudeLanguages || {}, ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
test('Expand html in completion list when inside script tag with javascript type if js is mapped to html (HTML)', async () => {
|
||||
const abbreviation = 'span.bye';
|
||||
const expandedText = '<span class="bye"></span>';
|
||||
await workspace.getConfiguration('emmet').update('includeLanguages', { 'javascript': 'html' }, ConfigurationTarget.Global);
|
||||
await withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(24, 10, 24, 10);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding span.bye`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const completionList = await completionPromise;
|
||||
if (!completionList || !completionList.items || !completionList.items.length) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding span.bye`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.strictEqual(emmetCompletionItem.label, abbreviation, `Label of completion item (${emmetCompletionItem.label}) doesnt match.`);
|
||||
assert.strictEqual(((<string>emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForInlcudeLanguages || {}, ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
// test('No expanding when html is excluded in the settings', () => {
|
||||
// return workspace.getConfiguration('emmet').update('excludeLanguages', ['html'], ConfigurationTarget.Global).then(() => {
|
||||
// return testExpandAbbreviation('html', new Selection(9, 6, 9, 6), '', '', true).then(() => {
|
||||
// return workspace.getConfiguration('emmet').update('excludeLanguages', oldValueForExcludeLanguages ? oldValueForExcludeLanguages.globalValue : undefined, ConfigurationTarget.Global);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
test('No expanding when html is excluded in the settings in completion list', async () => {
|
||||
await workspace.getConfiguration('emmet').update('excludeLanguages', ['html'], ConfigurationTarget.Global);
|
||||
await testHtmlCompletionProvider(new Selection(9, 6, 9, 6), '', '', true);
|
||||
return workspace.getConfiguration('emmet').update('excludeLanguages', oldValueForExcludeLanguages ? oldValueForExcludeLanguages.globalValue : undefined, ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
// test('No expanding when php (mapped syntax) is excluded in the settings', () => {
|
||||
// return workspace.getConfiguration('emmet').update('excludeLanguages', ['php'], ConfigurationTarget.Global).then(() => {
|
||||
// return testExpandAbbreviation('php', new Selection(9, 6, 9, 6), '', '', true).then(() => {
|
||||
// return workspace.getConfiguration('emmet').update('excludeLanguages', oldValueForExcludeLanguages ? oldValueForExcludeLanguages.globalValue : undefined, ConfigurationTarget.Global);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
});
|
||||
|
||||
suite('Tests for jsx, xml and xsl', () => {
|
||||
const oldValueForSyntaxProfiles = workspace.getConfiguration('emmet').inspect('syntaxProfiles');
|
||||
teardown(closeAllEditors);
|
||||
|
||||
test('Expand abbreviation with className instead of class in jsx', () => {
|
||||
return withRandomFileEditor('ul.nav', 'javascriptreact', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 6, 0, 6);
|
||||
await expandEmmetAbbreviation({ language: 'javascriptreact' });
|
||||
assert.strictEqual(editor.document.getText(), '<ul className="nav"></ul>');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation with self closing tags for jsx', () => {
|
||||
return withRandomFileEditor('img', 'javascriptreact', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 6, 0, 6);
|
||||
await expandEmmetAbbreviation({ language: 'javascriptreact' });
|
||||
assert.strictEqual(editor.document.getText(), '<img src="" alt=""/>');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation with single quotes for jsx', async () => {
|
||||
await workspace.getConfiguration('emmet').update('syntaxProfiles', { jsx: { 'attr_quotes': 'single' } }, ConfigurationTarget.Global);
|
||||
return withRandomFileEditor('img', 'javascriptreact', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 6, 0, 6);
|
||||
await expandEmmetAbbreviation({ language: 'javascriptreact' });
|
||||
assert.strictEqual(editor.document.getText(), '<img src=\'\' alt=\'\'/>');
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles', oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global);
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation with self closing tags for xml', () => {
|
||||
return withRandomFileEditor('img', 'xml', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 6, 0, 6);
|
||||
await expandEmmetAbbreviation({ language: 'xml' });
|
||||
assert.strictEqual(editor.document.getText(), '<img src="" alt=""/>');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation with no self closing tags for html', () => {
|
||||
return withRandomFileEditor('img', 'html', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 6, 0, 6);
|
||||
await expandEmmetAbbreviation({ language: 'html' });
|
||||
assert.strictEqual(editor.document.getText(), '<img src="" alt="">');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation with condition containing less than sign for jsx', () => {
|
||||
return withRandomFileEditor('if (foo < 10) { span.bar', 'javascriptreact', async (editor, _doc) => {
|
||||
editor.selection = new Selection(0, 27, 0, 27);
|
||||
await expandEmmetAbbreviation({ language: 'javascriptreact' });
|
||||
assert.strictEqual(editor.document.getText(), 'if (foo < 10) { <span className="bar"></span>');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('No expanding text inside open tag in completion list (jsx)', () => {
|
||||
return testNoCompletion('jsx', htmlContents, new Selection(2, 4, 2, 4));
|
||||
});
|
||||
|
||||
test('No expanding tag that is opened, but not closed in completion list (jsx)', () => {
|
||||
return testNoCompletion('jsx', htmlContents, new Selection(9, 6, 9, 6));
|
||||
});
|
||||
|
||||
test('No expanding text inside open tag when there is no closing tag in completion list (jsx)', () => {
|
||||
return testNoCompletion('jsx', htmlContents, new Selection(9, 8, 9, 8));
|
||||
});
|
||||
|
||||
test('No expanding text in completion list inside open tag when there is no closing tag when there is no parent node (jsx)', () => {
|
||||
return testNoCompletion('jsx', '<img s', new Selection(0, 6, 0, 6));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function testExpandAbbreviation(syntax: string, selection: Selection, abbreviation: string, expandedText: string, shouldFail?: boolean): Thenable<any> {
|
||||
return withRandomFileEditor(htmlContents, syntax, async (editor, _doc) => {
|
||||
editor.selection = selection;
|
||||
const expandPromise = expandEmmetAbbreviation(null);
|
||||
if (!expandPromise) {
|
||||
if (!shouldFail) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
await expandPromise;
|
||||
assert.strictEqual(editor.document.getText(), htmlContents.replace(abbreviation, expandedText));
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function testHtmlCompletionProvider(selection: Selection, abbreviation: string, expandedText: string, shouldFail?: boolean): Thenable<any> {
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
editor.selection = selection;
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise) {
|
||||
if (!shouldFail) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const completionList = await completionPromise;
|
||||
if (!completionList || !completionList.items || !completionList.items.length) {
|
||||
if (!shouldFail) {
|
||||
assert.strictEqual(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.strictEqual(emmetCompletionItem.label, abbreviation, `Label of completion item doesnt match.`);
|
||||
assert.strictEqual(((<string>emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function testNoCompletion(syntax: string, fileContents: string, selection: Selection): Thenable<any> {
|
||||
return withRandomFileEditor(fileContents, syntax, (editor, _doc) => {
|
||||
editor.selection = selection;
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
128
lib/vscode/extensions/emmet/src/test/completion.test.ts
Normal file
128
lib/vscode/extensions/emmet/src/test/completion.test.ts
Normal file
@ -0,0 +1,128 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import { CancellationTokenSource, CompletionTriggerKind, Selection } from 'vscode';
|
||||
import { DefaultCompletionItemProvider } from '../defaultCompletionProvider';
|
||||
import { closeAllEditors, withRandomFileEditor } from './testUtils';
|
||||
|
||||
const completionProvider = new DefaultCompletionItemProvider();
|
||||
|
||||
suite('Tests for completion in CSS embedded in HTML', () => {
|
||||
teardown(() => {
|
||||
// close all editors
|
||||
return closeAllEditors;
|
||||
});
|
||||
|
||||
test('style attribute & attribute value in html', async () => {
|
||||
await testHtmlCompletionProvider('<div style="|"', [{ label: 'padding: ;' }]);
|
||||
await testHtmlCompletionProvider(`<div style='|'`, [{ label: 'padding: ;' }]);
|
||||
await testHtmlCompletionProvider(`<div style='p|'`, [{ label: 'padding: ;' }]);
|
||||
await testHtmlCompletionProvider(`<div style='color: #0|'`, [{ label: '#000000' }]);
|
||||
});
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/79766
|
||||
test('#79766, correct region determination', async () => {
|
||||
await testHtmlCompletionProvider(`<div style="color: #000">di|</div>`, [
|
||||
{ label: 'div', documentation: `<div>|</div>` }
|
||||
]);
|
||||
});
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/86941
|
||||
test('#86941, widows should not be completed', async () => {
|
||||
await testCssCompletionProvider(`.foo { wi| }`, [
|
||||
{ label: 'widows: ;', documentation: `widows: ;` }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
interface TestCompletionItem {
|
||||
label: string;
|
||||
|
||||
documentation?: string;
|
||||
}
|
||||
|
||||
function testHtmlCompletionProvider(contents: string, expectedItems: TestCompletionItem[]): Thenable<any> {
|
||||
const cursorPos = contents.indexOf('|');
|
||||
const htmlContents = contents.slice(0, cursorPos) + contents.slice(cursorPos + 1);
|
||||
|
||||
return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => {
|
||||
const selection = new Selection(editor.document.positionAt(cursorPos), editor.document.positionAt(cursorPos));
|
||||
editor.selection = selection;
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(
|
||||
editor.document,
|
||||
editor.selection.active,
|
||||
cancelSrc.token,
|
||||
{ triggerKind: CompletionTriggerKind.Invoke }
|
||||
);
|
||||
if (!completionPromise) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const completionList = await completionPromise;
|
||||
if (!completionList || !completionList.items || !completionList.items.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
expectedItems.forEach(eItem => {
|
||||
const matches = completionList.items.filter(i => i.label === eItem.label);
|
||||
const match = matches && matches.length > 0 ? matches[0] : undefined;
|
||||
assert.ok(match, `Didn't find completion item with label ${eItem.label}`);
|
||||
|
||||
if (match) {
|
||||
assert.equal(match.detail, 'Emmet Abbreviation', `Match needs to come from Emmet`);
|
||||
|
||||
if (eItem.documentation) {
|
||||
assert.equal(match.documentation, eItem.documentation, `Emmet completion Documentation doesn't match`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function testCssCompletionProvider(contents: string, expectedItems: TestCompletionItem[]): Thenable<any> {
|
||||
const cursorPos = contents.indexOf('|');
|
||||
const cssContents = contents.slice(0, cursorPos) + contents.slice(cursorPos + 1);
|
||||
|
||||
return withRandomFileEditor(cssContents, 'css', async (editor, _doc) => {
|
||||
const selection = new Selection(editor.document.positionAt(cursorPos), editor.document.positionAt(cursorPos));
|
||||
editor.selection = selection;
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(
|
||||
editor.document,
|
||||
editor.selection.active,
|
||||
cancelSrc.token,
|
||||
{ triggerKind: CompletionTriggerKind.Invoke }
|
||||
);
|
||||
if (!completionPromise) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const completionList = await completionPromise;
|
||||
if (!completionList || !completionList.items || !completionList.items.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
expectedItems.forEach(eItem => {
|
||||
const matches = completionList.items.filter(i => i.label === eItem.label);
|
||||
const match = matches && matches.length > 0 ? matches[0] : undefined;
|
||||
assert.ok(match, `Didn't find completion item with label ${eItem.label}`);
|
||||
|
||||
if (match) {
|
||||
assert.equal(match.detail, 'Emmet Abbreviation', `Match needs to come from Emmet`);
|
||||
|
||||
if (eItem.documentation) {
|
||||
assert.equal(match.documentation, eItem.documentation, `Emmet completion Documentation doesn't match`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
@ -0,0 +1,503 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection, CompletionList, CancellationTokenSource, Position, CompletionTriggerKind } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { expandEmmetAbbreviation } from '../abbreviationActions';
|
||||
import { DefaultCompletionItemProvider } from '../defaultCompletionProvider';
|
||||
|
||||
const completionProvider = new DefaultCompletionItemProvider();
|
||||
const cssContents = `
|
||||
.boo {
|
||||
margin: 20px 10px;
|
||||
pos:f
|
||||
background-image: url('tryme.png');
|
||||
pos:f
|
||||
}
|
||||
|
||||
.boo .hoo {
|
||||
margin: 10px;
|
||||
ind
|
||||
}
|
||||
`;
|
||||
|
||||
const scssContents = `
|
||||
.boo {
|
||||
margin: 10px;
|
||||
p10
|
||||
.hoo {
|
||||
p20
|
||||
}
|
||||
}
|
||||
@include b(alert) {
|
||||
|
||||
margin: 10px;
|
||||
p30
|
||||
|
||||
@include b(alert) {
|
||||
p40
|
||||
}
|
||||
}
|
||||
.foo {
|
||||
margin: 10px;
|
||||
margin: a
|
||||
.hoo {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
suite('Tests for Expand Abbreviations (CSS)', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
test('Expand abbreviation (CSS)', () => {
|
||||
return withRandomFileEditor(cssContents, 'css', (editor, _) => {
|
||||
editor.selections = [new Selection(3, 1, 3, 6), new Selection(5, 1, 5, 6)];
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), cssContents.replace(/pos:f/g, 'position: fixed;'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('No emmet when cursor inside comment (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
/*margin: 10px;
|
||||
m10
|
||||
padding: 10px;*/
|
||||
display: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(3, 4, 3, 4);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion at property value`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('No emmet when cursor in selector of a rule (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
nav#
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(5, 4, 5, 4);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion at property value`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Skip when typing property values when there is a property in the next line (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
margin: a
|
||||
margin: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(2, 10, 2, 10);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion at property value`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Skip when typing the last property value in single line rules (CSS)', () => {
|
||||
const testContent = `.foo {padding: 10px; margin: a}`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(0, 30, 0, 30);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(0, 30), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion at property value`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Allow hex color or !important when typing property values when there is a property in the next line (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
margin: #12 !
|
||||
margin: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(2, 12), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(2, 14), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
|
||||
if (!completionPromise1 || !completionPromise2) {
|
||||
assert.equal(1, 2, `Completion promise wasnt returned`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const callBack = (completionList: CompletionList, expandedText: string) => {
|
||||
if (!completionList.items || !completionList.items.length) {
|
||||
assert.equal(1, 2, `Empty Completions`);
|
||||
return;
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
|
||||
assert.equal((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
};
|
||||
|
||||
return Promise.all<CompletionList>([completionPromise1, completionPromise2]).then(([result1, result2]) => {
|
||||
callBack(result1, '#121212');
|
||||
callBack(result2, '!important');
|
||||
editor.selections = [new Selection(2, 12, 2, 12), new Selection(2, 14, 2, 14)];
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent.replace('#12', '#121212').replace('!', '!important'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Skip when typing property values when there is a property in the previous line (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
margin: 10px;
|
||||
margin: a
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(3, 10, 3, 10);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(3, 10), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion at property value`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Allow hex color or !important when typing property values when there is a property in the previous line (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
margin: 10px;
|
||||
margin: #12 !
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3, 12), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(3, 14), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
|
||||
if (!completionPromise1 || !completionPromise2) {
|
||||
assert.equal(1, 2, `Completion promise wasnt returned`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const callBack = (completionList: CompletionList, expandedText: string) => {
|
||||
if (!completionList.items || !completionList.items.length) {
|
||||
assert.equal(1, 2, `Empty Completions`);
|
||||
return;
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
|
||||
assert.equal((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
};
|
||||
|
||||
return Promise.all<CompletionList>([completionPromise1, completionPromise2]).then(([result1, result2]) => {
|
||||
callBack(result1, '#121212');
|
||||
callBack(result2, '!important');
|
||||
editor.selections = [new Selection(3, 12, 3, 12), new Selection(3, 14, 3, 14)];
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent.replace('#12', '#121212').replace('!', '!important'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Skip when typing property values when it is the only property in the rule (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
margin: a
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(2, 10, 2, 10);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion at property value`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Allow hex colors or !important when typing property values when it is the only property in the rule (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
margin: #12 !
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(2, 12), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(2, 14), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
|
||||
if (!completionPromise1 || !completionPromise2) {
|
||||
assert.equal(1, 2, `Completion promise wasnt returned`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const callBack = (completionList: CompletionList, expandedText: string) => {
|
||||
if (!completionList.items || !completionList.items.length) {
|
||||
assert.equal(1, 2, `Empty Completions`);
|
||||
return;
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
|
||||
assert.equal((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
};
|
||||
|
||||
return Promise.all<CompletionList>([completionPromise1, completionPromise2]).then(([result1, result2]) => {
|
||||
callBack(result1, '#121212');
|
||||
callBack(result2, '!important');
|
||||
editor.selections = [new Selection(2, 12, 2, 12), new Selection(2, 14, 2, 14)];
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent.replace('#12', '#121212').replace('!', '!important'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('# shouldnt expand to hex color when in selector (CSS)', () => {
|
||||
const testContent = `
|
||||
.foo {
|
||||
#
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(testContent, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(2, 2, 2, 2);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), testContent);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 2), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion of hex color at property name`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Expand abbreviation in completion list (CSS)', () => {
|
||||
const abbreviation = 'pos:f';
|
||||
const expandedText = 'position: fixed;';
|
||||
|
||||
return withRandomFileEditor(cssContents, 'css', (editor, _) => {
|
||||
editor.selection = new Selection(3, 1, 3, 6);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3, 6), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(5, 6), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise1 || !completionPromise2) {
|
||||
assert.equal(1, 2, `Problem with expanding pos:f`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const callBack = (completionList: CompletionList) => {
|
||||
if (!completionList.items || !completionList.items.length) {
|
||||
assert.equal(1, 2, `Problem with expanding pos:f`);
|
||||
return;
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
|
||||
assert.equal((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);
|
||||
};
|
||||
|
||||
return Promise.all<CompletionList>([completionPromise1, completionPromise2]).then(([result1, result2]) => {
|
||||
callBack(result1);
|
||||
callBack(result2);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation (SCSS)', () => {
|
||||
return withRandomFileEditor(scssContents, 'scss', (editor, _) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 4, 3, 4),
|
||||
new Selection(5, 5, 5, 5),
|
||||
new Selection(11, 4, 11, 4),
|
||||
new Selection(14, 5, 14, 5)
|
||||
];
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), scssContents.replace(/p(\d\d)/g, 'padding: $1px;'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Expand abbreviation in completion list (SCSS)', () => {
|
||||
|
||||
return withRandomFileEditor(scssContents, 'scss', (editor, _) => {
|
||||
editor.selection = new Selection(3, 4, 3, 4);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3, 4), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(5, 5), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
const completionPromise3 = completionProvider.provideCompletionItems(editor.document, new Position(11, 4), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
const completionPromise4 = completionProvider.provideCompletionItems(editor.document, new Position(14, 5), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (!completionPromise1) {
|
||||
assert.equal(1, 2, `Problem with expanding padding abbreviations at line 3 col 4`);
|
||||
}
|
||||
if (!completionPromise2) {
|
||||
assert.equal(1, 2, `Problem with expanding padding abbreviations at line 5 col 5`);
|
||||
}
|
||||
if (!completionPromise3) {
|
||||
assert.equal(1, 2, `Problem with expanding padding abbreviations at line 11 col 4`);
|
||||
}
|
||||
if (!completionPromise4) {
|
||||
assert.equal(1, 2, `Problem with expanding padding abbreviations at line 14 col 5`);
|
||||
}
|
||||
|
||||
if (!completionPromise1 || !completionPromise2 || !completionPromise3 || !completionPromise4) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const callBack = (completionList: CompletionList, abbreviation: string, expandedText: string) => {
|
||||
if (!completionList.items || !completionList.items.length) {
|
||||
assert.equal(1, 2, `Problem with expanding m10`);
|
||||
return;
|
||||
}
|
||||
const emmetCompletionItem = completionList.items[0];
|
||||
assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
|
||||
assert.equal((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
|
||||
assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);
|
||||
};
|
||||
|
||||
return Promise.all<CompletionList>([completionPromise1, completionPromise2, completionPromise3, completionPromise4]).then(([result1, result2, result3, result4]) => {
|
||||
callBack(result1, 'p10', 'padding: 10px;');
|
||||
callBack(result2, 'p20', 'padding: 20px;');
|
||||
callBack(result3, 'p30', 'padding: 30px;');
|
||||
callBack(result4, 'p40', 'padding: 40px;');
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Invalid locations for abbreviations in scss', () => {
|
||||
const scssContentsNoExpand = `
|
||||
m10
|
||||
.boo {
|
||||
margin: 10px;
|
||||
.hoo {
|
||||
background:
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(scssContentsNoExpand, 'scss', (editor, _) => {
|
||||
editor.selections = [
|
||||
new Selection(1, 3, 1, 3), // outside rule
|
||||
new Selection(5, 15, 5, 15) // in the value part of property value
|
||||
];
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), scssContentsNoExpand);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Invalid locations for abbreviations in scss in completion list', () => {
|
||||
const scssContentsNoExpand = `
|
||||
m10
|
||||
.boo {
|
||||
margin: 10px;
|
||||
.hoo {
|
||||
background:
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(scssContentsNoExpand, 'scss', (editor, _) => {
|
||||
editor.selection = new Selection(1, 3, 1, 3); // outside rule
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
let completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `m10 gets expanded in invalid location (outside rule)`);
|
||||
}
|
||||
|
||||
editor.selection = new Selection(5, 15, 5, 15); // in the value part of property value
|
||||
completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
return completionPromise.then((completionList: CompletionList | undefined) => {
|
||||
if (completionList && completionList.items && completionList.items.length > 0) {
|
||||
assert.equal(1, 2, `m10 gets expanded in invalid location (n the value part of property value)`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Skip when typing property values when there is a nested rule in the next line (SCSS)', () => {
|
||||
return withRandomFileEditor(scssContents, 'scss', (editor, _) => {
|
||||
editor.selection = new Selection(19, 10, 19, 10);
|
||||
return expandEmmetAbbreviation(null).then(() => {
|
||||
assert.equal(editor.document.getText(), scssContents);
|
||||
const cancelSrc = new CancellationTokenSource();
|
||||
const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(19, 10), cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
if (completionPromise) {
|
||||
assert.equal(1, 2, `Invalid completion at property value`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,368 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { fetchEditPoint } from '../editPoint';
|
||||
import { fetchSelectItem } from '../selectItem';
|
||||
import { balanceOut, balanceIn } from '../balance';
|
||||
|
||||
suite('Tests for Next/Previous Select/Edit point and Balance actions', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
const cssContents = `
|
||||
.boo {
|
||||
margin: 20px 10px;
|
||||
background-image: url('tryme.png');
|
||||
}
|
||||
|
||||
.boo .hoo {
|
||||
margin: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const scssContents = `
|
||||
.boo {
|
||||
margin: 20px 10px;
|
||||
background-image: url('tryme.png');
|
||||
|
||||
.boo .hoo {
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const htmlContents = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
\t\t
|
||||
</div>
|
||||
<div class="header">
|
||||
<ul class="nav main">
|
||||
<li class="item1">Item 1</li>
|
||||
<li class="item2">Item 2</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
test('Emmet Next/Prev Edit point in html file', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, _) => {
|
||||
editor.selections = [new Selection(1, 5, 1, 5)];
|
||||
|
||||
let expectedNextEditPoints: [number, number][] = [[4, 16], [6, 8], [10, 2], [20, 0]];
|
||||
expectedNextEditPoints.forEach(([line, col]) => {
|
||||
fetchEditPoint('next');
|
||||
testSelection(editor.selection, col, line);
|
||||
});
|
||||
|
||||
let expectedPrevEditPoints = [[10, 2], [6, 8], [4, 16], [0, 0]];
|
||||
expectedPrevEditPoints.forEach(([line, col]) => {
|
||||
fetchEditPoint('prev');
|
||||
testSelection(editor.selection, col, line);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Select Next/Prev Item in html file', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, _) => {
|
||||
editor.selections = [new Selection(2, 2, 2, 2)];
|
||||
|
||||
let expectedNextItemPoints: [number, number, number][] = [
|
||||
[2, 1, 5], // html
|
||||
[2, 6, 15], // lang="en"
|
||||
[2, 12, 14], // en
|
||||
[3, 1, 5], // head
|
||||
[4, 2, 6], // meta
|
||||
[4, 7, 17], // charset=""
|
||||
[5, 2, 6], // meta
|
||||
[5, 7, 22], // name="viewport"
|
||||
[5, 13, 21], // viewport
|
||||
[5, 23, 70], // content="width=device-width, initial-scale=1.0"
|
||||
[5, 32, 69], // width=device-width, initial-scale=1.0
|
||||
[5, 32, 51], // width=device-width,
|
||||
[5, 52, 69], // initial-scale=1.0
|
||||
[6, 2, 7] // title
|
||||
];
|
||||
expectedNextItemPoints.forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('next');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
editor.selections = [new Selection(6, 15, 6, 15)];
|
||||
expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('prev');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Select Next/Prev item at boundary', function(): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, _) => {
|
||||
editor.selections = [new Selection(4, 1, 4, 1)];
|
||||
|
||||
fetchSelectItem('next');
|
||||
testSelection(editor.selection, 2, 4, 6);
|
||||
|
||||
editor.selections = [new Selection(4, 1, 4, 1)];
|
||||
|
||||
fetchSelectItem('prev');
|
||||
testSelection(editor.selection, 1, 3, 5);
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Next/Prev Item in html template', function (): any {
|
||||
const templateContents = `
|
||||
<script type="text/template">
|
||||
<div class="header">
|
||||
<ul class="nav main">
|
||||
</ul>
|
||||
</div>
|
||||
</script>
|
||||
`;
|
||||
return withRandomFileEditor(templateContents, '.html', (editor, _) => {
|
||||
editor.selections = [new Selection(2, 2, 2, 2)];
|
||||
|
||||
let expectedNextItemPoints: [number, number, number][] = [
|
||||
[2, 2, 5], // div
|
||||
[2, 6, 20], // class="header"
|
||||
[2, 13, 19], // header
|
||||
[3, 3, 5], // ul
|
||||
[3, 6, 22], // class="nav main"
|
||||
[3, 13, 21], // nav main
|
||||
[3, 13, 16], // nav
|
||||
[3, 17, 21], // main
|
||||
];
|
||||
expectedNextItemPoints.forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('next');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
editor.selections = [new Selection(4, 1, 4, 1)];
|
||||
expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('prev');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Select Next/Prev Item in css file', function (): any {
|
||||
return withRandomFileEditor(cssContents, '.css', (editor, _) => {
|
||||
editor.selections = [new Selection(0, 0, 0, 0)];
|
||||
|
||||
let expectedNextItemPoints: [number, number, number][] = [
|
||||
[1, 0, 4], // .boo
|
||||
[2, 1, 19], // margin: 20px 10px;
|
||||
[2, 9, 18], // 20px 10px
|
||||
[2, 9, 13], // 20px
|
||||
[2, 14, 18], // 10px
|
||||
[3, 1, 36], // background-image: url('tryme.png');
|
||||
[3, 19, 35], // url('tryme.png')
|
||||
[6, 0, 9], // .boo .hoo
|
||||
[7, 1, 14], // margin: 10px;
|
||||
[7, 9, 13], // 10px
|
||||
];
|
||||
expectedNextItemPoints.forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('next');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
editor.selections = [new Selection(9, 0, 9, 0)];
|
||||
expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('prev');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Select Next/Prev Item in scss file with nested rules', function (): any {
|
||||
return withRandomFileEditor(scssContents, '.scss', (editor, _) => {
|
||||
editor.selections = [new Selection(0, 0, 0, 0)];
|
||||
|
||||
let expectedNextItemPoints: [number, number, number][] = [
|
||||
[1, 0, 4], // .boo
|
||||
[2, 1, 19], // margin: 20px 10px;
|
||||
[2, 9, 18], // 20px 10px
|
||||
[2, 9, 13], // 20px
|
||||
[2, 14, 18], // 10px
|
||||
[3, 1, 36], // background-image: url('tryme.png');
|
||||
[3, 19, 35], // url('tryme.png')
|
||||
[5, 1, 10], // .boo .hoo
|
||||
[6, 2, 15], // margin: 10px;
|
||||
[6, 10, 14], // 10px
|
||||
];
|
||||
expectedNextItemPoints.forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('next');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
editor.selections = [new Selection(8, 0, 8, 0)];
|
||||
expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => {
|
||||
fetchSelectItem('prev');
|
||||
testSelection(editor.selection, colstart, line, colend);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Balance Out in html file', function (): any {
|
||||
return withRandomFileEditor(htmlContents, 'html', (editor, _) => {
|
||||
|
||||
editor.selections = [new Selection(14, 6, 14, 10)];
|
||||
let expectedBalanceOutRanges: [number, number, number, number][] = [
|
||||
[14, 3, 14, 32], // <li class="item1">Item 1</li>
|
||||
[13, 23, 16, 2], // inner contents of <ul class="nav main">
|
||||
[13, 2, 16, 7], // outer contents of <ul class="nav main">
|
||||
[12, 21, 17, 1], // inner contents of <div class="header">
|
||||
[12, 1, 17, 7], // outer contents of <div class="header">
|
||||
[8, 6, 18, 0], // inner contents of <body>
|
||||
[8, 0, 18, 7], // outer contents of <body>
|
||||
[2, 16, 19, 0], // inner contents of <html>
|
||||
[2, 0, 19, 7], // outer contents of <html>
|
||||
];
|
||||
expectedBalanceOutRanges.forEach(([linestart, colstart, lineend, colend]) => {
|
||||
balanceOut();
|
||||
testSelection(editor.selection, colstart, linestart, colend, lineend);
|
||||
});
|
||||
|
||||
editor.selections = [new Selection(12, 7, 12, 7)];
|
||||
let expectedBalanceInRanges: [number, number, number, number][] = [
|
||||
[12, 21, 17, 1], // inner contents of <div class="header">
|
||||
[13, 2, 16, 7], // outer contents of <ul class="nav main">
|
||||
[13, 23, 16, 2], // inner contents of <ul class="nav main">
|
||||
[14, 3, 14, 32], // <li class="item1">Item 1</li>
|
||||
[14, 21, 14, 27] // Item 1
|
||||
];
|
||||
expectedBalanceInRanges.forEach(([linestart, colstart, lineend, colend]) => {
|
||||
balanceIn();
|
||||
testSelection(editor.selection, colstart, linestart, colend, lineend);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Balance In using the same stack as Balance out in html file', function (): any {
|
||||
return withRandomFileEditor(htmlContents, 'html', (editor, _) => {
|
||||
|
||||
editor.selections = [new Selection(15, 6, 15, 10)];
|
||||
let expectedBalanceOutRanges: [number, number, number, number][] = [
|
||||
[15, 3, 15, 32], // <li class="item1">Item 2</li>
|
||||
[13, 23, 16, 2], // inner contents of <ul class="nav main">
|
||||
[13, 2, 16, 7], // outer contents of <ul class="nav main">
|
||||
[12, 21, 17, 1], // inner contents of <div class="header">
|
||||
[12, 1, 17, 7], // outer contents of <div class="header">
|
||||
[8, 6, 18, 0], // inner contents of <body>
|
||||
[8, 0, 18, 7], // outer contents of <body>
|
||||
[2, 16, 19, 0], // inner contents of <html>
|
||||
[2, 0, 19, 7], // outer contents of <html>
|
||||
];
|
||||
expectedBalanceOutRanges.forEach(([linestart, colstart, lineend, colend]) => {
|
||||
balanceOut();
|
||||
testSelection(editor.selection, colstart, linestart, colend, lineend);
|
||||
});
|
||||
|
||||
expectedBalanceOutRanges.reverse().forEach(([linestart, colstart, lineend, colend]) => {
|
||||
testSelection(editor.selection, colstart, linestart, colend, lineend);
|
||||
balanceIn();
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Balance In when selection doesnt span entire node or its inner contents', function (): any {
|
||||
return withRandomFileEditor(htmlContents, 'html', (editor, _) => {
|
||||
|
||||
editor.selection = new Selection(13, 7, 13, 10); // Inside the open tag of <ul class="nav main">
|
||||
balanceIn();
|
||||
testSelection(editor.selection, 23, 13, 2, 16); // inner contents of <ul class="nav main">
|
||||
|
||||
editor.selection = new Selection(16, 4, 16, 5); // Inside the open close of <ul class="nav main">
|
||||
balanceIn();
|
||||
testSelection(editor.selection, 23, 13, 2, 16); // inner contents of <ul class="nav main">
|
||||
|
||||
editor.selection = new Selection(13, 7, 14, 2); // Inside the open tag of <ul class="nav main"> and the next line
|
||||
balanceIn();
|
||||
testSelection(editor.selection, 23, 13, 2, 16); // inner contents of <ul class="nav main">
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Balance In/Out in html template', function (): any {
|
||||
const htmlTemplate = `
|
||||
<script type="text/html">
|
||||
<div class="header">
|
||||
<ul class="nav main">
|
||||
<li class="item1">Item 1</li>
|
||||
<li class="item2">Item 2</li>
|
||||
</ul>
|
||||
</div>
|
||||
</script>`;
|
||||
|
||||
return withRandomFileEditor(htmlTemplate, 'html', (editor, _) => {
|
||||
|
||||
editor.selections = [new Selection(5, 24, 5, 24)];
|
||||
let expectedBalanceOutRanges: [number, number, number, number][] = [
|
||||
[5, 20, 5, 26], // <li class="item1">``Item 2''</li>
|
||||
[5, 2, 5, 31], // ``<li class="item1">Item 2</li>''
|
||||
[3, 22, 6, 1], // inner contents of ul
|
||||
[3, 1, 6, 6], // outer contents of ul
|
||||
[2, 20, 7, 0], // inner contents of div
|
||||
[2, 0, 7, 6], // outer contents of div
|
||||
];
|
||||
expectedBalanceOutRanges.forEach(([linestart, colstart, lineend, colend]) => {
|
||||
balanceOut();
|
||||
testSelection(editor.selection, colstart, linestart, colend, lineend);
|
||||
});
|
||||
|
||||
expectedBalanceOutRanges.pop();
|
||||
expectedBalanceOutRanges.reverse().forEach(([linestart, colstart, lineend, colend]) => {
|
||||
balanceIn();
|
||||
testSelection(editor.selection, colstart, linestart, colend, lineend);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function testSelection(selection: Selection, startChar: number, startline: number, endChar?: number, endLine?: number) {
|
||||
|
||||
assert.equal(selection.anchor.line, startline);
|
||||
assert.equal(selection.anchor.character, startChar);
|
||||
if (!endLine && endLine !== 0) {
|
||||
assert.equal(selection.isSingleLine, true);
|
||||
} else {
|
||||
assert.equal(selection.active.line, endLine);
|
||||
}
|
||||
if (!endChar && endChar !== 0) {
|
||||
assert.equal(selection.isEmpty, true);
|
||||
} else {
|
||||
assert.equal(selection.active.character, endChar);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { incrementDecrement as incrementDecrementImpl } from '../incrementDecrement';
|
||||
|
||||
function incrementDecrement(delta: number): Thenable<boolean> {
|
||||
const result = incrementDecrementImpl(delta);
|
||||
assert.ok(result);
|
||||
return result!;
|
||||
}
|
||||
|
||||
suite('Tests for Increment/Decrement Emmet Commands', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
const contents = `
|
||||
hello 123.43 there
|
||||
hello 999.9 there
|
||||
hello 100 there
|
||||
`;
|
||||
|
||||
test('incrementNumberByOne', function (): any {
|
||||
return withRandomFileEditor(contents, 'txt', async (editor, doc) => {
|
||||
editor.selections = [new Selection(1, 7, 1, 10), new Selection(2, 7, 2, 10)];
|
||||
await incrementDecrement(1);
|
||||
assert.equal(doc.getText(), contents.replace('123', '124').replace('999', '1000'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('incrementNumberByTen', function (): any {
|
||||
return withRandomFileEditor(contents, 'txt', async (editor, doc) => {
|
||||
editor.selections = [new Selection(1, 7, 1, 10), new Selection(2, 7, 2, 10)];
|
||||
await incrementDecrement(10);
|
||||
assert.equal(doc.getText(), contents.replace('123', '133').replace('999', '1009'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('incrementNumberByOneTenth', function (): any {
|
||||
return withRandomFileEditor(contents, 'txt', async (editor, doc) => {
|
||||
editor.selections = [new Selection(1, 7, 1, 13), new Selection(2, 7, 2, 12)];
|
||||
await incrementDecrement(0.1);
|
||||
assert.equal(doc.getText(), contents.replace('123.43', '123.53').replace('999.9', '1000'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('decrementNumberByOne', function (): any {
|
||||
return withRandomFileEditor(contents, 'txt', async (editor, doc) => {
|
||||
editor.selections = [new Selection(1, 7, 1, 10), new Selection(3, 7, 3, 10)];
|
||||
await incrementDecrement(-1);
|
||||
assert.equal(doc.getText(), contents.replace('123', '122').replace('100', '99'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('decrementNumberByTen', function (): any {
|
||||
return withRandomFileEditor(contents, 'txt', async (editor, doc) => {
|
||||
editor.selections = [new Selection(1, 7, 1, 10), new Selection(3, 7, 3, 10)];
|
||||
await incrementDecrement(-10);
|
||||
assert.equal(doc.getText(), contents.replace('123', '113').replace('100', '90'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('decrementNumberByOneTenth', function (): any {
|
||||
return withRandomFileEditor(contents, 'txt', async (editor, doc) => {
|
||||
editor.selections = [new Selection(1, 7, 1, 13), new Selection(3, 7, 3, 10)];
|
||||
await incrementDecrement(-0.1);
|
||||
assert.equal(doc.getText(), contents.replace('123.43', '123.33').replace('100', '99.9'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
40
lib/vscode/extensions/emmet/src/test/index.ts
Normal file
40
lib/vscode/extensions/emmet/src/test/index.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const path = require('path');
|
||||
const testRunner = require('vscode/lib/testrunner');
|
||||
|
||||
const options: any = {
|
||||
ui: 'tdd',
|
||||
useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'),
|
||||
timeout: 60000
|
||||
};
|
||||
|
||||
// These integration tests is being run in multiple environments (electron, web, remote)
|
||||
// so we need to set the suite name based on the environment as the suite name is used
|
||||
// for the test results file name
|
||||
let suite = '';
|
||||
if (process.env.VSCODE_BROWSER) {
|
||||
suite = `${process.env.VSCODE_BROWSER} Browser Integration Emmet Tests`;
|
||||
} else if (process.env.REMOTE_VSCODE) {
|
||||
suite = 'Remote Integration Emmet Tests';
|
||||
} else {
|
||||
suite = 'Integration Emmet Tests';
|
||||
}
|
||||
|
||||
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
|
||||
options.reporter = 'mocha-multi-reporters';
|
||||
options.reporterOptions = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
testsuitesTitle: `${suite} ${process.platform}`,
|
||||
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
testRunner.configure(options);
|
||||
|
||||
export = testRunner;
|
@ -0,0 +1,260 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { withRandomFileEditor } from './testUtils';
|
||||
import * as vscode from 'vscode';
|
||||
import { parsePartialStylesheet, getNode } from '../util';
|
||||
import { isValidLocationForEmmetAbbreviation } from '../abbreviationActions';
|
||||
|
||||
suite('Tests for partial parse of Stylesheets', () => {
|
||||
|
||||
function isValid(doc: vscode.TextDocument, range: vscode.Range, syntax: string): boolean {
|
||||
const rootNode = parsePartialStylesheet(doc, range.end);
|
||||
const currentNode = getNode(rootNode, range.end, true);
|
||||
return isValidLocationForEmmetAbbreviation(doc, rootNode, currentNode, syntax, range.end, range);
|
||||
}
|
||||
|
||||
test('Ignore block comment inside rule', function (): any {
|
||||
const cssContents = `
|
||||
p {
|
||||
margin: p ;
|
||||
/*dn: none; p */ p
|
||||
p
|
||||
p.
|
||||
} p
|
||||
`;
|
||||
return withRandomFileEditor(cssContents, '.css', (_, doc) => {
|
||||
let rangesForEmmet = [
|
||||
new vscode.Range(3, 18, 3, 19), // Same line after block comment
|
||||
new vscode.Range(4, 1, 4, 2), // p after block comment
|
||||
new vscode.Range(5, 1, 5, 3) // p. after block comment
|
||||
];
|
||||
let rangesNotEmmet = [
|
||||
new vscode.Range(1, 0, 1, 1), // Selector
|
||||
new vscode.Range(2, 9, 2, 10), // Property value
|
||||
new vscode.Range(3, 3, 3, 5), // dn inside block comment
|
||||
new vscode.Range(3, 13, 3, 14), // p just before ending of block comment
|
||||
new vscode.Range(6, 2, 6, 3) // p after ending of block
|
||||
|
||||
];
|
||||
rangesForEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'css'), true);
|
||||
});
|
||||
rangesNotEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'css'), false);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Ignore commented braces', function (): any {
|
||||
const sassContents = `
|
||||
.foo
|
||||
// .foo { brs
|
||||
/* .foo { op.3
|
||||
dn {
|
||||
*/
|
||||
@
|
||||
} bg
|
||||
`;
|
||||
return withRandomFileEditor(sassContents, '.scss', (_, doc) => {
|
||||
let rangesNotEmmet = [
|
||||
new vscode.Range(1, 0, 1, 4), // Selector
|
||||
new vscode.Range(2, 3, 2, 7), // Line commented selector
|
||||
new vscode.Range(3, 3, 3, 7), // Block commented selector
|
||||
new vscode.Range(4, 0, 4, 2), // dn inside block comment
|
||||
new vscode.Range(6, 1, 6, 2), // @ inside a rule whose opening brace is commented
|
||||
new vscode.Range(7, 2, 7, 4) // bg after ending of badly constructed block
|
||||
];
|
||||
rangesNotEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), false);
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Block comment between selector and open brace', function (): any {
|
||||
const cssContents = `
|
||||
p
|
||||
/* First line
|
||||
of a multiline
|
||||
comment */
|
||||
{
|
||||
margin: p ;
|
||||
/*dn: none; p */ p
|
||||
p
|
||||
p.
|
||||
} p
|
||||
`;
|
||||
return withRandomFileEditor(cssContents, '.css', (_, doc) => {
|
||||
let rangesForEmmet = [
|
||||
new vscode.Range(7, 18, 7, 19), // Same line after block comment
|
||||
new vscode.Range(8, 1, 8, 2), // p after block comment
|
||||
new vscode.Range(9, 1, 9, 3) // p. after block comment
|
||||
];
|
||||
let rangesNotEmmet = [
|
||||
new vscode.Range(1, 2, 1, 3), // Selector
|
||||
new vscode.Range(3, 3, 3, 4), // Inside multiline comment
|
||||
new vscode.Range(5, 0, 5, 1), // Opening Brace
|
||||
new vscode.Range(6, 9, 6, 10), // Property value
|
||||
new vscode.Range(7, 3, 7, 5), // dn inside block comment
|
||||
new vscode.Range(7, 13, 7, 14), // p just before ending of block comment
|
||||
new vscode.Range(10, 2, 10, 3) // p after ending of block
|
||||
];
|
||||
rangesForEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'css'), true);
|
||||
});
|
||||
rangesNotEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'css'), false);
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Nested and consecutive rulesets with errors', function (): any {
|
||||
const sassContents = `
|
||||
.foo{
|
||||
a
|
||||
a
|
||||
}}{ p
|
||||
}
|
||||
.bar{
|
||||
@
|
||||
.rudi {
|
||||
@
|
||||
}
|
||||
}}}
|
||||
`;
|
||||
return withRandomFileEditor(sassContents, '.scss', (_, doc) => {
|
||||
let rangesForEmmet = [
|
||||
new vscode.Range(2, 1, 2, 2), // Inside a ruleset before errors
|
||||
new vscode.Range(3, 1, 3, 2), // Inside a ruleset after no serious error
|
||||
new vscode.Range(7, 1, 7, 2), // @ inside a so far well structured ruleset
|
||||
new vscode.Range(9, 2, 9, 3), // @ inside a so far well structured nested ruleset
|
||||
];
|
||||
let rangesNotEmmet = [
|
||||
new vscode.Range(4, 4, 4, 5), // p inside ruleset without proper selector
|
||||
new vscode.Range(6, 3, 6, 4) // In selector
|
||||
];
|
||||
rangesForEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), true);
|
||||
});
|
||||
rangesNotEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), false);
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('One liner sass', function (): any {
|
||||
const sassContents = `
|
||||
.foo{dn}.bar{.boo{dn}dn}.comd{/*{dn*/p{div{dn}} }.foo{.other{dn}} dn
|
||||
`;
|
||||
return withRandomFileEditor(sassContents, '.scss', (_, doc) => {
|
||||
let rangesForEmmet = [
|
||||
new vscode.Range(1, 5, 1, 7), // Inside a ruleset
|
||||
new vscode.Range(1, 18, 1, 20), // Inside a nested ruleset
|
||||
new vscode.Range(1, 21, 1, 23), // Inside ruleset after nested one.
|
||||
new vscode.Range(1, 43, 1, 45), // Inside nested ruleset after comment
|
||||
new vscode.Range(1, 61, 1, 63) // Inside nested ruleset
|
||||
];
|
||||
let rangesNotEmmet = [
|
||||
new vscode.Range(1, 3, 1, 4), // In foo selector
|
||||
new vscode.Range(1, 10, 1, 11), // In bar selector
|
||||
new vscode.Range(1, 15, 1, 16), // In boo selector
|
||||
new vscode.Range(1, 28, 1, 29), // In comd selector
|
||||
new vscode.Range(1, 33, 1, 34), // In commented dn
|
||||
new vscode.Range(1, 37, 1, 38), // In p selector
|
||||
new vscode.Range(1, 39, 1, 42), // In div selector
|
||||
new vscode.Range(1, 66, 1, 68) // Outside any ruleset
|
||||
];
|
||||
rangesForEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), true);
|
||||
});
|
||||
rangesNotEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), false);
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Variables and interpolation', function (): any {
|
||||
const sassContents = `
|
||||
p.#{dn} {
|
||||
p.3
|
||||
#{$attr}-color: blue;
|
||||
dn
|
||||
} op
|
||||
.foo{nes{ted}} {
|
||||
dn
|
||||
}
|
||||
`;
|
||||
return withRandomFileEditor(sassContents, '.scss', (_, doc) => {
|
||||
let rangesForEmmet = [
|
||||
new vscode.Range(2, 1, 2, 4), // p.3 inside a ruleset whose selector uses interpolation
|
||||
new vscode.Range(4, 1, 4, 3) // dn inside ruleset after property with variable
|
||||
];
|
||||
let rangesNotEmmet = [
|
||||
new vscode.Range(1, 0, 1, 1), // In p in selector
|
||||
new vscode.Range(1, 2, 1, 3), // In # in selector
|
||||
new vscode.Range(1, 4, 1, 6), // In dn inside variable in selector
|
||||
new vscode.Range(3, 7, 3, 8), // r of attr inside variable
|
||||
new vscode.Range(5, 2, 5, 4), // op after ruleset
|
||||
new vscode.Range(7, 1, 7, 3), // dn inside ruleset whose selector uses nested interpolation
|
||||
new vscode.Range(3, 1, 3, 2), // # inside ruleset
|
||||
];
|
||||
rangesForEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), true);
|
||||
});
|
||||
rangesNotEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), false);
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Comments in sass', function (): any {
|
||||
const sassContents = `
|
||||
.foo{
|
||||
/* p // p */ brs6-2p
|
||||
dn
|
||||
}
|
||||
p
|
||||
/* c
|
||||
om
|
||||
ment */{
|
||||
m10
|
||||
}
|
||||
.boo{
|
||||
op.3
|
||||
}
|
||||
`;
|
||||
return withRandomFileEditor(sassContents, '.scss', (_, doc) => {
|
||||
let rangesForEmmet = [
|
||||
new vscode.Range(2, 14, 2, 21), // brs6-2p with a block commented line comment ('/* */' overrides '//')
|
||||
new vscode.Range(3, 1, 3, 3), // dn after a line with combined comments inside a ruleset
|
||||
new vscode.Range(9, 1, 9, 4), // m10 inside ruleset whose selector is before a comment
|
||||
new vscode.Range(12, 1, 12, 5) // op3 inside a ruleset with commented extra braces
|
||||
];
|
||||
let rangesNotEmmet = [
|
||||
new vscode.Range(2, 4, 2, 5), // In p inside block comment
|
||||
new vscode.Range(2, 9, 2, 10), // In p inside block comment and after line comment
|
||||
new vscode.Range(6, 3, 6, 4) // In c inside block comment
|
||||
];
|
||||
rangesForEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), true);
|
||||
});
|
||||
rangesNotEmmet.forEach(range => {
|
||||
assert.equal(isValid(doc, range, 'scss'), false);
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
89
lib/vscode/extensions/emmet/src/test/reflectCssValue.test.ts
Normal file
89
lib/vscode/extensions/emmet/src/test/reflectCssValue.test.ts
Normal file
@ -0,0 +1,89 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { reflectCssValue as reflectCssValueImpl } from '../reflectCssValue';
|
||||
|
||||
function reflectCssValue(): Thenable<boolean> {
|
||||
const result = reflectCssValueImpl();
|
||||
assert.ok(result);
|
||||
return result!;
|
||||
}
|
||||
|
||||
suite('Tests for Emmet: Reflect CSS Value command', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
const cssContents = `
|
||||
.header {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
transform: rotate(50deg);
|
||||
-moz-transform: rotate(20deg);
|
||||
-o-transform: rotate(50deg);
|
||||
-webkit-transform: rotate(50deg);
|
||||
-ms-transform: rotate(50deg);
|
||||
}
|
||||
`;
|
||||
|
||||
const htmlContents = `
|
||||
<html>
|
||||
<style>
|
||||
.header {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
transform: rotate(50deg);
|
||||
-moz-transform: rotate(20deg);
|
||||
-o-transform: rotate(50deg);
|
||||
-webkit-transform: rotate(50deg);
|
||||
-ms-transform: rotate(50deg);
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
`;
|
||||
|
||||
test('Reflect Css Value in css file', function (): any {
|
||||
return withRandomFileEditor(cssContents, '.css', (editor, doc) => {
|
||||
editor.selections = [new Selection(5, 10, 5, 10)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), cssContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in css file, selecting entire property', function (): any {
|
||||
return withRandomFileEditor(cssContents, '.css', (editor, doc) => {
|
||||
editor.selections = [new Selection(5, 2, 5, 32)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), cssContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in html file', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
|
||||
editor.selections = [new Selection(7, 20, 7, 20)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), htmlContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in html file, selecting entire property', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
|
||||
editor.selections = [new Selection(7, 4, 7, 34)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), htmlContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
404
lib/vscode/extensions/emmet/src/test/tagActions.test.ts
Normal file
404
lib/vscode/extensions/emmet/src/test/tagActions.test.ts
Normal file
@ -0,0 +1,404 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection, workspace, ConfigurationTarget } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { removeTag } from '../removeTag';
|
||||
import { updateTag } from '../updateTag';
|
||||
import { matchTag } from '../matchTag';
|
||||
import { splitJoinTag } from '../splitJoinTag';
|
||||
import { mergeLines } from '../mergeLines';
|
||||
|
||||
suite('Tests for Emmet actions on html tags', () => {
|
||||
teardown(() => {
|
||||
// close all editors
|
||||
return closeAllEditors;
|
||||
});
|
||||
|
||||
const contents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let contentsWithTemplate = `
|
||||
<script type="text/template">
|
||||
<ul>
|
||||
<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span/>
|
||||
</script>
|
||||
`;
|
||||
|
||||
test('update tag with multiple cursors', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><section>Hello</section></li>
|
||||
<section><span>There</span></section>
|
||||
<section><li><span>Bye</span></li></section>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 17, 3, 17), // cursor inside tags
|
||||
new Selection(4, 5, 4, 5), // cursor inside opening tag
|
||||
new Selection(5, 35, 5, 35), // cursor inside closing tag
|
||||
];
|
||||
|
||||
return updateTag('section')!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// #region update tag
|
||||
test('update tag with entire node selected', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><section>Hello</section></li>
|
||||
<li><span>There</span></li>
|
||||
<section><li><span>Bye</span></li></section>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25),
|
||||
new Selection(5, 3, 5, 39),
|
||||
];
|
||||
|
||||
return updateTag('section')!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('update tag with template', () => {
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
<section>
|
||||
<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</section>
|
||||
<span/>
|
||||
</script>
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(contentsWithTemplate, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 4, 2, 4), // cursor inside ul tag
|
||||
];
|
||||
|
||||
return updateTag('section')!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region remove tag
|
||||
test('remove tag with mutliple cursors', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li>Hello</li>
|
||||
<span>There</span>
|
||||
<li><span>Bye</span></li>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 17, 3, 17), // cursor inside tags
|
||||
new Selection(4, 5, 4, 5), // cursor inside opening tag
|
||||
new Selection(5, 35, 5, 35), // cursor inside closing tag
|
||||
];
|
||||
|
||||
return removeTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('remove tag with boundary conditions', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li>Hello</li>
|
||||
<li><span>There</span></li>
|
||||
<li><span>Bye</span></li>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25),
|
||||
new Selection(5, 3, 5, 39),
|
||||
];
|
||||
|
||||
return removeTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('remove tag with template', () => {
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
\t\t
|
||||
<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
\t
|
||||
<span/>
|
||||
</script>
|
||||
`;
|
||||
return withRandomFileEditor(contentsWithTemplate, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 4, 2, 4), // cursor inside ul tag
|
||||
];
|
||||
|
||||
return removeTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region split/join tag
|
||||
test('split/join tag with mutliple cursors', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><span/></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span></span>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 17, 3, 17), // join tag
|
||||
new Selection(7, 5, 7, 5), // split tag
|
||||
];
|
||||
|
||||
return splitJoinTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('split/join tag with boundary selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><span/></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span></span>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25), // join tag
|
||||
new Selection(7, 2, 7, 9), // split tag
|
||||
];
|
||||
|
||||
return splitJoinTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('split/join tag with templates', () => {
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
<ul>
|
||||
<li><span/></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span></span>
|
||||
</script>
|
||||
`;
|
||||
return withRandomFileEditor(contentsWithTemplate, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 17, 3, 17), // join tag
|
||||
new Selection(7, 5, 7, 5), // split tag
|
||||
];
|
||||
|
||||
return splitJoinTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('split/join tag in jsx with xhtml self closing tag', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><span /></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span></span>
|
||||
</div>
|
||||
`;
|
||||
const oldValueForSyntaxProfiles = workspace.getConfiguration('emmet').inspect('syntaxProfiles');
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles', { jsx: { selfClosingStyle: 'xhtml' } }, ConfigurationTarget.Global).then(() => {
|
||||
return withRandomFileEditor(contents, 'jsx', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 17, 3, 17), // join tag
|
||||
new Selection(7, 5, 7, 5), // split tag
|
||||
];
|
||||
|
||||
return splitJoinTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles', oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region match tag
|
||||
test('match tag with mutliple cursors', () => {
|
||||
return withRandomFileEditor(contents, 'html', (editor, _) => {
|
||||
editor.selections = [
|
||||
new Selection(1, 0, 1, 0), // just before tag starts, i.e before <
|
||||
new Selection(1, 1, 1, 1), // just before tag name starts
|
||||
new Selection(1, 2, 1, 2), // inside tag name
|
||||
new Selection(1, 6, 1, 6), // after tag name but before opening tag ends
|
||||
new Selection(1, 18, 1, 18), // just before opening tag ends
|
||||
new Selection(1, 19, 1, 19), // just after opening tag ends
|
||||
];
|
||||
|
||||
matchTag();
|
||||
|
||||
editor.selections.forEach(selection => {
|
||||
assert.equal(selection.active.line, 8);
|
||||
assert.equal(selection.active.character, 3);
|
||||
assert.equal(selection.anchor.line, 8);
|
||||
assert.equal(selection.anchor.character, 3);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('match tag with template scripts', () => {
|
||||
let templateScript = `
|
||||
<script type="text/template">
|
||||
<div>
|
||||
Hello
|
||||
</div>
|
||||
</script>`;
|
||||
|
||||
return withRandomFileEditor(templateScript, 'html', (editor, _) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 2, 2, 2), // just before div tag starts, i.e before <
|
||||
];
|
||||
|
||||
matchTag();
|
||||
|
||||
editor.selections.forEach(selection => {
|
||||
assert.equal(selection.active.line, 4);
|
||||
assert.equal(selection.active.character, 4);
|
||||
assert.equal(selection.anchor.line, 4);
|
||||
assert.equal(selection.anchor.character, 4);
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region merge lines
|
||||
test('merge lines of tag with children when empty selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul><li><span>Hello</span></li><li><span>There</span></li><div><li><span>Bye</span></li></div></ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 3, 2, 3)
|
||||
];
|
||||
|
||||
return mergeLines()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('merge lines of tag with children when full node selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul><li><span>Hello</span></li><li><span>There</span></li><div><li><span>Bye</span></li></div></ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 3, 6, 7)
|
||||
];
|
||||
|
||||
return mergeLines()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('merge lines is no-op when start and end nodes are on the same line', () => {
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 9, 3, 9), // cursor is inside the <span> in <li><span>Hello</span></li>
|
||||
new Selection(4, 5, 4, 5), // cursor is inside the <li> in <li><span>Hello</span></li>
|
||||
new Selection(5, 5, 5, 20) // selection spans multiple nodes in the same line
|
||||
];
|
||||
|
||||
return mergeLines()!.then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
});
|
||||
|
@ -0,0 +1 @@
|
||||
DO NOT DELETE, USED BY INTEGRATION TESTS
|
70
lib/vscode/extensions/emmet/src/test/testUtils.ts
Normal file
70
lib/vscode/extensions/emmet/src/test/testUtils.ts
Normal file
@ -0,0 +1,70 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { join } from 'path';
|
||||
|
||||
function rndName() {
|
||||
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10);
|
||||
}
|
||||
|
||||
export function createRandomFile(contents = '', fileExtension = 'txt'): Thenable<vscode.Uri> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const tmpFile = join(os.tmpdir(), rndName() + '.' + fileExtension);
|
||||
fs.writeFile(tmpFile, contents, (error) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve(vscode.Uri.file(tmpFile));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function pathEquals(path1: string, path2: string): boolean {
|
||||
if (process.platform !== 'linux') {
|
||||
path1 = path1.toLowerCase();
|
||||
path2 = path2.toLowerCase();
|
||||
}
|
||||
|
||||
return path1 === path2;
|
||||
}
|
||||
|
||||
export function deleteFile(file: vscode.Uri): Thenable<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.unlink(file.fsPath, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function closeAllEditors(): Thenable<any> {
|
||||
return vscode.commands.executeCommand('workbench.action.closeAllEditors');
|
||||
|
||||
}
|
||||
|
||||
export function withRandomFileEditor(initialContents: string, fileExtension: string = 'txt', run: (editor: vscode.TextEditor, doc: vscode.TextDocument) => Thenable<void>): Thenable<boolean> {
|
||||
return createRandomFile(initialContents, fileExtension).then(file => {
|
||||
return vscode.workspace.openTextDocument(file).then(doc => {
|
||||
return vscode.window.showTextDocument(doc).then((editor) => {
|
||||
return run(editor, doc).then(_ => {
|
||||
if (doc.isDirty) {
|
||||
return doc.save().then(() => {
|
||||
return deleteFile(file);
|
||||
});
|
||||
} else {
|
||||
return deleteFile(file);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
729
lib/vscode/extensions/emmet/src/test/toggleComment.test.ts
Normal file
729
lib/vscode/extensions/emmet/src/test/toggleComment.test.ts
Normal file
@ -0,0 +1,729 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { toggleComment as toggleCommentImpl } from '../toggleComment';
|
||||
|
||||
function toggleComment(): Thenable<boolean> {
|
||||
const result = toggleCommentImpl();
|
||||
assert.ok(result);
|
||||
return result!;
|
||||
}
|
||||
|
||||
suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
const contents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<ul>
|
||||
<!--<li>Previously Commented Node</li>-->
|
||||
<li>Another Node</li>
|
||||
</ul>
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
.hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
|
||||
test('toggle comment with multiple cursors, but no selection (HTML)', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><!--<span>Hello</span>--></li>
|
||||
<!--<li><span>There</span></li>-->
|
||||
<!--<div><li><span>Bye</span></li></div>-->
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
/*margin: 10px;*/
|
||||
padding: 20px;
|
||||
}
|
||||
/*.hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}*/
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 17, 3, 17), // cursor inside the inner span element
|
||||
new Selection(4, 5, 4, 5), // cursor inside opening tag
|
||||
new Selection(5, 35, 5, 35), // cursor inside closing tag
|
||||
new Selection(7, 3, 7, 3), // cursor inside open tag of <ul> one of whose children is already commented
|
||||
new Selection(14, 8, 14, 8), // cursor inside the css property inside the style tag
|
||||
new Selection(18, 3, 18, 3) // cursor inside the css rule inside the style tag
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment with multiple cursors and whole node selected (HTML)', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><!--<span>Hello</span>--></li>
|
||||
<!--<li><span>There</span></li>-->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
/*margin: 10px;*/
|
||||
padding: 20px;
|
||||
}
|
||||
/*.hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}*/
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25), // <span>Hello</span><
|
||||
new Selection(4, 3, 4, 30), // <li><span>There</span></li>
|
||||
new Selection(7, 2, 10, 7), // The <ul> one of whose children is already commented
|
||||
new Selection(14, 4, 14, 17), // css property inside the style tag
|
||||
new Selection(17, 3, 20, 4) // the css rule inside the style tag
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when multiple nodes are completely under single selection (HTML)', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<!--<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>-->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<ul>
|
||||
<!--<li>Previously Commented Node</li>-->
|
||||
<li>Another Node</li>
|
||||
</ul>
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
/*margin: 10px;
|
||||
padding: 20px;*/
|
||||
}
|
||||
.hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 4, 4, 30),
|
||||
new Selection(14, 4, 15, 18) // 2 css properties inside the style tag
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when multiple nodes are partially under single selection (HTML)', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<!--<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>-->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
.hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 24, 4, 20),
|
||||
new Selection(7, 2, 9, 10) // The <ul> one of whose children is already commented
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment with multiple cursors selecting parent and child nodes', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><!--<span>Hello</span>--></li>
|
||||
<!--<li><span>There</span></li>-->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
<span/>
|
||||
<!--<style>
|
||||
.boo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
.hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>-->
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 17, 3, 17), // cursor inside the inner span element
|
||||
new Selection(4, 5, 4, 5), // two cursors: one inside opening tag
|
||||
new Selection(4, 17, 4, 17), // and the second inside the inner span element
|
||||
new Selection(7, 3, 7, 3), // two cursors: one inside open tag of <ul> one of whose children is already commented
|
||||
new Selection(9, 10, 9, 10), // and the second inside inner li element, whose parent is selected
|
||||
new Selection(12, 3, 12, 3), // four nested cursors: one inside the style open tag
|
||||
new Selection(14, 8, 14, 8), // the second inside the css property inside the style tag
|
||||
new Selection(18, 3, 18, 3), // the third inside the css rule inside the style tag
|
||||
new Selection(19, 8, 19, 8) // and the fourth inside the css property inside the style tag
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment within script template', () => {
|
||||
const templateContents = `
|
||||
<script type="text/template">
|
||||
<li><span>Hello</span></li>
|
||||
<li><!--<span>There</span>--></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
<span/>
|
||||
</script>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
<!--<li><span>Hello</span></li>-->
|
||||
<li><span>There</span></li>
|
||||
<div><li><!--<span>Bye</span>--></li></div>
|
||||
<span/>
|
||||
</script>
|
||||
`;
|
||||
return withRandomFileEditor(templateContents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 2, 2, 28), // select entire li element
|
||||
new Selection(3, 17, 3, 17), // cursor inside the commented span
|
||||
new Selection(4, 18, 4, 18), // cursor inside the noncommented span
|
||||
];
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
const contents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
|
||||
test('toggle comment with multiple cursors, but no selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*margin: 10px;*/
|
||||
padding: 10px;
|
||||
}
|
||||
/*.two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}*/
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 5, 2, 5), // cursor inside a property
|
||||
new Selection(5, 4, 5, 4), // cursor inside selector
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment with multiple cursors and whole node selected (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*margin: 10px;*/
|
||||
/*padding: 10px;*/
|
||||
}
|
||||
/*.two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}*/
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 2, 2, 15), // A property completely selected
|
||||
new Selection(3, 0, 3, 16), // A property completely selected along with whitespace
|
||||
new Selection(5, 1, 8, 2), // A rule completely selected
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
//return toggleComment().then(() => {
|
||||
//assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
//});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
test('toggle comment when multiple nodes of same parent are completely under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/* margin: 10px;
|
||||
padding: 10px;*/
|
||||
}
|
||||
/*.two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
}*/`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 0, 3, 16), // 2 properties completely under a single selection along with whitespace
|
||||
new Selection(5, 1, 11, 2), // 2 rules completely under a single selection
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when start and end of selection is inside properties of separate rules (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 6, 6)
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when selection spans properties of separate rules, with start in whitespace and end inside the property (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 0, 6, 6)
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when selection spans properties of separate rules, with end in whitespace and start inside the property (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 7, 0)
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when selection spans properties of separate rules, with both start and end in whitespace (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 0, 7, 0)
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when multiple nodes of same parent are partially under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*margin: 10px;
|
||||
padding: 10px;*/
|
||||
}
|
||||
/*.two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
*/ }`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 7, 3, 10), // 2 properties partially under a single selection
|
||||
new Selection(5, 2, 11, 0), // 2 rules partially under a single selection
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
const contents = `
|
||||
.one {
|
||||
height: 42px;
|
||||
|
||||
.two {
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.three {
|
||||
padding: 10px;
|
||||
}
|
||||
}`;
|
||||
|
||||
test('toggle comment with multiple cursors selecting nested nodes (SCSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;*/
|
||||
|
||||
/*.two {
|
||||
width: 42px;
|
||||
}*/
|
||||
|
||||
.three {
|
||||
/*padding: 10px;*/
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 5, 2, 5), // cursor inside a property
|
||||
new Selection(4, 4, 4, 4), // two cursors: one inside a nested rule
|
||||
new Selection(5, 5, 5, 5), // and the second one inside a nested property
|
||||
new Selection(9, 5, 9, 5) // cursor inside a property inside a nested rule
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
test('toggle comment with multiple cursors selecting several nested nodes (SCSS)', () => {
|
||||
const expectedContents = `
|
||||
/*.one {
|
||||
height: 42px;
|
||||
|
||||
.two {
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.three {
|
||||
padding: 10px;
|
||||
}
|
||||
}*/`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(1, 3, 1, 3), // cursor in the outside rule. And several cursors inside:
|
||||
new Selection(2, 5, 2, 5), // cursor inside a property
|
||||
new Selection(4, 4, 4, 4), // two cursors: one inside a nested rule
|
||||
new Selection(5, 5, 5, 5), // and the second one inside a nested property
|
||||
new Selection(9, 5, 9, 5) // cursor inside a property inside a nested rule
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment with multiple cursors, but no selection (SCSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;*/
|
||||
|
||||
/*.two {
|
||||
width: 42px;
|
||||
}*/
|
||||
|
||||
.three {
|
||||
/*padding: 10px;*/
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 5, 2, 5), // cursor inside a property
|
||||
new Selection(4, 4, 4, 4), // cursor inside a nested rule
|
||||
new Selection(9, 5, 9, 5) // cursor inside a property inside a nested rule
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
//return toggleComment().then(() => {
|
||||
// assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
//});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment with multiple cursors and whole node selected (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;*/
|
||||
|
||||
/*.two {
|
||||
width: 42px;
|
||||
}*/
|
||||
|
||||
.three {
|
||||
/*padding: 10px;*/
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 2, 2, 15), // A property completely selected
|
||||
new Selection(4, 2, 6, 3), // A rule completely selected
|
||||
new Selection(9, 3, 9, 17) // A property inside a nested rule completely selected
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
test('toggle comment when multiple nodes are completely under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;
|
||||
|
||||
.two {
|
||||
width: 42px;
|
||||
}*/
|
||||
|
||||
.three {
|
||||
padding: 10px;
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 2, 6, 3), // A properties and a nested rule completely under a single selection
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('toggle comment when multiple nodes are partially under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;
|
||||
|
||||
.two {
|
||||
width: 42px;
|
||||
*/ }
|
||||
|
||||
.three {
|
||||
padding: 10px;
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 6, 6, 1), // A properties and a nested rule partially under a single selection
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
150
lib/vscode/extensions/emmet/src/test/updateImageSize.test.ts
Normal file
150
lib/vscode/extensions/emmet/src/test/updateImageSize.test.ts
Normal file
@ -0,0 +1,150 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// import 'mocha';
|
||||
// import * as assert from 'assert';
|
||||
// import { Selection } from 'vscode';
|
||||
// import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
// import { updateImageSize } from '../updateImageSize';
|
||||
|
||||
// suite('Tests for Emmet actions on html tags', () => {
|
||||
// teardown(closeAllEditors);
|
||||
|
||||
// test('update image css with multiple cursors in css file', () => {
|
||||
// const cssContents = `
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 42px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 42px;
|
||||
// }
|
||||
// `;
|
||||
// const expectedContents = `
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 32px;
|
||||
// width: 32px;
|
||||
// }
|
||||
// `;
|
||||
// return withRandomFileEditor(cssContents, 'css', (editor, doc) => {
|
||||
// editor.selections = [
|
||||
// new Selection(4, 50, 4, 50),
|
||||
// new Selection(7, 50, 7, 50),
|
||||
// new Selection(11, 50, 11, 50)
|
||||
// ];
|
||||
|
||||
// return updateImageSize()!.then(() => {
|
||||
// assert.equal(doc.getText(), expectedContents);
|
||||
// return Promise.resolve();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// test('update image size in css in html file with multiple cursors', () => {
|
||||
// const htmlWithCssContents = `
|
||||
// <html>
|
||||
// <style>
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 42px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 42px;
|
||||
// }
|
||||
// </style>
|
||||
// </html>
|
||||
// `;
|
||||
// const expectedContents = `
|
||||
// <html>
|
||||
// <style>
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 32px;
|
||||
// width: 32px;
|
||||
// }
|
||||
// </style>
|
||||
// </html>
|
||||
// `;
|
||||
// return withRandomFileEditor(htmlWithCssContents, 'html', (editor, doc) => {
|
||||
// editor.selections = [
|
||||
// new Selection(6, 50, 6, 50),
|
||||
// new Selection(9, 50, 9, 50),
|
||||
// new Selection(13, 50, 13, 50)
|
||||
// ];
|
||||
|
||||
// return updateImageSize()!.then(() => {
|
||||
// assert.equal(doc.getText(), expectedContents);
|
||||
// return Promise.resolve();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// test('update image size in img tag in html file with multiple cursors', () => {
|
||||
// const htmlwithimgtag = `
|
||||
// <html>
|
||||
// <img id="one" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" />
|
||||
// <img id="two" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="56" />
|
||||
// <img id="three" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" height="56" />
|
||||
// </html>
|
||||
// `;
|
||||
// const expectedContents = `
|
||||
// <html>
|
||||
// <img id="one" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="32" height="32" />
|
||||
// <img id="two" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="32" height="32" />
|
||||
// <img id="three" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" height="32" width="32" />
|
||||
// </html>
|
||||
// `;
|
||||
// return withRandomFileEditor(htmlwithimgtag, 'html', (editor, doc) => {
|
||||
// editor.selections = [
|
||||
// new Selection(2, 50, 2, 50),
|
||||
// new Selection(3, 50, 3, 50),
|
||||
// new Selection(4, 50, 4, 50)
|
||||
// ];
|
||||
|
||||
// return updateImageSize()!.then(() => {
|
||||
// assert.equal(doc.getText(), expectedContents);
|
||||
// return Promise.resolve();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// });
|
@ -0,0 +1,351 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection, workspace, ConfigurationTarget } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { wrapWithAbbreviation, wrapIndividualLinesWithAbbreviation } from '../abbreviationActions';
|
||||
|
||||
const htmlContentsForWrapTests = `
|
||||
<ul class="nav main">
|
||||
<li class="item1">img</li>
|
||||
<li class="item2">$hithere</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapBlockElementExpected = `
|
||||
<ul class="nav main">
|
||||
<div>
|
||||
<li class="item1">img</li>
|
||||
</div>
|
||||
<div>
|
||||
<li class="item2">$hithere</li>
|
||||
</div>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapInlineElementExpected = `
|
||||
<ul class="nav main">
|
||||
<span><li class="item1">img</li></span>
|
||||
<span><li class="item2">$hithere</li></span>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapSnippetExpected = `
|
||||
<ul class="nav main">
|
||||
<a href=""><li class="item1">img</li></a>
|
||||
<a href=""><li class="item2">$hithere</li></a>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapMultiLineAbbrExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li>
|
||||
<li class="item1">img</li>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<li class="item2">$hithere</li>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapInlineElementExpectedFormatFalse = `
|
||||
<ul class="nav main">
|
||||
<h1><li class="item1">img</li></h1>
|
||||
<h1><li class="item2">$hithere</li></h1>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
suite('Tests for Wrap with Abbreviations', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
const multiCursors = [new Selection(2, 6, 2, 6), new Selection(3, 6, 3, 6)];
|
||||
const multiCursorsWithSelection = [new Selection(2, 2, 2, 28), new Selection(3, 2, 3, 33)];
|
||||
const multiCursorsWithFullLineSelection = [new Selection(2, 0, 2, 28), new Selection(3, 0, 4, 0)];
|
||||
|
||||
const oldValueForSyntaxProfiles = workspace.getConfiguration('emmet').inspect('syntaxProfile');
|
||||
|
||||
test('Wrap with block element using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'div', wrapBlockElementExpected);
|
||||
});
|
||||
|
||||
test('Wrap with inline element using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'span', wrapInlineElementExpected);
|
||||
});
|
||||
|
||||
test('Wrap with snippet using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'a', wrapSnippetExpected);
|
||||
});
|
||||
|
||||
test('Wrap with multi line abbreviation using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'ul>li', wrapMultiLineAbbrExpected);
|
||||
});
|
||||
|
||||
test('Wrap with block element using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'div', wrapBlockElementExpected);
|
||||
});
|
||||
|
||||
test('Wrap with inline element using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'span', wrapInlineElementExpected);
|
||||
});
|
||||
|
||||
test('Wrap with snippet using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'a', wrapSnippetExpected);
|
||||
});
|
||||
|
||||
test('Wrap with multi line abbreviation using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'ul>li', wrapMultiLineAbbrExpected);
|
||||
});
|
||||
|
||||
test('Wrap with block element using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'div', wrapBlockElementExpected);
|
||||
});
|
||||
|
||||
test('Wrap with inline element using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'span', wrapInlineElementExpected);
|
||||
});
|
||||
|
||||
test('Wrap with snippet using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'a', wrapSnippetExpected);
|
||||
});
|
||||
|
||||
test('Wrap with multi line abbreviation using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'ul>li', wrapMultiLineAbbrExpected);
|
||||
});
|
||||
|
||||
test('Wrap with abbreviation and comment filter', () => {
|
||||
const contents = `
|
||||
<ul class="nav main">
|
||||
line
|
||||
</ul>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<ul class="nav main">
|
||||
<li class="hello">
|
||||
line
|
||||
</li>
|
||||
<!-- /.hello -->
|
||||
</ul>
|
||||
`;
|
||||
return testWrapWithAbbreviation([new Selection(2, 0, 2, 0)], 'li.hello|c', expectedContents, contents);
|
||||
});
|
||||
|
||||
test('Wrap with abbreviation entire node when cursor is on opening tag', () => {
|
||||
const contents = `
|
||||
<div class="nav main">
|
||||
hello
|
||||
</div>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<div>
|
||||
<div class="nav main">
|
||||
hello
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
return testWrapWithAbbreviation([new Selection(1, 1, 1, 1)], 'div', expectedContents, contents);
|
||||
});
|
||||
|
||||
test('Wrap with abbreviation entire node when cursor is on closing tag', () => {
|
||||
const contents = `
|
||||
<div class="nav main">
|
||||
hello
|
||||
</div>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<div>
|
||||
<div class="nav main">
|
||||
hello
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
return testWrapWithAbbreviation([new Selection(3, 1, 3, 1)], 'div', expectedContents, contents);
|
||||
});
|
||||
|
||||
test('Wrap with multiline abbreviation doesnt add extra spaces', () => {
|
||||
// Issue #29898
|
||||
const contents = `
|
||||
hello
|
||||
`;
|
||||
const expectedContents = `
|
||||
<ul>
|
||||
<li><a href="">hello</a></li>
|
||||
</ul>
|
||||
`;
|
||||
return testWrapWithAbbreviation([new Selection(1, 2, 1, 2)], 'ul>li>a', expectedContents, contents);
|
||||
});
|
||||
|
||||
test('Wrap individual lines with abbreviation', () => {
|
||||
const contents = `
|
||||
<ul class="nav main">
|
||||
<li class="item1">This $10 is not a tabstop</li>
|
||||
<li class="item2">hi.there</li>
|
||||
</ul>
|
||||
`;
|
||||
const wrapIndividualLinesExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li class="hello1"><li class="item1">This $10 is not a tabstop</li></li>
|
||||
<li class="hello2"><li class="item2">hi.there</li></li>
|
||||
</ul>
|
||||
</ul>
|
||||
`;
|
||||
return testWrapIndividualLinesWithAbbreviation([new Selection(2, 2, 3, 33)], 'ul>li.hello$*', wrapIndividualLinesExpected, contents);
|
||||
});
|
||||
|
||||
test('Wrap individual lines with abbreviation with extra space selected', () => {
|
||||
const contents = `
|
||||
<ul class="nav main">
|
||||
<li class="item1">img</li>
|
||||
<li class="item2">hi.there</li>
|
||||
</ul>
|
||||
`;
|
||||
const wrapIndividualLinesExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li class="hello1"><li class="item1">img</li></li>
|
||||
<li class="hello2"><li class="item2">hi.there</li></li>
|
||||
</ul>
|
||||
</ul>
|
||||
`;
|
||||
return testWrapIndividualLinesWithAbbreviation([new Selection(2, 1, 4, 0)], 'ul>li.hello$*', wrapIndividualLinesExpected, contents);
|
||||
});
|
||||
|
||||
test('Wrap individual lines with abbreviation with comment filter', () => {
|
||||
const contents = `
|
||||
<ul class="nav main">
|
||||
<li class="item1">img</li>
|
||||
<li class="item2">hi.there</li>
|
||||
</ul>
|
||||
`;
|
||||
const wrapIndividualLinesExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li class="hello"><li class="item1">img</li></li>
|
||||
<!-- /.hello -->
|
||||
<li class="hello"><li class="item2">hi.there</li></li>
|
||||
<!-- /.hello -->
|
||||
</ul>
|
||||
</ul>
|
||||
`;
|
||||
return testWrapIndividualLinesWithAbbreviation([new Selection(2, 2, 3, 33)], 'ul>li.hello*|c', wrapIndividualLinesExpected, contents);
|
||||
});
|
||||
|
||||
test('Wrap individual lines with abbreviation and trim', () => {
|
||||
const contents = `
|
||||
<ul class="nav main">
|
||||
• lorem ipsum
|
||||
• lorem ipsum
|
||||
</ul>
|
||||
`;
|
||||
const wrapIndividualLinesExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li class="hello1">lorem ipsum</li>
|
||||
<li class="hello2">lorem ipsum</li>
|
||||
</ul>
|
||||
</ul>
|
||||
`;
|
||||
return testWrapIndividualLinesWithAbbreviation([new Selection(2, 3, 3, 16)], 'ul>li.hello$*|t', wrapIndividualLinesExpected, contents);
|
||||
});
|
||||
|
||||
test('Wrap with abbreviation and format set to false', () => {
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles',{ 'html' : { 'format': false } } , ConfigurationTarget.Global).then(() => {
|
||||
return testWrapWithAbbreviation(multiCursors,'h1',wrapInlineElementExpectedFormatFalse).then(() => {
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles',oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Wrap multi line selections with abbreviation', () => {
|
||||
const htmlContentsForWrapMultiLineTests = `
|
||||
<ul class="nav main">
|
||||
line1
|
||||
line2
|
||||
|
||||
line3
|
||||
line4
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapMultiLineExpected = `
|
||||
<ul class="nav main">
|
||||
<div>
|
||||
line1
|
||||
line2
|
||||
</div>
|
||||
|
||||
<div>
|
||||
line3
|
||||
line4
|
||||
</div>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
return testWrapWithAbbreviation([new Selection(2, 4, 3, 9), new Selection(5, 4, 6, 9)], 'div', wrapMultiLineExpected, htmlContentsForWrapMultiLineTests);
|
||||
});
|
||||
|
||||
test('Wrap multiline with abbreviation uses className for jsx files', () => {
|
||||
const wrapMultiLineJsxExpected = `
|
||||
<ul class="nav main">
|
||||
<div className="hello">
|
||||
<li class="item1">img</li>
|
||||
<li class="item2">$hithere</li>
|
||||
</div>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
return testWrapWithAbbreviation([new Selection(2,2,3,33)], '.hello', wrapMultiLineJsxExpected, htmlContentsForWrapTests, 'jsx');
|
||||
});
|
||||
|
||||
test('Wrap individual line with abbreviation uses className for jsx files', () => {
|
||||
const wrapIndividualLinesJsxExpected = `
|
||||
<ul class="nav main">
|
||||
<div className="hello1"><li class="item1">img</li></div>
|
||||
<div className="hello2"><li class="item2">$hithere</li></div>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
return testWrapIndividualLinesWithAbbreviation([new Selection(2,2,3,33)], '.hello$*', wrapIndividualLinesJsxExpected, htmlContentsForWrapTests, 'jsx');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests, fileExtension: string = 'html'): Thenable<any> {
|
||||
return withRandomFileEditor(input, fileExtension, (editor, _) => {
|
||||
editor.selections = selections;
|
||||
const promise = wrapWithAbbreviation({ abbreviation });
|
||||
if (!promise) {
|
||||
assert.equal(1, 2, 'Wrap with Abbreviation returned undefined.');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
assert.equal(editor.document.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testWrapIndividualLinesWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests, fileExtension: string = 'html'): Thenable<any> {
|
||||
return withRandomFileEditor(input, fileExtension, (editor, _) => {
|
||||
editor.selections = selections;
|
||||
const promise = wrapIndividualLinesWithAbbreviation({ abbreviation });
|
||||
if (!promise) {
|
||||
assert.equal(1, 2, 'Wrap individual lines with Abbreviation returned undefined.');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
assert.equal(editor.document.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user