Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Console] Add tests for XJSON #136711

Merged
merged 10 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 147 additions & 11 deletions test/functional/apps/console/_xjson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
*/

import expect from '@kbn/expect';
import { rgbToHex } from '@elastic/eui';
import { FtrProviderContext } from '../../ftr_provider_context';

export default ({ getService, getPageObjects }: FtrProviderContext) => {
const retry = getService('retry');
const log = getService('log');
const PageObjects = getPageObjects(['common', 'console', 'header']);

describe("Console's XJSON features", function testXjson() {
describe('XJSON', function testXjson() {
this.tags('includeFirefox');
before(async () => {
log.debug('navigateTo console');
await PageObjects.common.navigateToApp('console');
await retry.try(async () => {
await PageObjects.console.collapseHelp();
Expand All @@ -28,22 +28,158 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
await PageObjects.console.clearTextArea();
});

describe('with triple quoted strings', () => {
const executeRequest = async (request = '\n GET _search') => {
await PageObjects.console.enterRequest(request);
await PageObjects.console.clickPlay();
await PageObjects.header.waitUntilLoadingHasFinished();
};

describe('inline http request', () => {
it('should have method and path', async () => {
await PageObjects.console.enterRequest('\n PUT foo/bar');
expect(await PageObjects.console.getRequestMethod()).to.be('PUT');
expect(await PageObjects.console.getRequestPath()).to.be('foo/bar');
});

it('should have optional query parameters', async () => {
await PageObjects.console.enterRequest('\n GET foo/bar?pretty');
expect(await PageObjects.console.getRequestQueryParams()).to.be('pretty');
});

it('should have optional request body', async () => {
await PageObjects.console.enterRequest('\n POST foo/bar\n {"foo": "bar"}');
log.debug('request body: ' + (await PageObjects.console.getRequestBody()));
expect(await PageObjects.console.getRequestBody()).to.be('{"foo": "bar"}');
});

it('should not have validation errors', async () => {
await PageObjects.console.enterRequest('\n GET foo/bar');
expect(await PageObjects.console.hasErrorMarker()).to.be(false);
});

it('should have validation error for invalid method', async () => {
await PageObjects.console.enterRequest('\n FOO foo/bar');
// Retry because the error marker is not always immediately visible.
await retry.try(async () => {
expect(await PageObjects.console.hasErrorMarker()).to.be(true);
});
});

it('should have validation error for invalid path', async () => {
await PageObjects.console.enterRequest('\n GET');
// Retry because the error marker is not always immediately visible.
await retry.try(async () => {
expect(await PageObjects.console.hasErrorMarker()).to.be(true);
});
});

it('should have validation error for invalid body', async () => {
await PageObjects.console.enterRequest('\n POST foo/bar\n {"foo": "bar"');
// Retry because the error marker is not always immediately visible.
await retry.try(async () => {
expect(await PageObjects.console.hasErrorMarker()).to.be(true);
});
});

it('should have correct syntax highlighting', async () => {
await PageObjects.console.enterRequest('\n GET foo/bar');
expect(await PageObjects.console.getRequestLineHighlighting()).to.contain(
'ace_method ace_whitespace ace_url ace_part ace_url ace_slash ace_url ace_part'
);
});

it('should have correct syntax highlighting for method', async () => {
await PageObjects.console.enterRequest('\n PUT foo/bar');
const color = await PageObjects.console.getRequestMethodColor();
expect(rgbToHex(color)).to.be('#c80a68');
});

it('should have correct syntax highlighting for path', async () => {
await PageObjects.console.enterRequest('\n PUT foo/bar');
const color = await PageObjects.console.getRequestPathColor();
expect(rgbToHex(color)).to.be('#00756c');
});

it('should have correct syntax highlighting for query', async () => {
await PageObjects.console.enterRequest('\n PUT foo/bar?pretty');
const color = await PageObjects.console.getRequestQueryColor();
expect(rgbToHex(color)).to.be('#00756c');
});

it('should have correct syntax highlighting for body', async () => {
await PageObjects.console.enterRequest('\n PUT foo/bar\n {"foo": "bar"}');
const color = await PageObjects.console.getRequestBodyColor();
expect(rgbToHex(color)).to.be('#343741');
});

it('should have multiple bodies for _msearch requests', async () => {
await PageObjects.console.enterRequest(
'\nGET foo/_msearch \n{}\n{"query": {"match_all": {}}}\n{"index": "bar"}\n{"query": {"match_all": {}}}'
);
// Retry because body elements are not always immediately visible.
await retry.try(async () => {
expect(await PageObjects.console.getRequestBodyCount()).to.be(4);
});
});

it('should not trigger error for multiple bodies for _msearch requests', async () => {
await PageObjects.console.enterRequest(
'\nGET foo/_msearch \n{}\n{"query": {"match_all": {}}}\n{"index": "bar"}\n{"query": {"match_all": {}}}'
);
// Retry until typing is finished.
await retry.try(async () => {
expect(await PageObjects.console.hasErrorMarker()).to.be(false);
});
});

it('should not trigger validation errors for multiple JSON blocks', async () => {
await PageObjects.console.enterRequest('\nPOST test/doc/1 \n{\n "foo": "bar"');
await PageObjects.console.enterRequest('\nPOST test/doc/2 \n{\n "foo": "baz"');
await PageObjects.console.enterRequest('\nPOST test/doc/3 \n{\n "foo": "qux"');
// Retry until typing is finished.
await retry.try(async () => {
expect(await PageObjects.console.hasErrorMarker()).to.be(false);
});
});

it('should allow escaping quotation mark by wrapping it in triple quotes', async () => {
await PageObjects.console.enterRequest(
'\nPOST test/_doc/1 \n{\n "foo": """look "escaped" quotes"""'
);
await PageObjects.console.clickPlay();
await PageObjects.header.waitUntilLoadingHasFinished();
// Retry until typing is finished and validation errors are gone.
await retry.try(async () => {
expect(await PageObjects.console.hasErrorMarker()).to.be(false);
});
});

it('should have correct syntax highlighting for inline comments', async () => {
await PageObjects.console.enterRequest(
'\nPOST test/_doc/1 \n{\n "foo": "bar" # inline comment'
);
const color = await PageObjects.console.getCommentColor();
expect(rgbToHex(color)).to.be('#41755c');
});

it('should allow inline comments in request url row', async () => {
await executeRequest('\n GET _search // inline comment');
expect(await PageObjects.console.hasErrorMarker()).to.be(false);
expect(await PageObjects.console.getResponseStatus()).to.eql(200);
});

it('should allow inline comments in request body', async () => {
await executeRequest('\n GET _search \n{\n "query": {\n "match_all": {} // inline comment');
expect(await PageObjects.console.hasErrorMarker()).to.be(false);
expect(await PageObjects.console.getResponseStatus()).to.eql(200);
});

it('should print warning for deprecated request', async () => {
await executeRequest('\nGET .kibana');
expect(await PageObjects.console.responseHasDeprecationWarning()).to.be(true);
});
});

describe('with invalid syntax', () => {
it('should trigger validation errors', async () => {
await PageObjects.console.enterRequest('\nGET test/doc/1 \n{\n "foo": \'\'');
expect(await PageObjects.console.hasInvalidSyntax()).to.be(true);
expect(await PageObjects.console.hasErrorMarker()).to.be(true);
it('should not print warning for non-deprecated request', async () => {
await executeRequest('\n GET _search');
expect(await PageObjects.console.responseHasDeprecationWarning()).to.be(false);
});
});
});
Expand Down
101 changes: 101 additions & 0 deletions test/functional/page_objects/console_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,19 @@ export class ConsolePageObject extends FtrService {
return await this.find.existsByCssSelector('.ace_error');
}

public async getTokenColor(token: string) {
const element = await this.find.byClassName(token);
return await element.getComputedStyle('color');
}

public async responseHasDeprecationWarning() {
// Retry for a while to allow the deprecation warning to appear
return await this.retry.try(async () => {
const response = await this.getResponse();
return response.trim().startsWith('#!');
});
}

public async clickFoldWidget() {
const widget = await this.find.byCssSelector('.ace_fold-widget');
await widget.click();
Expand Down Expand Up @@ -404,4 +417,92 @@ export class ConsolePageObject extends FtrService {
const button = await this.testSubjects.find('consoleMenuAutoIndent');
await button.click();
}

public async getRequestMethod() {
const requestEditor = await this.getRequestEditor();
const requestMethod = await requestEditor.findByClassName('ace_method');
const method = await requestMethod.getVisibleText();
return method.trim();
}

public async getRequestPath() {
const requestEditor = await this.getRequestEditor();
const requestPath = await requestEditor.findAllByCssSelector('.ace_url');
const path = [];
for (const pathPart of requestPath) {
const className = await pathPart.getAttribute('class');
if (className.includes('ace_param')) {
// This is a parameter, we don't want to include it in the path
break;
}
path.push(await pathPart.getVisibleText());
}
return path.join('').trim();
}

public async getRequestQueryParams() {
const requestEditor = await this.getRequestEditor();
const requestQueryParams = await requestEditor.findAllByCssSelector('.ace_url.ace_param');

if (requestQueryParams.length === 0) {
// No query params
return;
}

const params = [];
for (const param of requestQueryParams) {
params.push(await param.getVisibleText());
}
return params.join('').trim();
}

public async getRequestBody() {
let request = await this.getRequest();
// Remove new lines at the beginning of the request
request = request.replace(/^\n/, '');
const method = await this.getRequestMethod();
const path = await this.getRequestPath();
const query = await this.getRequestQueryParams();

if (query) {
return request.replace(`${method} ${path}?${query}`, '').trim();
}

return request.replace(`${method} ${path}`, '').trim();
}

public async getRequestLineHighlighting() {
const requestEditor = await this.getRequestEditor();
const requestLine = await requestEditor.findAllByCssSelector('.ace_line > *');
const line = [];
for (const linePart of requestLine) {
line.push(await linePart.getAttribute('class'));
}
return line.join(' ');
}

public async getRequestMethodColor() {
return await this.getTokenColor('ace_method');
}

public async getRequestPathColor() {
return await this.getTokenColor('ace_url');
}

public async getRequestQueryColor() {
return await this.getTokenColor('ace_param');
}

public async getRequestBodyColor() {
return await this.getTokenColor('ace_paren');
}

public async getCommentColor() {
return await this.getTokenColor('ace_comment');
}

public async getRequestBodyCount() {
const body = await this.getRequestBody();
return body.split('\n').length;
}
}