mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
285 lines
12 KiB
TypeScript
285 lines
12 KiB
TypeScript
import { test, expect, Page } from '../../../playwright';
|
|
import { closeAllCollections, openRequest } from '../../utils/page';
|
|
|
|
const qb = (page: Page) => page.locator('.graphql-query-builder-container');
|
|
|
|
const getQueryEditorContent = async (page: Page) => {
|
|
const editor = page.locator('[aria-label="Query Editor"] .CodeMirror').first();
|
|
await expect(editor).toBeVisible();
|
|
return await editor.evaluate((el) => (el as any).CodeMirror?.getValue() || '') as string;
|
|
};
|
|
|
|
const ensureVariablesPaneOpen = async (page: Page) => {
|
|
const variablesEditor = page.locator('.variables-section .CodeMirror').first();
|
|
if (!(await variablesEditor.isVisible())) {
|
|
await page.locator('.variables-header').click();
|
|
await expect(variablesEditor).toBeVisible();
|
|
}
|
|
};
|
|
|
|
const getVariablesEditorContent = async (page: Page) => {
|
|
await ensureVariablesPaneOpen(page);
|
|
const editor = page.locator('.variables-section .CodeMirror').first();
|
|
return await editor.evaluate((el) => (el as any).CodeMirror?.getValue() || '') as string;
|
|
};
|
|
|
|
test.describe('GraphQL Query Builder', () => {
|
|
test.afterAll(async ({ pageWithUserData: page }) => {
|
|
await closeAllCollections(page);
|
|
});
|
|
|
|
test('Select fields and generate a query', async ({ pageWithUserData: page }) => {
|
|
await test.step('Open GraphQL request, Query Builder, and load schema', async () => {
|
|
await openRequest(page, 'graphql-query-builder', 'test-graphql');
|
|
await page.locator('.tabs').waitFor({ state: 'visible' });
|
|
|
|
// Open query builder via dedicated button
|
|
if (!(await page.locator('.graphql-query-builder-container').isVisible())) {
|
|
const queryBuilderBtn = page.getByRole('tablist').locator('button[title="Show Query Builder"]');
|
|
await queryBuilderBtn.waitFor({ state: 'visible' });
|
|
await queryBuilderBtn.click();
|
|
}
|
|
|
|
await expect(qb(page)).toBeVisible();
|
|
|
|
// Load schema via introspection
|
|
const dotsMenu = page.getByRole('tablist').locator('button[title="More actions"]');
|
|
await dotsMenu.waitFor({ state: 'visible' });
|
|
await dotsMenu.click();
|
|
const introspectionItem = page.locator('[data-testid="menu-dropdown-schema-introspection"]');
|
|
await introspectionItem.waitFor({ state: 'visible' });
|
|
await introspectionItem.click();
|
|
await expect(page.getByText('GraphQL Schema loaded successfully').first()).toBeVisible();
|
|
|
|
await expect(qb(page).locator('.query-builder-tree')).toBeVisible();
|
|
});
|
|
|
|
await test.step('Click on "users" field to expand it', async () => {
|
|
const usersField = qb(page).locator('.field-node').filter({ hasText: /^users/ }).first();
|
|
await usersField.click();
|
|
await expect(qb(page).locator('.section-header').filter({ hasText: 'ARGUMENTS' }).first()).toBeVisible();
|
|
});
|
|
|
|
await test.step('Check the "users" field checkbox', async () => {
|
|
const usersCheckbox = qb(page).locator('.field-node').filter({ hasText: /^users/ }).first().locator('.field-checkbox');
|
|
await usersCheckbox.check();
|
|
await expect(usersCheckbox).toBeChecked();
|
|
});
|
|
|
|
await test.step('Check child fields: id, name, email', async () => {
|
|
const fieldsSection = qb(page).locator('.query-builder-tree');
|
|
|
|
const idField = fieldsSection.locator('.field-node').filter({ hasText: /^id/ }).first();
|
|
await idField.locator('.field-checkbox').check();
|
|
|
|
const nameField = fieldsSection.locator('.field-node').filter({ hasText: /^name/ }).first();
|
|
await nameField.locator('.field-checkbox').check();
|
|
|
|
const emailField = fieldsSection.locator('.field-node').filter({ hasText: /^email/ }).first();
|
|
await emailField.locator('.field-checkbox').check();
|
|
});
|
|
|
|
await test.step('Verify query is generated in the editor', async () => {
|
|
// Poll to allow the 150ms debounce to fire
|
|
await expect.poll(() => getQueryEditorContent(page)).toContain('id');
|
|
const editorContent = await getQueryEditorContent(page);
|
|
expect(editorContent).toContain('id');
|
|
expect(editorContent).toContain('name');
|
|
expect(editorContent).toContain('email');
|
|
});
|
|
});
|
|
|
|
test('Enable argument and set value', async ({ pageWithUserData: page }) => {
|
|
await test.step('Expand "user" field to show arguments', async () => {
|
|
const userField = qb(page).locator('.field-node').filter({ hasText: /^user\b/ }).first();
|
|
await userField.click();
|
|
await expect(qb(page).locator('.section-header').filter({ hasText: 'ARGUMENTS' }).first()).toBeVisible();
|
|
});
|
|
|
|
await test.step('Check the "user" field', async () => {
|
|
const userCheckbox = qb(page).locator('.field-node').filter({ hasText: /^user\b/ }).first().locator('.field-checkbox');
|
|
await userCheckbox.check();
|
|
});
|
|
|
|
await test.step('Enable the "id" argument and set a value', async () => {
|
|
const argRow = qb(page).locator('.arg-row').filter({ hasText: /^id/ }).first();
|
|
const argCheckbox = argRow.locator('.field-checkbox');
|
|
await argCheckbox.check();
|
|
await expect(argCheckbox).toBeChecked();
|
|
|
|
const argInput = argRow.locator('input[type="text"]');
|
|
await expect(argInput).toBeVisible();
|
|
await argInput.fill('123');
|
|
});
|
|
|
|
await test.step('Check child field "name"', async () => {
|
|
const nameField = qb(page).locator('.field-node').filter({ hasText: /^name/ }).first();
|
|
await nameField.locator('.field-checkbox').check();
|
|
});
|
|
|
|
await test.step('Verify generated query contains the argument', async () => {
|
|
await expect.poll(() => getQueryEditorContent(page)).toContain('$id');
|
|
const editorContent = await getQueryEditorContent(page);
|
|
expect(editorContent).toContain('name');
|
|
expect(editorContent).toContain('$id');
|
|
});
|
|
|
|
await test.step('Verify variables pane contains the argument value', async () => {
|
|
const variablesContent = await getVariablesEditorContent(page);
|
|
expect(variablesContent).toContain('"id"');
|
|
expect(variablesContent).toContain('"123"');
|
|
});
|
|
});
|
|
|
|
test('Expand nested object types', async ({ pageWithUserData: page }) => {
|
|
await test.step('Expand "post" field', async () => {
|
|
const postField = qb(page).locator('.field-node').filter({ hasText: /^post/ }).first();
|
|
await postField.click();
|
|
});
|
|
|
|
await test.step('Check "post" and expand "author" nested field', async () => {
|
|
const postCheckbox = qb(page).locator('.field-node').filter({ hasText: /^post/ }).first().locator('.field-checkbox');
|
|
await postCheckbox.check();
|
|
|
|
const titleField = qb(page).locator('.field-node').filter({ hasText: /^title/ }).first();
|
|
await titleField.locator('.field-checkbox').check();
|
|
|
|
const authorField = qb(page).locator('.field-node').filter({ hasText: /^author/ }).first();
|
|
await authorField.click();
|
|
});
|
|
|
|
await test.step('Select nested author fields', async () => {
|
|
const authorCheckbox = qb(page).locator('.field-node').filter({ hasText: /^author/ }).first().locator('.field-checkbox');
|
|
await authorCheckbox.check();
|
|
|
|
const nameFields = qb(page).locator('.field-node').filter({ hasText: /^name/ });
|
|
const authorNameField = nameFields.nth(1);
|
|
await authorNameField.locator('.field-checkbox').check();
|
|
});
|
|
|
|
await test.step('Verify nested query structure in editor', async () => {
|
|
await expect.poll(() => getQueryEditorContent(page)).toContain('post');
|
|
const editorContent = await getQueryEditorContent(page);
|
|
expect(editorContent).toContain('title');
|
|
expect(editorContent).toContain('author');
|
|
expect(editorContent).toContain('name');
|
|
});
|
|
});
|
|
|
|
test('Removing a field in code editor unchecks it in query builder', async ({ pageWithUserData: page }) => {
|
|
await test.step('Ensure "users" is expanded with child fields id, name, email checked', async () => {
|
|
const usersField = qb(page).locator('.field-node').filter({ hasText: /^users/ }).first();
|
|
const usersChildrenVisible = await qb(page).locator('.field-node').filter({ hasText: /^email/ }).first().isVisible();
|
|
if (!usersChildrenVisible) {
|
|
await usersField.click();
|
|
}
|
|
|
|
const usersCheckbox = usersField.locator('.field-checkbox');
|
|
if (!(await usersCheckbox.isChecked())) {
|
|
await usersCheckbox.check();
|
|
}
|
|
|
|
const fieldsSection = qb(page).locator('.query-builder-tree');
|
|
for (const fieldName of ['id', 'name', 'email']) {
|
|
const field = fieldsSection.locator('.field-node').filter({ hasText: new RegExp(`^${fieldName}`) }).first();
|
|
const checkbox = field.locator('.field-checkbox');
|
|
if (!(await checkbox.isChecked())) {
|
|
await checkbox.check();
|
|
}
|
|
}
|
|
|
|
await expect.poll(() => getQueryEditorContent(page)).toContain('email');
|
|
await page.waitForTimeout(200);
|
|
});
|
|
|
|
await test.step('Remove "email" field from the code editor', async () => {
|
|
const content = await getQueryEditorContent(page);
|
|
const updatedContent = content
|
|
.split('\n')
|
|
.filter((line: string) => !line.trim().startsWith('email'))
|
|
.join('\n');
|
|
|
|
// Set content directly via CodeMirror
|
|
const editor = page.locator('[aria-label="Query Editor"] .CodeMirror').first();
|
|
await editor.evaluate((el, val) => {
|
|
const cm = (el as any).CodeMirror;
|
|
if (cm) cm.setValue(val);
|
|
}, updatedContent);
|
|
});
|
|
|
|
await test.step('Verify "email" checkbox is unchecked in query builder', async () => {
|
|
const fieldsSection = qb(page).locator('.query-builder-tree');
|
|
const emailCheckbox = fieldsSection
|
|
.locator('.field-node')
|
|
.filter({ hasText: /^email/ })
|
|
.first()
|
|
.locator('.field-checkbox');
|
|
await expect(emailCheckbox).not.toBeChecked();
|
|
});
|
|
|
|
await test.step('Verify "id" and "name" are still checked', async () => {
|
|
const fieldsSection = qb(page).locator('.query-builder-tree');
|
|
|
|
const idCheckbox = fieldsSection.locator('.field-node').filter({ hasText: /^id/ }).first().locator('.field-checkbox');
|
|
await expect(idCheckbox).toBeChecked();
|
|
|
|
const nameCheckbox = fieldsSection
|
|
.locator('.field-node')
|
|
.filter({ hasText: /^name/ })
|
|
.first()
|
|
.locator('.field-checkbox');
|
|
await expect(nameCheckbox).toBeChecked();
|
|
});
|
|
});
|
|
|
|
test('Changing variable value in variables editor updates argument in query builder', async ({
|
|
pageWithUserData: page
|
|
}) => {
|
|
await test.step('Set up "user" field with "id" argument via query builder', async () => {
|
|
const userField = qb(page).locator('.field-node').filter({ hasText: /^user\b/ }).first();
|
|
await expect(qb(page).locator('.section-header').filter({ hasText: 'ARGUMENTS' }).first()).toBeVisible();
|
|
|
|
const userCheckbox = userField.locator('.field-checkbox');
|
|
if (!(await userCheckbox.isChecked())) {
|
|
await userCheckbox.check();
|
|
}
|
|
|
|
const argRow = qb(page).locator('.arg-row').filter({ hasText: /^id/ }).first();
|
|
const argCheckbox = argRow.locator('.field-checkbox');
|
|
if (!(await argCheckbox.isChecked())) {
|
|
await argCheckbox.check();
|
|
}
|
|
const argInput = argRow.locator('input[type="text"]');
|
|
await argInput.fill('100');
|
|
|
|
const nameField = qb(page).locator('.field-node').filter({ hasText: /^name/ }).first();
|
|
const nameCheckbox = nameField.locator('.field-checkbox');
|
|
if (!(await nameCheckbox.isChecked())) {
|
|
await nameCheckbox.check();
|
|
}
|
|
|
|
await expect.poll(() => getQueryEditorContent(page)).toContain('$id');
|
|
await expect.poll(() => getVariablesEditorContent(page)).toContain('"100"');
|
|
});
|
|
|
|
await test.step('Change the variable value in the variables editor', async () => {
|
|
const variablesContent = await getVariablesEditorContent(page);
|
|
const updatedVariables = variablesContent.replace('"100"', '"999"');
|
|
|
|
// Set content directly via CodeMirror
|
|
await ensureVariablesPaneOpen(page);
|
|
const editor = page.locator('.variables-section .CodeMirror').first();
|
|
await editor.evaluate((el, val) => {
|
|
const cm = (el as any).CodeMirror;
|
|
if (cm) cm.setValue(val);
|
|
}, updatedVariables);
|
|
});
|
|
|
|
await test.step('Verify the argument value is updated in query builder', async () => {
|
|
const argRow = qb(page).locator('.arg-row').filter({ hasText: /^id/ }).first();
|
|
const argInput = argRow.locator('input[type="text"]');
|
|
await expect(argInput).toHaveValue('999');
|
|
});
|
|
});
|
|
});
|