import { EditorState } from '@codemirror/state'; import { CompletionContext, CompletionResult, CompletionSource } from '@codemirror/autocomplete'; import { html } from '../dist/index.js'; import ist from 'ist'; function get(doc: string, conf: { explicit?: boolean } = {}) { let cur = doc.indexOf('|'); doc = doc.slice(0, cur) + doc.slice(cur + 1); let state = EditorState.create({ doc, selection: { anchor: cur }, extensions: [html()], }); let result = state.languageDataAt('autocomplete', cur)[0]( new CompletionContext(state, cur, !!conf.explicit), ); return result as CompletionResult | null; } describe('HTML completion', () => { it('completes tag names', () => { let c = get('<|')!.options; ist(c.length, 100, '>'); ist(!c.some((o) => /\W/.test(o.label))); }); it("doesn't complete from nothing unless explicit", () => { ist(!get('|')); }); it('completes at top level', () => { let c = get('|', { explicit: true })!.options; ist(c.length, 100, '>'); ist(c.every((o) => /^<\w+$/.test(o.label) && o.type == 'type')); }); it('completes inside an element', () => { let c = get('|', { explicit: true })!.options; ist(c.length, 100, '>'); ist(c.some((o) => o.label == '')); ist(c.every((o) => /^<(\/body>|\w+)$/.test(o.label))); }); it('completes attribute names', () => { let c = get(' o.type == 'property')); }); it('completes attribute names explicitly', () => { let c = get(' o.type == 'property')); }); it('completes attribute values', () => { let c = get('
o.label) .sort() .join(','), 'delete,get,post,put', ); }); it("completes the 2nd attribute's values", () => { let c = get(' o.label) .sort() .join(','), 'delete,get,post,put', ); }); it('keeps quotes for attribute values', () => { let c = get(' { let c = get(' o.apply) .sort() .join(','), 'delete,get,post,put', ); }); it('can handle single quotes', () => { let c = get(" o.apply) .sort() .join(','), 'delete,get,post,put', ); }); it('completes close tags', () => { let c = get(''); }); it('completes partial close tags', () => { let c = get(''); }); it("only completes close tags that haven't already been closed", () => { let c = get('

')!.options; ist(c.length, 2); ist(c.map((o) => o.apply).join(','), 'p>,div>'); }); it('includes close tag in completion after less-than', () => { let c = get('<|')!.options; ist(c.some((o) => o.apply == '/html>')); }); });