UNPKG

@shopify/theme-language-server-common

Version:

<h1 align="center" style="position: relative;" > <br> <img src="https://github.com/Shopify/theme-check-vscode/blob/main/images/shopify_glyph.png?raw=true" alt="logo" width="141" height="160"> <br> Theme Language Server </h1>

232 lines (221 loc) 7.03 kB
import { describe, beforeEach, it, expect } from 'vitest'; import { DocumentManager } from '../../documents'; import { CompletionsProvider } from '../CompletionsProvider'; import { MetafieldDefinitionMap, ObjectEntry } from '@shopify/theme-check-common'; describe('Module: ObjectCompletionProvider', async () => { let provider: CompletionsProvider; beforeEach(async () => { const _objects: ObjectEntry[] = [ { name: 'all_products' }, { name: 'global' }, { name: 'section', access: { global: false, template: [], parents: [], }, }, { name: 'block', access: { global: false, template: [], parents: [], }, }, { name: 'predictive_search', access: { global: false, template: [], parents: [], }, }, { name: 'recommendations', access: { global: false, template: [], parents: [], }, }, { name: 'product', properties: [ { name: 'metafields', }, ], }, { name: 'metafield', access: { global: false, template: [], parents: [], }, properties: [ { name: 'type', description: 'the type of the metafield', return_type: [{ type: 'string', name: '' }], }, { name: 'value', description: 'the value of the metafield', return_type: [{ type: 'untyped', name: '' }], }, ], }, ]; provider = new CompletionsProvider({ documentManager: new DocumentManager(), themeDocset: { filters: async () => [], objects: async () => _objects, liquidDrops: async () => _objects, tags: async () => [], systemTranslations: async () => ({}), }, getMetafieldDefinitions: async (_rootUri: string) => { return { article: [], blog: [], collection: [], company: [], company_location: [], location: [], market: [], order: [], page: [], product: [ { key: 'color', name: 'color', namespace: 'custom', description: 'the color of the product', type: { category: 'COLOR', name: 'color', }, }, ], variant: [], shop: [], } as MetafieldDefinitionMap; }, }); }); it('should complete variable lookups', async () => { const contexts = [ `{{ a█`, `{% echo a█ %}`, `{% assign x = a█ %}`, `{% for a in a█ %}`, `{% for a in b reversed limit: a█ %}`, `{% paginate b by a█ %}`, `{% paginate b by col, window_size: a█ %}`, `{% if a█ %}`, `{% if a > a█ %}`, `{% if a > b or a█ %}`, `{% if a > b or c > a█ %}`, `{% elsif a > a█ %}`, `{% when a█ %}`, `{% when a, a█ %}`, `{% cycle a█ %}`, `{% cycle 'foo', a█ %}`, `{% cycle 'foo': a█ %}`, `{% render 'snip', var: a█ %}`, `{% render 'snip' for a█ as item %}`, `{% render 'snip' with a█ as name %}`, `{% for x in (1..a█) %}`, // `{% paginate a█ by 50 %}`, `<a-{{ a█ }}`, `<a data-{{ a█ }}`, `<a data={{ a█ }}`, `<a data="{{ a█ }}"`, `<a data='x{{ a█ }}'`, ]; await Promise.all( contexts.map((context) => expect(provider, context).to.complete(context, ['all_products'])), ); }); it('should complete variable lookups (placeholder mode)', async () => { const contexts = [ `{{ █`, `{% echo █ %}`, `{% assign x = █ %}`, `{% for a in █ %}`, `{% for a in b reversed limit: █ %}`, `{% paginate b by █ %}`, `{% paginate b by col, window_size: █ %}`, `{% if █ %}`, `{% if a > █ %}`, `{% if a > b or █ %}`, `{% if a > b or c > █ %}`, `{% elsif a > █ %}`, `{% when █ %}`, `{% when a, █ %}`, `{% cycle █ %}`, `{% cycle 'foo', █ %}`, `{% cycle 'foo': █ %}`, `{% render 'snip', var: █ %}`, `{% render 'snip' for █ as item %}`, `{% render 'snip' with █ as name %}`, `{% for x in (1..█) %}`, // `{% paginate █ by 50 %}`, `<a-{{ █ }}`, `<a data-{{ █ }}`, `<a data={{ █ }}`, `<a data="{{ █ }}"`, `<a data='x{{ █ }}'`, ]; await Promise.all( contexts.map((context) => expect(provider, context).to.complete(context, ['all_products', 'global', 'product']), ), ); }); it('should complete contextual variables', async () => { const contexts: [string, string][] = [ ['{% paginate all_products by 5 %}{{ pagi█ }}{% endpaginate %}', 'paginate'], ['{% form "cart" %}{{ for█ }}{% endform %}', 'form'], ['{% for p in all_products %}{{ for█ }}{% endfor %}', 'forloop'], ['{% tablerow p in all_products %}{{ tablerow█ }}{% endtablerow %}', 'tablerowloop'], ['{% layout non█ %}', 'none'], ['{% increment var %}{{ var█ }}', 'var'], ['{% decrement var %}{{ var█ }}', 'var'], ['{% assign var = 1 %}{{ var█ }}', 'var'], ]; for (const [context, expected] of contexts) { await expect(provider, context).to.complete(context, [expected]); const outOfContext = `{{ ${expected}█ }}`; await expect(provider, outOfContext).to.complete(outOfContext, []); } }); it('should complete relative-path-dependent contextual variables', async () => { const contexts: [string, string][] = [ ['section', 'sections/main-product.liquid'], ['block', 'blocks/my-block.liquid'], ['predictive_search', 'sections/predictive-search.liquid'], ['recommendations', 'sections/recommendations.liquid'], ]; for (const [object, relativePath] of contexts) { const source = `{{ ${object}█ }}`; await expect(provider, source).to.complete({ source, relativePath }, [object]); await expect(provider, source).to.complete({ source, relativePath: 'file.liquid' }, []); } }); it('should not complete anything if there is nothing to complete', async () => { await expect(provider).to.complete('{% assign x = "█" %}', []); }); it('should complete metafields defined by getMetafieldDefinitions', async () => { await expect(provider).to.complete('{% echo product.metafields.█ %}', ['custom']); await expect(provider).to.complete('{% echo product.metafields.custom.█ %}', ['color']); await expect(provider).to.complete('{% echo product.metafields.custom.color.█ %}', [ 'type', 'value', ]); }); });