Path: blob/main/extensions/emmet/src/test/cssAbbreviationAction.test.ts
4774 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import 'mocha';6import * as assert from 'assert';7import { Selection, CompletionList, CancellationTokenSource, Position, CompletionTriggerKind, CompletionContext } from 'vscode';8import { withRandomFileEditor, closeAllEditors } from './testUtils';9import { expandEmmetAbbreviation } from '../abbreviationActions';10import { DefaultCompletionItemProvider } from '../defaultCompletionProvider';1112const completionProvider = new DefaultCompletionItemProvider();13const cssContents = `14.boo {15margin: 20px 10px;16pos:f17background-image: url('tryme.png');18pos:f19}2021.boo .hoo {22margin: 10px;23ind24}25`;2627const scssContents = `28.boo {29margin: 10px;30p1031.hoo {32p2033}34}35@include b(alert) {3637margin: 10px;38p303940@include b(alert) {41p4042}43}44.foo {45margin: 10px;46margin: a47.hoo {48color: #000;49}50}51`;5253const invokeCompletionContext: CompletionContext = {54triggerKind: CompletionTriggerKind.Invoke,55triggerCharacter: undefined,56};5758suite('Tests for Expand Abbreviations (CSS)', () => {59teardown(closeAllEditors);6061test('Expand abbreviation (CSS)', () => {62return withRandomFileEditor(cssContents, 'css', (editor, _) => {63editor.selections = [new Selection(3, 1, 3, 6), new Selection(5, 1, 5, 6)];64return expandEmmetAbbreviation(null).then(() => {65assert.strictEqual(editor.document.getText(), cssContents.replace(/pos:f/g, 'position: fixed;'));66return Promise.resolve();67});68});69});7071test('No emmet when cursor inside comment (CSS)', () => {72const testContent = `73.foo {74/*margin: 10px;75m1076padding: 10px;*/77display: auto;78}79`;8081return withRandomFileEditor(testContent, 'css', (editor, _) => {82editor.selection = new Selection(3, 4, 3, 4);83return expandEmmetAbbreviation(null).then(() => {84assert.strictEqual(editor.document.getText(), testContent);85const cancelSrc = new CancellationTokenSource();86const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, invokeCompletionContext);87if (completionPromise) {88assert.strictEqual(1, 2, `Invalid completion at property value`);89}90return Promise.resolve();91});92});93});9495test('No emmet when cursor in selector of a rule (CSS)', () => {96const testContent = `97.foo {98margin: 10px;99}100101nav#102`;103104return withRandomFileEditor(testContent, 'css', (editor, _) => {105editor.selection = new Selection(5, 4, 5, 4);106return expandEmmetAbbreviation(null).then(() => {107assert.strictEqual(editor.document.getText(), testContent);108const cancelSrc = new CancellationTokenSource();109const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, invokeCompletionContext);110if (completionPromise) {111assert.strictEqual(1, 2, `Invalid completion at property value`);112}113return Promise.resolve();114});115});116});117118test('Skip when typing property values when there is a property in the next line (CSS)', () => {119const testContent = `120.foo {121margin: a122margin: 10px;123}124`;125126return withRandomFileEditor(testContent, 'css', (editor, _) => {127editor.selection = new Selection(2, 10, 2, 10);128return expandEmmetAbbreviation(null).then(() => {129assert.strictEqual(editor.document.getText(), testContent);130const cancelSrc = new CancellationTokenSource();131const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, invokeCompletionContext);132if (completionPromise) {133assert.strictEqual(1, 2, `Invalid completion at property value`);134}135return Promise.resolve();136});137});138});139140test('Skip when typing the last property value in single line rules (CSS)', () => {141const testContent = `.foo {padding: 10px; margin: a}`;142143return withRandomFileEditor(testContent, 'css', (editor, _) => {144editor.selection = new Selection(0, 30, 0, 30);145return expandEmmetAbbreviation(null).then(() => {146assert.strictEqual(editor.document.getText(), testContent);147const cancelSrc = new CancellationTokenSource();148const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(0, 30), cancelSrc.token, invokeCompletionContext);149if (completionPromise) {150assert.strictEqual(1, 2, `Invalid completion at property value`);151}152return Promise.resolve();153});154});155});156157test('Allow hex color or !important when typing property values when there is a property in the next line (CSS)', () => {158const testContent = `159.foo {160margin: #12 !161margin: 10px;162}163`;164165return withRandomFileEditor(testContent, 'css', (editor, _) => {166const cancelSrc = new CancellationTokenSource();167const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(2, 12), cancelSrc.token, invokeCompletionContext);168const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(2, 14), cancelSrc.token, invokeCompletionContext);169170if (!completionPromise1 || !completionPromise2) {171assert.strictEqual(1, 2, `Completion promise wasnt returned`);172return Promise.resolve();173}174175const callBack = (completionList: CompletionList, expandedText: string) => {176if (!completionList.items || !completionList.items.length) {177assert.strictEqual(1, 2, `Empty Completions`);178return;179}180const emmetCompletionItem = completionList.items[0];181assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);182assert.strictEqual((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);183};184185return Promise.all([completionPromise1, completionPromise2]).then(([result1, result2]) => {186assert.ok(result1);187assert.ok(result2);188callBack(result1, '#121212');189callBack(result2, '!important');190editor.selections = [new Selection(2, 12, 2, 12), new Selection(2, 14, 2, 14)];191return expandEmmetAbbreviation(null).then(() => {192assert.strictEqual(editor.document.getText(), testContent.replace('#12', '#121212').replace('!', '!important'));193});194});195});196});197198test('Skip when typing property values when there is a property in the previous line (CSS)', () => {199const testContent = `200.foo {201margin: 10px;202margin: a203}204`;205206return withRandomFileEditor(testContent, 'css', (editor, _) => {207editor.selection = new Selection(3, 10, 3, 10);208return expandEmmetAbbreviation(null).then(() => {209assert.strictEqual(editor.document.getText(), testContent);210const cancelSrc = new CancellationTokenSource();211const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(3, 10), cancelSrc.token, invokeCompletionContext);212if (completionPromise) {213assert.strictEqual(1, 2, `Invalid completion at property value`);214}215return Promise.resolve();216});217});218});219220test('Allow hex color or !important when typing property values when there is a property in the previous line (CSS)', () => {221const testContent = `222.foo {223margin: 10px;224margin: #12 !225}226`;227228return withRandomFileEditor(testContent, 'css', (editor, _) => {229const cancelSrc = new CancellationTokenSource();230const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3, 12), cancelSrc.token, invokeCompletionContext);231const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(3, 14), cancelSrc.token, invokeCompletionContext);232233if (!completionPromise1 || !completionPromise2) {234assert.strictEqual(1, 2, `Completion promise wasnt returned`);235return Promise.resolve();236}237238const callBack = (completionList: CompletionList, expandedText: string) => {239if (!completionList.items || !completionList.items.length) {240assert.strictEqual(1, 2, `Empty Completions`);241return;242}243const emmetCompletionItem = completionList.items[0];244assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);245assert.strictEqual((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);246};247248return Promise.all([completionPromise1, completionPromise2]).then(([result1, result2]) => {249assert.ok(result1);250assert.ok(result2);251callBack(result1, '#121212');252callBack(result2, '!important');253editor.selections = [new Selection(3, 12, 3, 12), new Selection(3, 14, 3, 14)];254return expandEmmetAbbreviation(null).then(() => {255assert.strictEqual(editor.document.getText(), testContent.replace('#12', '#121212').replace('!', '!important'));256});257});258});259});260261test('Skip when typing property values when it is the only property in the rule (CSS)', () => {262const testContent = `263.foo {264margin: a265}266`;267268return withRandomFileEditor(testContent, 'css', (editor, _) => {269editor.selection = new Selection(2, 10, 2, 10);270return expandEmmetAbbreviation(null).then(() => {271assert.strictEqual(editor.document.getText(), testContent);272const cancelSrc = new CancellationTokenSource();273const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 10), cancelSrc.token, invokeCompletionContext);274if (completionPromise) {275assert.strictEqual(1, 2, `Invalid completion at property value`);276}277return Promise.resolve();278});279});280});281282test('Allow hex colors or !important when typing property values when it is the only property in the rule (CSS)', () => {283const testContent = `284.foo {285margin: #12 !286}287`;288289return withRandomFileEditor(testContent, 'css', (editor, _) => {290const cancelSrc = new CancellationTokenSource();291const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(2, 12), cancelSrc.token, invokeCompletionContext);292const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(2, 14), cancelSrc.token, invokeCompletionContext);293294if (!completionPromise1 || !completionPromise2) {295assert.strictEqual(1, 2, `Completion promise wasnt returned`);296return Promise.resolve();297}298299const callBack = (completionList: CompletionList, expandedText: string) => {300if (!completionList.items || !completionList.items.length) {301assert.strictEqual(1, 2, `Empty Completions`);302return;303}304const emmetCompletionItem = completionList.items[0];305assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);306assert.strictEqual((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);307};308309return Promise.all([completionPromise1, completionPromise2]).then(([result1, result2]) => {310assert.ok(result1);311assert.ok(result2);312callBack(result1, '#121212');313callBack(result2, '!important');314editor.selections = [new Selection(2, 12, 2, 12), new Selection(2, 14, 2, 14)];315return expandEmmetAbbreviation(null).then(() => {316assert.strictEqual(editor.document.getText(), testContent.replace('#12', '#121212').replace('!', '!important'));317});318});319});320});321322test('# shouldnt expand to hex color when in selector (CSS)', () => {323const testContent = `324.foo {325#326}327`;328329return withRandomFileEditor(testContent, 'css', (editor, _) => {330editor.selection = new Selection(2, 2, 2, 2);331return expandEmmetAbbreviation(null).then(() => {332assert.strictEqual(editor.document.getText(), testContent);333const cancelSrc = new CancellationTokenSource();334const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(2, 2), cancelSrc.token, invokeCompletionContext);335if (completionPromise) {336assert.strictEqual(1, 2, `Invalid completion of hex color at property name`);337}338return Promise.resolve();339});340});341});342343344test('Expand abbreviation in completion list (CSS)', () => {345const abbreviation = 'pos:f';346const expandedText = 'position: fixed;';347348return withRandomFileEditor(cssContents, 'css', (editor, _) => {349editor.selection = new Selection(3, 1, 3, 6);350const cancelSrc = new CancellationTokenSource();351const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3, 6), cancelSrc.token, invokeCompletionContext);352const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(5, 6), cancelSrc.token, invokeCompletionContext);353if (!completionPromise1 || !completionPromise2) {354assert.strictEqual(1, 2, `Problem with expanding pos:f`);355return Promise.resolve();356}357358const callBack = (completionList: CompletionList) => {359if (!completionList.items || !completionList.items.length) {360assert.strictEqual(1, 2, `Problem with expanding pos:f`);361return;362}363const emmetCompletionItem = completionList.items[0];364assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);365assert.strictEqual((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);366assert.strictEqual(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);367};368369return Promise.all([completionPromise1, completionPromise2]).then(([result1, result2]) => {370assert.ok(result1);371assert.ok(result2);372callBack(result1);373callBack(result2);374return Promise.resolve();375});376});377});378379test('Expand abbreviation (SCSS)', () => {380return withRandomFileEditor(scssContents, 'scss', (editor, _) => {381editor.selections = [382new Selection(3, 4, 3, 4),383new Selection(5, 5, 5, 5),384new Selection(11, 4, 11, 4),385new Selection(14, 5, 14, 5)386];387return expandEmmetAbbreviation(null).then(() => {388assert.strictEqual(editor.document.getText(), scssContents.replace(/p(\d\d)/g, 'padding: $1px;'));389return Promise.resolve();390});391});392});393394test('Expand abbreviation in completion list (SCSS)', () => {395396return withRandomFileEditor(scssContents, 'scss', (editor, _) => {397editor.selection = new Selection(3, 4, 3, 4);398const cancelSrc = new CancellationTokenSource();399const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3, 4), cancelSrc.token, invokeCompletionContext);400const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(5, 5), cancelSrc.token, invokeCompletionContext);401const completionPromise3 = completionProvider.provideCompletionItems(editor.document, new Position(11, 4), cancelSrc.token, invokeCompletionContext);402const completionPromise4 = completionProvider.provideCompletionItems(editor.document, new Position(14, 5), cancelSrc.token, invokeCompletionContext);403if (!completionPromise1) {404assert.strictEqual(1, 2, `Problem with expanding padding abbreviations at line 3 col 4`);405}406if (!completionPromise2) {407assert.strictEqual(1, 2, `Problem with expanding padding abbreviations at line 5 col 5`);408}409if (!completionPromise3) {410assert.strictEqual(1, 2, `Problem with expanding padding abbreviations at line 11 col 4`);411}412if (!completionPromise4) {413assert.strictEqual(1, 2, `Problem with expanding padding abbreviations at line 14 col 5`);414}415416if (!completionPromise1 || !completionPromise2 || !completionPromise3 || !completionPromise4) {417return Promise.resolve();418}419420const callBack = (completionList: CompletionList, abbreviation: string, expandedText: string) => {421if (!completionList.items || !completionList.items.length) {422assert.strictEqual(1, 2, `Problem with expanding m10`);423return;424}425const emmetCompletionItem = completionList.items[0];426assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);427assert.strictEqual((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);428assert.strictEqual(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);429};430431return Promise.all([completionPromise1, completionPromise2, completionPromise3, completionPromise4]).then(([result1, result2, result3, result4]) => {432assert.ok(result1);433assert.ok(result2);434assert.ok(result3);435assert.ok(result4);436callBack(result1, 'p10', 'padding: 10px;');437callBack(result2, 'p20', 'padding: 20px;');438callBack(result3, 'p30', 'padding: 30px;');439callBack(result4, 'p40', 'padding: 40px;');440return Promise.resolve();441});442});443});444445446test('Invalid locations for abbreviations in scss', () => {447const scssContentsNoExpand = `448m10449.boo {450margin: 10px;451.hoo {452background:453}454}455`;456457return withRandomFileEditor(scssContentsNoExpand, 'scss', (editor, _) => {458editor.selections = [459new Selection(1, 3, 1, 3), // outside rule460new Selection(5, 15, 5, 15) // in the value part of property value461];462return expandEmmetAbbreviation(null).then(() => {463assert.strictEqual(editor.document.getText(), scssContentsNoExpand);464return Promise.resolve();465});466});467});468469test('Invalid locations for abbreviations in scss in completion list', () => {470const scssContentsNoExpand = `471m10472.boo {473margin: 10px;474.hoo {475background:476}477}478`;479480return withRandomFileEditor(scssContentsNoExpand, 'scss', (editor, _) => {481editor.selection = new Selection(1, 3, 1, 3); // outside rule482const cancelSrc = new CancellationTokenSource();483let completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, invokeCompletionContext);484if (completionPromise) {485assert.strictEqual(1, 2, `m10 gets expanded in invalid location (outside rule)`);486}487488editor.selection = new Selection(5, 15, 5, 15); // in the value part of property value489completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, invokeCompletionContext);490if (completionPromise) {491return completionPromise.then((completionList: CompletionList | undefined) => {492if (completionList && completionList.items && completionList.items.length > 0) {493assert.strictEqual(1, 2, `m10 gets expanded in invalid location (n the value part of property value)`);494}495return Promise.resolve();496});497}498return Promise.resolve();499});500});501502test('Skip when typing property values when there is a nested rule in the next line (SCSS)', () => {503return withRandomFileEditor(scssContents, 'scss', (editor, _) => {504editor.selection = new Selection(19, 10, 19, 10);505return expandEmmetAbbreviation(null).then(() => {506assert.strictEqual(editor.document.getText(), scssContents);507const cancelSrc = new CancellationTokenSource();508const completionPromise = completionProvider.provideCompletionItems(editor.document, new Position(19, 10), cancelSrc.token, invokeCompletionContext);509if (completionPromise) {510assert.strictEqual(1, 2, `Invalid completion at property value`);511}512return Promise.resolve();513});514});515});516});517518519520