diff --git a/src/extension.ts b/src/extension.ts index aec5c6c..e646a8b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -4,74 +4,82 @@ import { MarkdownString, Range, window, -} from "vscode"; -import { createConverter } from "vscode-languageclient/lib/common/codeConverter"; -import { formatDiagnostic } from "./format/formatDiagnostic"; -import { prettify } from "./format/prettify"; -import { hoverProvider } from "./provider/hoverProvider"; -import { registerSelectedTextHoverProvider } from "./provider/selectedTextHoverProvider"; -import { uriStore } from "./provider/uriStore"; -import { has } from "./utils"; + env +} from 'vscode' +import * as vscode from 'vscode' +import { createConverter } from 'vscode-languageclient/lib/common/codeConverter' +import { formatDiagnostic } from './format/formatDiagnostic' +import { prettify } from './format/prettify' +import { hoverProvider } from './provider/hoverProvider' +import { registerSelectedTextHoverProvider } from './provider/selectedTextHoverProvider' +import { uriStore } from './provider/uriStore' +import { has } from './utils' +import { getLocale } from './format/i18n/locales' export function activate(context: ExtensionContext) { - const registeredLanguages = new Set(); - const converter = createConverter(); + const registeredLanguages = new Set() + const converter = createConverter() + const { regexes } = getLocale(env.language) - registerSelectedTextHoverProvider(context); + registerSelectedTextHoverProvider(context, regexes) context.subscriptions.push( languages.onDidChangeDiagnostics(async (e) => { e.uris.forEach((uri) => { - const diagnostics = languages.getDiagnostics(uri); + const diagnostics = languages.getDiagnostics(uri) const items: { - range: Range; - contents: MarkdownString[]; - }[] = []; + range: Range + contents: MarkdownString[] + }[] = [] - let hasTsDiagnostic = false; + let hasTsDiagnostic = false diagnostics .filter((diagnostic) => diagnostic.source - ? has(["ts", "deno-ts", "js"], diagnostic.source) + ? has(['ts', 'deno-ts', 'js'], diagnostic.source) : false ) .forEach(async (diagnostic) => { // formatDiagnostic converts message based on LSP Diagnostic type, not VSCode Diagnostic type, so it can be used in other IDEs. // Here we convert VSCode Diagnostic to LSP Diagnostic to make formatDiagnostic recognize it. const markdownString = new MarkdownString( - formatDiagnostic(converter.asDiagnostic(diagnostic), prettify) - ); + formatDiagnostic( + converter.asDiagnostic(diagnostic), + prettify, + regexes + ) + ) - markdownString.isTrusted = true; - markdownString.supportHtml = true; + markdownString.isTrusted = true + markdownString.supportHtml = true items.push({ range: diagnostic.range, - contents: [markdownString], - }); - hasTsDiagnostic = true; - }); - uriStore[uri.path] = items; + contents: [markdownString] + }) + hasTsDiagnostic = true + }) + uriStore[uri.path] = items if (hasTsDiagnostic) { const editor = window.visibleTextEditors.find( (editor) => editor.document.uri.toString() === uri.toString() - ); + ) if (editor && !registeredLanguages.has(editor.document.languageId)) { - registeredLanguages.add(editor.document.languageId); + registeredLanguages.add(editor.document.languageId) context.subscriptions.push( languages.registerHoverProvider( { - language: editor.document.languageId, + language: editor.document.languageId }, hoverProvider ) - ); + ) } } - }); + }) }) - ); + ) } diff --git a/src/format/formatDiagnostic.ts b/src/format/formatDiagnostic.ts index f92648f..5e3d29d 100644 --- a/src/format/formatDiagnostic.ts +++ b/src/format/formatDiagnostic.ts @@ -2,16 +2,20 @@ import { Diagnostic } from "vscode-languageserver-types"; import { title } from "../components"; import { d } from "../utils"; import { embedSymbolLinks } from "./embedSymbolLinks"; -import { formatDiagnosticMessage } from "./formatDiagnosticMessage"; +import { FormatDiagnosticMessageRules, formatDiagnosticMessage } from "./formatDiagnosticMessage"; import { identSentences } from "./identSentences"; -export function formatDiagnostic(diagnostic: Diagnostic, format: (type: string) => string) { +export function formatDiagnostic(diagnostic: Diagnostic, format: (type: string) => string, formatRegexes:Record) { const newDiagnostic = embedSymbolLinks(diagnostic); return d/*html*/ ` ${title(newDiagnostic)} - ${formatDiagnosticMessage(identSentences(newDiagnostic.message), format)} + ${formatDiagnosticMessage( + identSentences(newDiagnostic.message), + format, + formatRegexes + )} `; } diff --git a/src/format/formatDiagnosticMessage.ts b/src/format/formatDiagnosticMessage.ts index c3e187e..63c48a6 100644 --- a/src/format/formatDiagnosticMessage.ts +++ b/src/format/formatDiagnosticMessage.ts @@ -1,11 +1,11 @@ -import { inlineCodeBlock, unstyledCodeBlock } from "../components"; -import { formatTypeBlock } from "./formatTypeBlock"; +import { inlineCodeBlock, unstyledCodeBlock } from '../components' +import { formatTypeBlock } from './formatTypeBlock' const formatTypeScriptBlock = (_: string, code: string) => - inlineCodeBlock(code, "typescript"); + inlineCodeBlock(code, 'typescript') const formatSimpleTypeBlock = (_: string, code: string) => - inlineCodeBlock(code, "type"); + inlineCodeBlock(code, 'type') const formatTypeOrModuleBlock = ( _: string, @@ -15,88 +15,100 @@ const formatTypeOrModuleBlock = ( ) => formatTypeBlock( prefix, - ["module", "file", "file name"].includes(prefix.toLowerCase()) + ['module', 'file', 'file name'].includes(prefix.toLowerCase()) ? `"${code}"` : code, format - ); + ) + +export type FormatDiagnosticMessageRules = + | 'DeclareModuleSnippet' + | 'MissingPropsError' + | 'TypePairs' + | 'TypeAnnotationOptions' + | 'Overloaded' + | 'SimpleStrings' + | 'Types' + | 'ReversedTypes' + | 'SimpleTypesRest' + | 'TypescriptKeywords' + | 'ReturnValues' + | 'RegularCodeBlocks' export const formatDiagnosticMessage = ( message: string, - format: (type: string) => string + format: (type: string) => string, + regexes: Record ) => message // format declare module snippet .replaceAll( - /'(declare module )'(.*)';'/g, + regexes['DeclareModuleSnippet'], (_: string, p1: string, p2: string) => formatTypeScriptBlock(_, `${p1} "${p2}"`) ) // format missing props error .replaceAll( - /(is missing the following properties from type )'(.*)': (.+?)(?=and|$)/g, + regexes['MissingPropsError'], (_, pre, type, post) => - `${pre}${formatTypeBlock("", type, format)}:
    ${post - .split(", ") + `${pre}${formatTypeBlock('', type, format)}:
      ${post + .split(', ') .filter(Boolean) .map((prop: string) => `
    • ${prop}
    • `) - .join("")}
    ` + .join('')}
` ) // Format type pairs .replaceAll( - /(types) '(.*?)' and '(.*?)'[\.]?/gi, + regexes['TypePairs'], (_: string, p1: string, p2: string, p3: string) => `${formatTypeBlock(p1, p2, format)} and ${formatTypeBlock( - "", + '', p3, format )}` ) // Format type annotation options .replaceAll( - /type annotation must be '(.*?)' or '(.*?)'[\.]?/gi, + regexes['TypeAnnotationOptions'], (_: string, p1: string, p2: string, p3: string) => `${formatTypeBlock(p1, p2, format)} or ${formatTypeBlock( - "", + '', p3, format )}` ) + // Format Overloaded .replaceAll( - /(Overload \d of \d), '(.*?)', /gi, - (_, p1: string, p2: string) => `${p1}${formatTypeBlock("", p2, format)}` + regexes['Overloaded'], + (_, p1: string, p2: string) => `${p1}${formatTypeBlock('', p2, format)}` ) // format simple strings - .replaceAll(/^'"[^"]*"'$/g, formatTypeScriptBlock) + .replaceAll(regexes['SimpleStrings'], formatTypeScriptBlock) // Format types - .replaceAll( - /(type|type alias|interface|module|file|file name|method's) '(.*?)'(?=[\s.])/gi, - (_, p1: string, p2: string) => formatTypeOrModuleBlock(_, p1, p2, format) + .replaceAll(regexes['Types'], (_, p1: string, p2: string) => + formatTypeOrModuleBlock(_, p1, p2, format) ) // Format reversed types .replaceAll( - /(.*)'([^>]*)' (type|interface|return type|file|module)/gi, + regexes['ReversedTypes'], (_: string, p1: string, p2: string, p3: string) => - `${p1}${formatTypeOrModuleBlock(_, "", p2, format)} ${p3}` + `${p1}${formatTypeOrModuleBlock(_, '', p2, format)} ${p3}` ) // Format simple types that didn't captured before - .replaceAll( - /'((void|null|undefined|any|boolean|string|number|bigint|symbol)(\[\])?)'/g, - formatSimpleTypeBlock - ) + .replaceAll(regexes['SimpleTypesRest'], formatSimpleTypeBlock) // Format some typescript key words .replaceAll( - /'(import|export|require|in|continue|break|let|false|true|const|new|throw|await|for await|[0-9]+)( ?.*?)'/g, + regexes['TypescriptKeywords'], (_: string, p1: string, p2: string) => formatTypeScriptBlock(_, `${p1}${p2}`) ) // Format return values .replaceAll( - /(return|operator) '(.*?)'/gi, - (_, p1: string, p2: string) => `${p1} ${formatTypeScriptBlock("", p2)}` + regexes['ReturnValues'], + (_, p1: string, p2: string) => `${p1} ${formatTypeScriptBlock('', p2)}` ) // Format regular code blocks .replaceAll( - /'((?:(?!:\s*}).)*?)' (?!\s*:)/g, + regexes['RegularCodeBlocks'], (_: string, p1: string) => `${unstyledCodeBlock(p1)} ` - ); + ) diff --git a/src/test/suite/errorMessageMocks.ts b/src/format/i18n/en.ts similarity index 51% rename from src/test/suite/errorMessageMocks.ts rename to src/format/i18n/en.ts index 6ea4d41..b017956 100644 --- a/src/test/suite/errorMessageMocks.ts +++ b/src/format/i18n/en.ts @@ -1,45 +1,83 @@ -import { d } from "../../utils"; +import { inlineCodeBlock } from '../../components' +import { ErrorMessageMocks, TestSuites } from '../../test/suite/types' +import { d } from '../../utils' +import { FormatDiagnosticMessageRules } from '../formatDiagnosticMessage' +import { Locale } from './locales' -/** - * This file contains mocks of error messages, only some of them - * are used in tests but all of them can be used to test and debug - * the formatting visually, you can try to select them on debug and check the hover. - */ +const regexes: Record = { + DeclareModuleSnippet: /'(declare module )'(.*)';'/g, + MissingPropsError: + /(is missing the following properties from type .*: )(.+?)(?=and|$)/g, + TypePairs: /(types) '(.*?)' and '(.*?)'[\.]?/gi, + TypeAnnotationOptions: /type annotation must be '(.*?)' or '(.*?)'[\.]?/gi, + Overloaded: /(Overload \d of \d), '(.*?)', /gi, + SimpleStrings: /^'"[^"]*"'$/g, + Types: /(.*)'([^>]*)' (type|interface|return type|file|module)/gi, + ReversedTypes: /(.*)'([^>]*)' (type|interface|return type|file|module)/gi, + SimpleTypesRest: + /'((void|null|undefined|any|boolean|string|number|bigint|symbol)(\[\])?)'/g, + TypescriptKeywords: + /'(import|export|require|in|continue|break|let|false|true|const|new|throw|await|for await|[0-9]+)( ?.*?)'/g, + ReturnValues: /(return|operator) '(.*?)'/gi, + RegularCodeBlocks: /'(.*?)'/g +} -export const errorWithSpecialCharsInObjectKeys = d` -Type 'string' is not assignable to type '{ 'abc*bc': string; }'. -`; +const testCase: Record = { + 'Test of adding missing parentheses': 'Hello, {world! [This] is a (test.)}', -export const errorWithDashInObjectKeys = d` -Type '{ person: { 'first-name': string; }; }' is not assignable to type 'string'. -`; + 'Special characters in object keys': + 'Type ' + + inlineCodeBlock('string', 'type') + + ' is not assignable to type ' + + inlineCodeBlock(`{ "abc*bc": string }`, 'type') + + '.', + + "Special method's word in the error": + 'Type ' + + inlineCodeBlock(`{ person: { "first-name": string } }`, 'type') + + ' is not assignable to type ' + + inlineCodeBlock('string', 'type') + + '.', + + 'Formatting type with params destructuring should succeed': '' +} -/** - * Formatting error from this issue: https://github.com/yoavbls/pretty-ts-errors/issues/20 - */ -export const errorWithMethodsWordInIt = d` +const errorMessageMocks: Record = { + errorWithSpecialCharsInObjectKeys: d` +Type 'string' is not assignable to type '{ 'abc*bc': string; }'. +`, + errorWithDashInObjectKeys: d` +Type '{ person: { 'first-name': string; }; }' is not assignable to type 'string'. +`, + errorWithMethodsWordInIt: d` The 'this' context of type 'ElementHandle' is not assignable to method's 'this' of type 'ElementHandle'. Type 'Node' is missing the following properties from type 'Element': attributes, classList, className, clientHeight, and 114 more. -`; - -const errorWithParamsDestructuring = d` +`, + errorWithParamsDestructuring: d` Argument of type '{ $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; }' is not assignable to parameter of type 'VTableConfig'. Property 'data' is missing in type '{ $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; }' but required in type 'VTableConfig'. -`; +`, -const errorWithLongType = d` + errorWithLongType: d` Property 'isFlying' is missing in type '{ animal: { __typename?: "Animal" | undefined; id: string; name: string; age: number; isAlived: boolean; ... 8 more ...; attributes: { ...; } | ... 3 more ... | { ...; }; }; }' but required in type '{ animal: { __typename?: "Animal" | undefined; id: string; name: string; age: number; isAlived: boolean; isFlying: boolean; ... 8 more ...; attributes: { ...; } | ... 3 more ... | { ...; }; }; }'. -`; +`, -const errorWithTruncatedType2 = d` + errorWithTruncatedType2: d` Type '{ '!top': string[]; 'xsl:declaration': { attrs: { 'default-collation': null; 'exclude-result-prefixes': null; 'extension-element-prefixes': null; 'use-when': null; 'xpath-default-namespace': null; }; }; 'xsl:instruction': { ...; }; ... 49 more ...; 'xsl:literal-result-element': {}; }' is missing the following properties from type 'GraphQLSchema': description, extensions, astNode, extensionASTNodes, and 21 more. -`; +`, -const errorWithSimpleIndentations = d` + errorWithSimpleIndentations: d` Type '(newIds: number[]) => void' is not assignable to type '(selectedId: string[]) => void'. Types of parameters 'newIds' and 'selectedId' are incompatible. Type 'string[]' is not assignable to type 'number[]'. Type 'string' is not assignable to type 'number'. -`; +` +} + +const en: Locale = { + regexes, + testCase, + errorMessageMocks +} -("Property 'user' is missing in type '{ person: { username: string; email: string; }; }' but required in type '{ user: { name: string; email: `${string}@${string}.${string}`; age: number; }; }'."); +export default en diff --git a/src/format/i18n/ko.ts b/src/format/i18n/ko.ts new file mode 100644 index 0000000..9e757ae --- /dev/null +++ b/src/format/i18n/ko.ts @@ -0,0 +1,83 @@ +import { inlineCodeBlock } from '../../components' +import { ErrorMessageMocks, TestSuites } from '../../test/suite/types' +import { d } from '../../utils' +import { FormatDiagnosticMessageRules } from '../formatDiagnosticMessage' +import { Locale } from './locales' + +const regexes: Record = { + DeclareModuleSnippet: /'(declare module )'(.*)';'/g, + MissingPropsError: + /(is missing the following properties from type .*: )(.+?)(?=and|$)/g, + TypePairs: /(types) '(.*?)' and '(.*?)'[\.]?/gi, + TypeAnnotationOptions: /type annotation must be '(.*?)' or '(.*?)'[\.]?/gi, + Overloaded: /(Overload \d of \d), '(.*?)', /gi, + SimpleStrings: /^'"[^"]*"'$/g, + Types: /(.*)'([^>]*)' (type|interface|return type|file|module)/gi, + ReversedTypes: /(.*)'([^>]*)' (type|interface|return type|file|module)/gi, + SimpleTypesRest: + /'((void|null|undefined|any|boolean|string|number|bigint|symbol)(\[\])?)'/g, + TypescriptKeywords: + /'(import|export|require|in|continue|break|let|false|true|const|new|throw|await|for await|[0-9]+)( ?.*?)'/g, + ReturnValues: /(return|operator) '(.*?)'/gi, + RegularCodeBlocks: /'(.*?)'/g +} + +const testCase: Record = { + 'Test of adding missing parentheses': 'Hello, {world! [This] is a (test.)}', + + 'Special characters in object keys': + 'Type ' + + inlineCodeBlock('string', 'type') + + ' is not assignable to type ' + + inlineCodeBlock(`{ "abc*bc": string }`, 'type') + + '.', + + "Special method's word in the error": + 'Type ' + + inlineCodeBlock(`{ person: { "first-name": string } }`, 'type') + + ' is not assignable to type ' + + inlineCodeBlock('string', 'type') + + '.', + + 'Formatting type with params destructuring should succeed': '' +} + +const errorMessageMocks: Record = { + errorWithSpecialCharsInObjectKeys: d` +'string' 형식은 '{ 'abc*bc': string; }' 형식에 할당할 수 없습니다. +`, + errorWithDashInObjectKeys: d` +'{ person: { 'first-name': string; }; }' 형식은 'string' 형식에 할당할 수 없습니다. +`, + errorWithMethodsWordInIt: d` +The 'this' context of type 'ElementHandle' is not assignable to method's 'this' of type 'ElementHandle'. + Type 'Node' is missing the following properties from type 'Element': attributes, classList, className, clientHeight, and 114 more. +`, + errorWithParamsDestructuring: d` +Argument of type '{ $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; }' is not assignable to parameter of type 'VTableConfig'. + Property 'data' is missing in type '{ $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; }' but required in type 'VTableConfig'. +`, + + errorWithLongType: d` +Property 'isFlying' is missing in type '{ animal: { __typename?: "Animal" | undefined; id: string; name: string; age: number; isAlived: boolean; ... 8 more ...; attributes: { ...; } | ... 3 more ... | { ...; }; }; }' but required in type '{ animal: { __typename?: "Animal" | undefined; id: string; name: string; age: number; isAlived: boolean; isFlying: boolean; ... 8 more ...; attributes: { ...; } | ... 3 more ... | { ...; }; }; }'. +`, + + errorWithTruncatedType2: d` +Type '{ '!top': string[]; 'xsl:declaration': { attrs: { 'default-collation': null; 'exclude-result-prefixes': null; 'extension-element-prefixes': null; 'use-when': null; 'xpath-default-namespace': null; }; }; 'xsl:instruction': { ...; }; ... 49 more ...; 'xsl:literal-result-element': {}; }' is missing the following properties from type 'GraphQLSchema': description, extensions, astNode, extensionASTNodes, and 21 more. +`, + + errorWithSimpleIndentations: d` +Type '(newIds: number[]) => void' is not assignable to type '(selectedId: string[]) => void'. + Types of parameters 'newIds' and 'selectedId' are incompatible. + Type 'string[]' is not assignable to type 'number[]'. + Type 'string' is not assignable to type 'number'. +` +} + +const ko: Locale = { + regexes, + testCase, + errorMessageMocks +} + +export default ko diff --git a/src/format/i18n/locales.ts b/src/format/i18n/locales.ts new file mode 100644 index 0000000..b4e1bdd --- /dev/null +++ b/src/format/i18n/locales.ts @@ -0,0 +1,23 @@ +import { ErrorMessageMocks, TestSuites } from '../../test/suite/types' +import { FormatDiagnosticMessageRules } from '../formatDiagnosticMessage' +import en from './en' +import ko from './ko' + +const supportedLocales = ['en', 'ko'] as const +export type SupportedLocales = (typeof supportedLocales)[number] + +export type Locale = { + regexes: Record + testCase: Record + errorMessageMocks: Record +} + +export function getLocale(locale: string) { + switch (locale) { + case 'ko': + return ko + case 'en': + default: + return en + } +} diff --git a/src/provider/selectedTextHoverProvider.ts b/src/provider/selectedTextHoverProvider.ts index 59b2d6d..d611428 100644 --- a/src/provider/selectedTextHoverProvider.ts +++ b/src/provider/selectedTextHoverProvider.ts @@ -1,34 +1,38 @@ -import { ExtensionContext, MarkdownString, languages, window } from "vscode"; -import { createConverter } from "vscode-languageclient/lib/common/codeConverter"; -import { miniLine } from "../components"; -import { formatDiagnostic } from "../format/formatDiagnostic"; -import { prettify } from "../format/prettify"; -import { d } from "../utils"; +import { ExtensionContext, MarkdownString, languages, window } from 'vscode' +import { createConverter } from 'vscode-languageclient/lib/common/codeConverter' +import { miniLine } from '../components' +import { formatDiagnostic } from '../format/formatDiagnostic' +import { prettify } from '../format/prettify' +import { d } from '../utils' +import { FormatDiagnosticMessageRules } from '../format/formatDiagnosticMessage' -const isDebugMode = () => process.env.VSCODE_DEBUG_MODE === "true"; +const isDebugMode = () => process.env.VSCODE_DEBUG_MODE === 'true' /** * Register an hover provider in debug only. * It format selected text and help test things visually easier. */ -export function registerSelectedTextHoverProvider(context: ExtensionContext) { - const converter = createConverter(); +export function registerSelectedTextHoverProvider( + context: ExtensionContext, + regexes: Record +) { + const converter = createConverter() if (!isDebugMode()) { - return; + return } context.subscriptions.push( languages.registerHoverProvider( { - language: "typescript", - pattern: "**/test/**/*.ts", + language: 'typescript', + pattern: '**/test/**/*.ts' }, { provideHover(document, position) { - const editor = window.activeTextEditor; - const range = document.getWordRangeAtPosition(position); - const message = document.getText(editor!.selection); + const editor = window.activeTextEditor + const range = document.getWordRangeAtPosition(position) + const message = document.getText(editor!.selection) const contents = range && message @@ -40,25 +44,26 @@ export function registerSelectedTextHoverProvider(context: ExtensionContext) { message, range, severity: 0, - source: "ts", - code: 1337, + source: 'ts', + code: 1337 }), - prettify + prettify, + regexes ) - ), + ) ] - : []; + : [] - contents[0].isTrusted = true; - contents[0].supportHtml = true; + contents[0].isTrusted = true + contents[0].supportHtml = true return { - contents, - }; - }, + contents + } + } } ) - ); + ) } const debugHoverHeader = d/*html*/ ` @@ -69,4 +74,4 @@ const debugHoverHeader = d/*html*/ `

${miniLine} -`; +` diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts index 7c6f389..be13803 100644 --- a/src/test/suite/extension.test.ts +++ b/src/test/suite/extension.test.ts @@ -1,57 +1,56 @@ -import * as assert from "assert"; +import * as assert from 'assert' // You can import and use all API from the 'vscode' module // as well as import your extension to test it -import * as vscode from "vscode"; -import { inlineCodeBlock } from "../../components"; -import { addMissingParentheses } from "../../format/addMissingParentheses"; -import { formatDiagnosticMessage } from "../../format/formatDiagnosticMessage"; -import { prettifyType } from "../../format/formatTypeBlock"; -import { prettify } from "../../format/prettify"; -import { d } from "../../utils"; -import { - errorWithDashInObjectKeys, - errorWithSpecialCharsInObjectKeys, -} from "./errorMessageMocks"; +import * as vscode from 'vscode' +import { addMissingParentheses } from '../../format/addMissingParentheses' +import { formatDiagnosticMessage } from '../../format/formatDiagnosticMessage' +import { prettifyType } from '../../format/formatTypeBlock' +import { prettify } from '../../format/prettify' +import { d } from '../../utils' +import { getLocale } from '../../format/i18n/locales' -suite("Extension Test Suite", () => { - vscode.window.showInformationMessage("Start all tests."); +suite('Extension Test Suite', () => { + vscode.window.showInformationMessage('Start all tests.') + const { regexes, testCase, errorMessageMocks } = getLocale( + vscode.env.language + ) - test("Test of adding missing parentheses", () => { + test('Test of adding missing parentheses', () => { assert.strictEqual( - addMissingParentheses("Hello, {world! [This] is a (test."), - "Hello, {world! [This] is a (test.)}" - ); - }); + addMissingParentheses('Hello, {world! [This] is a (test.'), + testCase['Test of adding missing parentheses'] + ) + }) - test("Special characters in object keys", () => { + test('Special characters in object keys', () => { assert.strictEqual( - formatDiagnosticMessage(errorWithSpecialCharsInObjectKeys, prettify), - "Type " + - inlineCodeBlock("string", "type") + - " is not assignable to type " + - inlineCodeBlock(`{ "abc*bc": string }`, "type") + - "." - ); - }); + formatDiagnosticMessage( + errorMessageMocks['errorWithSpecialCharsInObjectKeys'], + prettify, + regexes + ), + testCase['Special characters in object keys'] + ) + }) test("Special method's word in the error", () => { assert.strictEqual( - formatDiagnosticMessage(errorWithDashInObjectKeys, prettify), - "Type " + - inlineCodeBlock(`{ person: { "first-name": string } }`, "type") + - " is not assignable to type " + - inlineCodeBlock("string", "type") + - "." - ); - }); + formatDiagnosticMessage( + errorMessageMocks['errorWithDashInObjectKeys'], + prettify, + regexes + ), + testCase["Special method's word in the error"] + ) + }) - test("Formatting type with params destructuring should succeed", () => { + test('Formatting type with params destructuring should succeed', () => { prettifyType( d` { $ref: null; ref: (ref: any) => any; columns: ({ label: string; prop: string; } | { label: string; formatter: ({ ip_type }: any) => any; } | { actions: { label: string; disabled: ({ contract_id }: any) => boolean; handler({ contract_id }: any): void; }[]; })[]; ... 4 more ...; load(): Promise<...>; } `, prettify, { throwOnError: true } - ); - }); -}); + ) + }) +}) diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts index 7029e38..c98d20c 100644 --- a/src/test/suite/index.ts +++ b/src/test/suite/index.ts @@ -1,38 +1,38 @@ -import * as path from 'path'; -import * as Mocha from 'mocha'; -import * as glob from 'glob'; +import * as path from 'path' +import * as Mocha from 'mocha' +import * as glob from 'glob' export function run(): Promise { - // Create the mocha test - const mocha = new Mocha({ - ui: 'tdd', - color: true - }); + // Create the mocha test + const mocha = new Mocha({ + ui: 'tdd', + color: true + }) - const testsRoot = path.resolve(__dirname, '..'); + const testsRoot = path.resolve(__dirname, '..') - return new Promise((c, e) => { - glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { - if (err) { - return e(err); - } + return new Promise((c, e) => { + glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { + if (err) { + return e(err) + } - // Add files to the test suite - files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + // Add files to the test suite + files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))) - try { - // Run the mocha test - mocha.run(failures => { - if (failures > 0) { - e(new Error(`${failures} tests failed.`)); - } else { - c(); - } - }); - } catch (err) { - console.error(err); - e(err); - } - }); - }); + try { + // Run the mocha test + mocha.run((failures) => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)) + } else { + c() + } + }) + } catch (err) { + console.error(err) + e(err) + } + }) + }) } diff --git a/src/test/suite/types.ts b/src/test/suite/types.ts new file mode 100644 index 0000000..5d89fdb --- /dev/null +++ b/src/test/suite/types.ts @@ -0,0 +1,14 @@ +export type ErrorMessageMocks = + | 'errorWithSpecialCharsInObjectKeys' + | 'errorWithDashInObjectKeys' + | 'errorWithMethodsWordInIt' + | 'errorWithParamsDestructuring' + | 'errorWithLongType' + | 'errorWithTruncatedType2' + | 'errorWithSimpleIndentations' + +export type TestSuites = + | 'Test of adding missing parentheses' + | 'Special characters in object keys' + | "Special method's word in the error" + | 'Formatting type with params destructuring should succeed'