mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-26 22:35:18 +02:00
136 lines
3.2 KiB
TypeScript
136 lines
3.2 KiB
TypeScript
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<CompletionSource>('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('<body>|', { explicit: true })!.options;
|
|
ist(c.length, 100, '>');
|
|
ist(c.some((o) => o.label == '</body>'));
|
|
ist(c.every((o) => /^<(\/body>|\w+)$/.test(o.label)));
|
|
});
|
|
|
|
it('completes attribute names', () => {
|
|
let c = get('<body f|')!.options;
|
|
ist(c.length);
|
|
ist(c.every((o) => o.type == 'property'));
|
|
});
|
|
|
|
it('completes attribute names explicitly', () => {
|
|
let c = get('<body |', { explicit: true })!.options;
|
|
ist(c.length);
|
|
ist(c.every((o) => o.type == 'property'));
|
|
});
|
|
|
|
it('completes attribute values', () => {
|
|
let c = get('<form method=|')!.options;
|
|
ist(
|
|
c
|
|
.map((o) => o.label)
|
|
.sort()
|
|
.join(','),
|
|
'delete,get,post,put',
|
|
);
|
|
});
|
|
|
|
it("completes the 2nd attribute's values", () => {
|
|
let c = get('<form lang=en method=|')!.options;
|
|
ist(
|
|
c
|
|
.map((o) => o.label)
|
|
.sort()
|
|
.join(','),
|
|
'delete,get,post,put',
|
|
);
|
|
});
|
|
|
|
it('keeps quotes for attribute values', () => {
|
|
let c = get('<form method="|')!.options;
|
|
ist(
|
|
c
|
|
.map((o) => o.apply)
|
|
.sort()
|
|
.join(','),
|
|
'delete",get",post",put"',
|
|
);
|
|
});
|
|
|
|
it('omits already closed quotes', () => {
|
|
let c = get('<form method="|"')!;
|
|
ist(c.to, 14);
|
|
ist(
|
|
c.options
|
|
.map((o) => o.apply)
|
|
.sort()
|
|
.join(','),
|
|
'delete,get,post,put',
|
|
);
|
|
});
|
|
|
|
it('can handle single quotes', () => {
|
|
let c = get("<form method='|'")!;
|
|
ist(c.to, 14);
|
|
ist(
|
|
c.options
|
|
.map((o) => o.apply)
|
|
.sort()
|
|
.join(','),
|
|
'delete,get,post,put',
|
|
);
|
|
});
|
|
|
|
it('completes close tags', () => {
|
|
let c = get('<body></|')!.options;
|
|
ist(c.length, 1);
|
|
ist(c[0].apply, 'body>');
|
|
});
|
|
|
|
it('completes partial close tags', () => {
|
|
let c = get('<body></b|')!.options;
|
|
ist(c.length, 1);
|
|
ist(c[0].apply, 'body>');
|
|
});
|
|
|
|
it("only completes close tags that haven't already been closed", () => {
|
|
let c = get('<html><body><div><p></|</body></html>')!.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('<html><|')!.options;
|
|
ist(c.some((o) => o.apply == '/html>'));
|
|
});
|
|
});
|