mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-06 04:21:09 +00:00
510 lines
14 KiB
TypeScript
510 lines
14 KiB
TypeScript
import { outdent } from 'outdent';
|
||
|
||
import { getLineColLocation, getCodeframe } from '../src/format/codeframes';
|
||
import { LocationObject } from '../src/walk';
|
||
import { Source } from '../src/resolve';
|
||
|
||
describe('Location', () => {
|
||
it('should correctly calculate location for key', () => {
|
||
const loc = {
|
||
reportOnKey: true,
|
||
pointer: '#/info/license',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
const preciseLocation = getLineColLocation(loc);
|
||
expect(preciseLocation.start).toEqual({ line: 3, col: 3 });
|
||
expect(preciseLocation.end).toEqual({ line: 3, col: 10 });
|
||
});
|
||
|
||
it('should correctly calculate location for key on top level', () => {
|
||
const loc = {
|
||
reportOnKey: true,
|
||
pointer: '#/info',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
const preciseLocation = getLineColLocation(loc);
|
||
expect(preciseLocation.start).toEqual({ line: 2, col: 1 });
|
||
expect(preciseLocation.end).toEqual({ line: 2, col: 5 });
|
||
});
|
||
|
||
it('should correctly calculate location for value', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/license/name',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
const preciseLocation = getLineColLocation(loc);
|
||
expect(preciseLocation.start).toEqual({ line: 4, col: 11 });
|
||
expect(preciseLocation.end).toEqual({ line: 4, col: 14 });
|
||
});
|
||
|
||
it('should correctly fallback to the closest parent node if pointer is incorrect', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/missing',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
const preciseLocation = getLineColLocation(loc);
|
||
expect(preciseLocation.start).toEqual({ line: 3, col: 3 });
|
||
expect(preciseLocation.end).toEqual({ line: 5, col: 28 });
|
||
});
|
||
|
||
it('should return first line for empty doc', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/missing',
|
||
source: new Source('foobar.yaml', ''),
|
||
};
|
||
const preciseLocation = getLineColLocation(loc);
|
||
expect(preciseLocation.start).toEqual({ line: 1, col: 1 });
|
||
expect(preciseLocation.end).toEqual({ line: 1, col: 1 });
|
||
});
|
||
|
||
it('should return full range for file with newlines', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/missing',
|
||
source: new Source('foobar.yaml', '\n\n\n'),
|
||
};
|
||
const preciseLocation = getLineColLocation(loc);
|
||
expect(preciseLocation.start).toEqual({ line: 1, col: 1 });
|
||
expect(preciseLocation.end).toEqual({ line: 1, col: 1 });
|
||
});
|
||
});
|
||
|
||
describe('codeframes', () => {
|
||
function getColorCodeframe(loc: LocationObject) {
|
||
return getCodeframe(getLineColLocation(loc), true)
|
||
.replace(/\x1b\[90m(.*?)\x1b\[39m/g, '<g>$1</g>')
|
||
.replace(/\x1b\[31m(.*?)\x1b\[39m/g, '<r>$1</r>');
|
||
}
|
||
|
||
function getPlainCodeframe(loc: LocationObject) {
|
||
return getCodeframe(getLineColLocation(loc), false);
|
||
}
|
||
|
||
expect.addSnapshotSerializer({
|
||
test: (val) => typeof val === 'string',
|
||
print: (v) => v as string,
|
||
});
|
||
|
||
it('should correctly generate simple codeframe', () => {
|
||
const loc = {
|
||
reportOnKey: true,
|
||
pointer: '#/info/license',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | openapi: 3.0.2
|
||
2 | info:
|
||
3 | license:
|
||
| ^^^^^^^
|
||
4 | name: MIT
|
||
5 | url: https://google.com
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>1 |</g> <g>openapi: 3.0.2</g>
|
||
<g>2 |</g> <g>info:</g>
|
||
<g>3 |</g> <r>license</r>:
|
||
<g>4 |</g> <g>name: MIT</g>
|
||
<g>5 |</g> <g>url: https://google.com</g>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame for single-line file on key', () => {
|
||
const loc = {
|
||
reportOnKey: true,
|
||
pointer: '#/openapi',
|
||
source: new Source('foobar.yaml', `openapi: 3.0.2`),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | openapi: 3.0.2
|
||
| ^^^^^^^
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`<g>1 |</g> <r>openapi</r>: 3.0.2`);
|
||
});
|
||
|
||
it('correctly generate code-frame for single-line file on value', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/openapi',
|
||
source: new Source('foobar.yaml', `openapi: 3.0.2`),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | openapi: 3.0.2
|
||
| ^^^^^
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`<g>1 |</g> openapi: <r>3.0.2</r>`);
|
||
});
|
||
|
||
it('correctly generate code-frame for multiline-value', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/license',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
2 | info:
|
||
3 | license:
|
||
4 | name: MIT
|
||
| ^^^^^^^^^
|
||
5 | url: https://google.com
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>2 |</g> <g>info:</g>
|
||
<g>3 |</g> <g>license:</g>
|
||
<g>4 |</g> <r>name: MIT</r>
|
||
<g>5 |</g> <r>url: https://google.com</r>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame for multiline-value json-like', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/license',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license: {
|
||
name: MIT,
|
||
url: https://google.com
|
||
}
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | openapi: 3.0.2
|
||
2 | info:
|
||
3 | license: {
|
||
| ^
|
||
4 | name: MIT,
|
||
| ^^^^^^^^^^
|
||
5 | url: https://google.com
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||
6 | }
|
||
| ^
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>1 |</g> <g>openapi: 3.0.2</g>
|
||
<g>2 |</g> <g>info:</g>
|
||
<g>3 |</g> license: <r>{</r>
|
||
<g>4 |</g> <r>name: MIT,</r>
|
||
<g>5 |</g> <r>url: https://google.com</r>
|
||
<g>6 |</g> <r>}</r>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame for multiline-value and show only 5 lines', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/license',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
info:
|
||
license:
|
||
name: MIT
|
||
field2: MIT
|
||
field3: MIT
|
||
field4: MIT
|
||
field5: MIT
|
||
url: https://google.com
|
||
openapi: 3.0.2
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | info:
|
||
2 | license:
|
||
3 | name: MIT
|
||
| ^^^^^^^^^
|
||
4 | field2: MIT
|
||
| ^^^^^^^^^^^
|
||
… | < 3 more lines >
|
||
8 | url: https://google.com
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||
9 | openapi: 3.0.2
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>1 |</g> <g>info:</g>
|
||
<g>2 |</g> <g>license:</g>
|
||
<g>3 |</g> <r>name: MIT</r>
|
||
<g>4 |</g> <r>field2: MIT</r>
|
||
<g>… |</g> <g>< 3 more lines ></g>
|
||
<g>8 |</g> <r>url: https://google.com</r>
|
||
<g>9 |</g> <g>openapi: 3.0.2</g>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame for multiline-value and show only 5 lines with empty lines', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/license',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
info:
|
||
|
||
license:
|
||
name: MIT
|
||
|
||
field2: MIT
|
||
field3: MIT
|
||
field4: MIT
|
||
field5: MIT
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
2 |
|
||
3 | license:
|
||
4 | name: MIT
|
||
| ^^^^^^^^^
|
||
5 |
|
||
| ^
|
||
… | < 3 more lines >
|
||
9 | field5: MIT
|
||
| ^^^^^^^^^^^
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>2 |</g>
|
||
<g>3 |</g> <g>license:</g>
|
||
<g>4 |</g> <r>name: MIT</r>
|
||
<g>5 |</g>
|
||
<g>… |</g> <g>< 3 more lines ></g>
|
||
<g>9 |</g> <r>field5: MIT</r>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame dedent', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/paths/~1pet/put/parameters/0/in',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
paths:
|
||
'/pet':
|
||
put:
|
||
operationId: test
|
||
parameters:
|
||
- name: a
|
||
in: wrong
|
||
post:
|
||
operationId: test2
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
10 | parameters:
|
||
11 | - name: a
|
||
12 | in: wrong
|
||
| ^^^^^
|
||
13 | post:
|
||
14 | operationId: test2
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>10 |</g> <g>parameters:</g>
|
||
<g>11 |</g> <g>- name: a</g>
|
||
<g>12 |</g> in: <r>wrong</r>
|
||
<g>13 |</g> <g>post:</g>
|
||
<g>14 |</g> <g>operationId: test2</g>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame for too long line', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/description',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
info:
|
||
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | openapi: 3.0.2
|
||
2 | info:
|
||
3 | description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua...<77 chars>
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...<77 chars>
|
||
4 | license:
|
||
5 | name: MIT
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>1 |</g> <g>openapi: 3.0.2</g>
|
||
<g>2 |</g> <g>info:</g>
|
||
<g>3 |</g> description: [31mLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolo<g>...<87 chars></g>
|
||
<g>4 |</g> <g>license:</g>
|
||
<g>5 |</g> <g>name: MIT</g>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame for empty file', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/info/description',
|
||
source: new Source('foobar.yaml', `\n\n`),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 |
|
||
| ^
|
||
2 |
|
||
3 |
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>1 |</g>
|
||
<g>2 |</g>
|
||
<g>3 |</g>
|
||
`);
|
||
});
|
||
|
||
it('correctly generate code-frame for whole file and correctly skip newlines', () => {
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: '#/',
|
||
source: new Source(
|
||
'foobar.yaml',
|
||
outdent`
|
||
openapi: 3.0.2
|
||
|
||
info:
|
||
|
||
description: Lorem ipsum
|
||
license:
|
||
name: MIT
|
||
url: https://google.com
|
||
`,
|
||
),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | openapi: 3.0.2
|
||
| ^^^^^^^^^^^^^^
|
||
2 |
|
||
| ^
|
||
… | < 5 more lines >
|
||
8 | url: https://google.com
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>1 |</g> <r>openapi: 3.0.2</r>
|
||
<g>2 |</g>
|
||
<g>… |</g> <g>< 5 more lines ></g>
|
||
<g>8 |</g> <r>url: https://google.com</r>
|
||
`);
|
||
});
|
||
|
||
it('should show line even if there are not so many file in the file', () => {
|
||
// (yaml parser sometimes shows error on the next after last line
|
||
const loc = {
|
||
reportOnKey: false,
|
||
pointer: undefined,
|
||
start: {
|
||
line: 2,
|
||
col: 1,
|
||
},
|
||
source: new Source('foobar.yaml', outdent`openapi: 3.0.2`),
|
||
};
|
||
|
||
expect(getPlainCodeframe(loc)).toMatchInlineSnapshot(`
|
||
1 | openapi: 3.0.2
|
||
2 |
|
||
| ^
|
||
`);
|
||
|
||
expect(getColorCodeframe(loc)).toMatchInlineSnapshot(`
|
||
<g>1 |</g> <g>openapi: 3.0.2</g>
|
||
<g>2 |</g>
|
||
`);
|
||
});
|
||
});
|