mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-23 12:45:38 +00:00
fix: unable to add assertions to a request (#6435)
* fix: add assertion * rm: unnecessary wait fn * fix: test * fix: tests * fix: review comments * fix: review * fix: review comments * fix: review comments * fix: test failure * review fixes * fix: rm sandbox accept * fix: indentation
This commit is contained in:
@@ -16,7 +16,8 @@ const EditableTable = ({
|
||||
checkboxKey = 'enabled',
|
||||
reorderable = false,
|
||||
onReorder,
|
||||
showAddRow = true
|
||||
showAddRow = true,
|
||||
testId = 'editable-table'
|
||||
}) => {
|
||||
const tableRef = useRef(null);
|
||||
const emptyRowUidRef = useRef(null);
|
||||
@@ -224,7 +225,7 @@ const EditableTable = ({
|
||||
|
||||
return (
|
||||
<StyledWrapper className={showCheckbox ? 'has-checkbox' : 'no-checkbox'}>
|
||||
<div className="table-container" ref={tableRef}>
|
||||
<div className="table-container" ref={tableRef} data-testid={testId}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -285,6 +286,7 @@ const EditableTable = ({
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mousetrap"
|
||||
data-testid="column-checkbox"
|
||||
checked={row[checkboxKey] ?? true}
|
||||
onChange={(e) => handleCheckboxChange(row.uid, e.target.checked)}
|
||||
/>
|
||||
@@ -292,14 +294,17 @@ const EditableTable = ({
|
||||
</td>
|
||||
)}
|
||||
{columns.map((column) => (
|
||||
<td key={column.key}>
|
||||
<td key={column.key} data-testid={`column-${column.key}`}>
|
||||
{renderCell(column, row, rowIndex)}
|
||||
</td>
|
||||
))}
|
||||
{showDelete && (
|
||||
<td>
|
||||
{!isEmpty && (
|
||||
<button onClick={() => handleRemoveRow(row.uid)}>
|
||||
<button
|
||||
data-testid="column-delete"
|
||||
onClick={() => handleRemoveRow(row.uid)}
|
||||
>
|
||||
<IconTrash strokeWidth={1.5} size={18} />
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -81,7 +81,7 @@ const AssertionOperator = ({ operator, onChange }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<select value={operator} onChange={handleChange} className="mousetrap">
|
||||
<select value={operator} onChange={handleChange} className="mousetrap" data-testid="assertion-operator-select">
|
||||
{operators.map((operator) => (
|
||||
<option key={operator} value={operator}>
|
||||
{getLabel(operator)}
|
||||
|
||||
@@ -163,6 +163,7 @@ const Assertions = ({ item, collection }) => {
|
||||
defaultRow={defaultRow}
|
||||
reorderable={true}
|
||||
onReorder={handleAssertionDrag}
|
||||
testId="assertions-table"
|
||||
/>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -33,6 +33,46 @@ const keyValueSchema = Yup.object({
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
||||
const assertionOperators = [
|
||||
'eq',
|
||||
'neq',
|
||||
'gt',
|
||||
'gte',
|
||||
'lt',
|
||||
'lte',
|
||||
'in',
|
||||
'notIn',
|
||||
'contains',
|
||||
'notContains',
|
||||
'length',
|
||||
'matches',
|
||||
'notMatches',
|
||||
'startsWith',
|
||||
'endsWith',
|
||||
'between',
|
||||
'isEmpty',
|
||||
'isNotEmpty',
|
||||
'isNull',
|
||||
'isUndefined',
|
||||
'isDefined',
|
||||
'isTruthy',
|
||||
'isFalsy',
|
||||
'isJson',
|
||||
'isNumber',
|
||||
'isString',
|
||||
'isBoolean',
|
||||
'isArray'
|
||||
];
|
||||
|
||||
const assertionSchema = keyValueSchema.shape({
|
||||
operator: Yup.string()
|
||||
.oneOf(assertionOperators)
|
||||
.nullable()
|
||||
.optional()
|
||||
})
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
||||
const varsSchema = Yup.object({
|
||||
uid: uidSchema,
|
||||
name: Yup.string().nullable(),
|
||||
@@ -372,7 +412,7 @@ const requestSchema = Yup.object({
|
||||
.noUnknown(true)
|
||||
.strict()
|
||||
.nullable(),
|
||||
assertions: Yup.array().of(keyValueSchema).nullable(),
|
||||
assertions: Yup.array().of(assertionSchema).nullable(),
|
||||
tests: Yup.string().nullable(),
|
||||
docs: Yup.string().nullable()
|
||||
})
|
||||
@@ -408,7 +448,7 @@ const grpcRequestSchema = Yup.object({
|
||||
.noUnknown(true)
|
||||
.strict()
|
||||
.nullable(),
|
||||
assertions: Yup.array().of(keyValueSchema).nullable(),
|
||||
assertions: Yup.array().of(assertionSchema).nullable(),
|
||||
tests: Yup.string().nullable(),
|
||||
docs: Yup.string().nullable(),
|
||||
})
|
||||
@@ -446,7 +486,7 @@ const wsRequestSchema = Yup.object({
|
||||
.noUnknown(true)
|
||||
.strict()
|
||||
.nullable(),
|
||||
assertions: Yup.array().of(keyValueSchema).nullable(),
|
||||
assertions: Yup.array().of(assertionSchema).nullable(),
|
||||
tests: Yup.string().nullable(),
|
||||
docs: Yup.string().nullable()
|
||||
})
|
||||
|
||||
279
tests/asserts/add-assertions.spec.ts
Normal file
279
tests/asserts/add-assertions.spec.ts
Normal file
@@ -0,0 +1,279 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import {
|
||||
closeAllCollections,
|
||||
openCollection,
|
||||
openRequest,
|
||||
selectRequestPaneTab,
|
||||
sendRequest,
|
||||
selectEnvironment,
|
||||
addAssertion,
|
||||
editAssertion,
|
||||
deleteAssertion,
|
||||
saveRequest
|
||||
} from '../utils/page';
|
||||
import { buildCommonLocators } from '../utils/page/locators';
|
||||
|
||||
test.describe('Assertions - BRU Collection', () => {
|
||||
test.beforeAll(async ({ pageWithUserData: page }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
await test.step('Navigate to assertions tab', async () => {
|
||||
await openCollection(page, 'test-assertions-bru');
|
||||
await selectEnvironment(page, 'Local', 'collection');
|
||||
await openRequest(page, 'test-assertions-bru', 'ping');
|
||||
await selectRequestPaneTab(page, 'Assert');
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
// Ensure we're on the Assertions tab
|
||||
await selectRequestPaneTab(page, 'Assert');
|
||||
|
||||
// Wait for table to be visible
|
||||
await expect(table.container()).toBeVisible();
|
||||
|
||||
// Get all rows and delete assertions (skip the empty row at the end)
|
||||
let rowCount = await table.allRows().count();
|
||||
|
||||
// Keep deleting assertions until only the empty row remains
|
||||
// We delete from the end to avoid index shifting issues
|
||||
while (rowCount > 1) {
|
||||
const deleteButton = table.rowDeleteButton(rowCount - 2); // Second to last (skip empty row)
|
||||
|
||||
await expect(deleteButton).toBeVisible({ timeout: 1000 });
|
||||
await deleteButton.click();
|
||||
// Wait for row count to decrease after deletion
|
||||
await expect(table.allRows()).toHaveCount(rowCount - 1);
|
||||
rowCount = await table.allRows().count(); // Re-count rows
|
||||
}
|
||||
|
||||
// Save the request to persist the clean state
|
||||
// saveRequest already waits for the "Request saved successfully" toast internally
|
||||
await saveRequest(page);
|
||||
});
|
||||
|
||||
test.afterAll(async ({ pageWithUserData: page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('should add assertion to request, verify toast, and run request successfully', async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Add assertion to the request', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.body',
|
||||
value: 'pong'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Save request and verify success toast', async () => {
|
||||
await saveRequest(page);
|
||||
});
|
||||
|
||||
await test.step('Send request and verify response', async () => {
|
||||
await sendRequest(page, 200);
|
||||
|
||||
// Verify response status
|
||||
await expect(locators.response.statusCode()).toContainText('200');
|
||||
|
||||
// Verify response body contains "pong"
|
||||
await expect(locators.response.body()).toContainText('pong', { timeout: 5000 });
|
||||
});
|
||||
|
||||
await test.step('Delete assertion and save', async () => {
|
||||
// Navigate back to Assertions tab
|
||||
await selectRequestPaneTab(page, 'Assert');
|
||||
|
||||
// Delete the assertion at row 0 (first data row)
|
||||
await deleteAssertion(page, 0);
|
||||
|
||||
// Save the request
|
||||
await saveRequest(page);
|
||||
});
|
||||
});
|
||||
|
||||
test('should add multiple assertions', async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
await test.step('Add first assertion', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.status',
|
||||
value: '200'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Add second assertion', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.body',
|
||||
value: 'pong'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Add third assertion', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.responseTime',
|
||||
value: '1000'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Verify all assertions are present', async () => {
|
||||
// Check input values instead of cell text content
|
||||
await expect(table.rowExprInput(0)).toHaveValue('res.status');
|
||||
await expect(table.rowExprInput(1)).toHaveValue('res.body');
|
||||
await expect(table.rowExprInput(2)).toHaveValue('res.responseTime');
|
||||
});
|
||||
|
||||
await test.step('Save request', async () => {
|
||||
await saveRequest(page);
|
||||
});
|
||||
});
|
||||
|
||||
test('should edit an existing assertion', async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
await test.step('Add initial assertion', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.body',
|
||||
value: 'ping'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Edit the assertion', async () => {
|
||||
await editAssertion(page, 0, {
|
||||
expr: 'res.status',
|
||||
value: '200'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Verify assertion was updated', async () => {
|
||||
await expect(table.rowExprInput(0)).toHaveValue('res.status');
|
||||
// The value cell might contain the operator, so we check it contains our value
|
||||
const valueCell = table.rowCell('value', 0);
|
||||
await expect(valueCell).toContainText('200');
|
||||
});
|
||||
|
||||
await test.step('Save request', async () => {
|
||||
await saveRequest(page);
|
||||
});
|
||||
});
|
||||
|
||||
test('should toggle assertion checkbox (enable/disable)', async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
await test.step('Add assertion', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.status',
|
||||
value: '200'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Verify checkbox is checked by default', async () => {
|
||||
const checkbox = table.rowCheckbox(0);
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
await test.step('Uncheck the assertion', async () => {
|
||||
const checkbox = table.rowCheckbox(0);
|
||||
await checkbox.uncheck();
|
||||
});
|
||||
|
||||
await test.step('Verify checkbox is unchecked', async () => {
|
||||
const checkbox = table.rowCheckbox(0);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
});
|
||||
|
||||
await test.step('Re-check the assertion', async () => {
|
||||
const checkbox = table.rowCheckbox(0);
|
||||
await checkbox.check();
|
||||
});
|
||||
|
||||
await test.step('Verify checkbox is checked again', async () => {
|
||||
const checkbox = table.rowCheckbox(0);
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
await test.step('Save request', async () => {
|
||||
await saveRequest(page);
|
||||
});
|
||||
});
|
||||
|
||||
test('should delete multiple assertions', async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
await test.step('Add multiple assertions', async () => {
|
||||
await addAssertion(page, { expr: 'res.status', value: '200' });
|
||||
await addAssertion(page, { expr: 'res.body', value: 'pong' });
|
||||
await addAssertion(page, { expr: 'res.responseTime', value: '1000' });
|
||||
});
|
||||
|
||||
await test.step('Verify three assertions exist', async () => {
|
||||
const rowCount = await table.allRows().count();
|
||||
expect(rowCount).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
|
||||
await test.step('Delete first assertion', async () => {
|
||||
await deleteAssertion(page, 0);
|
||||
});
|
||||
|
||||
await test.step('Delete second assertion (now at index 0 after first deletion)', async () => {
|
||||
await deleteAssertion(page, 0);
|
||||
});
|
||||
|
||||
await test.step('Verify only one assertion remains', async () => {
|
||||
const rowCount = await table.allRows().count();
|
||||
// Should have at least 1 assertion row + 1 empty row
|
||||
expect(rowCount).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
await test.step('Save request', async () => {
|
||||
await saveRequest(page);
|
||||
});
|
||||
});
|
||||
|
||||
test('should add assertion with different operators', async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
await test.step('Add assertion with contains operator', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.body',
|
||||
value: 'pong',
|
||||
operator: 'contains'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Add assertion with greater than operator', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.status',
|
||||
value: '199',
|
||||
operator: 'gt'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Add assertion with length operator', async () => {
|
||||
await addAssertion(page, {
|
||||
expr: 'res.body',
|
||||
value: '4',
|
||||
operator: 'length'
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Verify assertions with different operators exist', async () => {
|
||||
await expect(table.rowExprInput(0)).toHaveValue('res.body');
|
||||
await expect(table.rowExprInput(1)).toHaveValue('res.status');
|
||||
await expect(table.rowExprInput(2)).toHaveValue('res.body');
|
||||
});
|
||||
|
||||
await test.step('Save request', async () => {
|
||||
await saveRequest(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
6
tests/asserts/fixtures/collection/bruno.json
Normal file
6
tests/asserts/fixtures/collection/bruno.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "test-assertions-bru",
|
||||
"type": "collection",
|
||||
"uid": "test-assertions-bru-uid"
|
||||
}
|
||||
3
tests/asserts/fixtures/collection/environments/Local.bru
Normal file
3
tests/asserts/fixtures/collection/environments/Local.bru
Normal file
@@ -0,0 +1,3 @@
|
||||
vars {
|
||||
host: https://testbench-sanity.usebruno.com
|
||||
}
|
||||
11
tests/asserts/fixtures/collection/ping.bru
Normal file
11
tests/asserts/fixtures/collection/ping.bru
Normal file
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: ping
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{host}}/ping
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
5
tests/asserts/init-user-data/preferences.json
Normal file
5
tests/asserts/init-user-data/preferences.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"lastOpenedCollections": [
|
||||
"{{projectRoot}}/tests/asserts/fixtures/collection"
|
||||
]
|
||||
}
|
||||
@@ -705,6 +705,143 @@ const clickResponseAction = async (page: Page, actionTestId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
type AssertionInput = {
|
||||
expr: string;
|
||||
value: string;
|
||||
operator?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an assertion to the current request (adds to the last empty row)
|
||||
* @param page - The page object
|
||||
* @param assertion - The assertion to add (expr, value, optional operator)
|
||||
* @returns The row index where the assertion was added
|
||||
*/
|
||||
const addAssertion = async (page: Page, assertion: AssertionInput): Promise<number> => {
|
||||
const operator = assertion.operator || 'eq';
|
||||
|
||||
return await test.step(`Add assertion: ${assertion.expr} ${operator} ${assertion.value}`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
// Ensure assertions table is visible
|
||||
await expect(table.container()).toBeVisible();
|
||||
|
||||
// Find the last row (which is the empty row for adding new assertions)
|
||||
const rowCount = await table.allRows().count();
|
||||
const targetRowIndex = rowCount - 1; // Last row is the empty row
|
||||
|
||||
// Wait for the row to exist
|
||||
await expect(table.row(targetRowIndex)).toBeVisible();
|
||||
|
||||
// Fill in the expression
|
||||
const exprInput = table.rowExprInput(targetRowIndex);
|
||||
await expect(exprInput).toBeVisible({ timeout: 2000 });
|
||||
await exprInput.click();
|
||||
await page.keyboard.type(assertion.expr);
|
||||
|
||||
// The component creates a new empty row when the key field is filled
|
||||
await expect(table.allRows()).toHaveCount(rowCount + 1);
|
||||
|
||||
// Fill in the value first (defaults to 'eq value')
|
||||
const valueInput = table.rowValueInput(targetRowIndex);
|
||||
await valueInput.click();
|
||||
await page.keyboard.type(assertion.value);
|
||||
|
||||
// Select the operator from dropdown (if provided and not default 'eq')
|
||||
// This will update the value field to combine operator + value
|
||||
if (assertion.operator && assertion.operator !== 'eq') {
|
||||
const operatorSelect = table.rowOperatorSelect(targetRowIndex);
|
||||
await operatorSelect.selectOption(assertion.operator);
|
||||
}
|
||||
|
||||
// Wait for the assertion to be fully processed
|
||||
// Verify the expression was actually saved by checking the input value
|
||||
const exprInputAfter = table.rowExprInput(targetRowIndex);
|
||||
await expect(exprInputAfter).toHaveValue(assertion.expr, { timeout: 2000 });
|
||||
|
||||
return targetRowIndex;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Edit an assertion at a specific row index
|
||||
* @param page - The page object
|
||||
* @param rowIndex - The row index of the assertion to edit
|
||||
* @param assertion - The assertion data to update (expr, value, optional operator)
|
||||
* @returns void
|
||||
*/
|
||||
const editAssertion = async (page: Page, rowIndex: number, assertion: AssertionInput) => {
|
||||
const operator = assertion.operator || 'eq';
|
||||
|
||||
await test.step(`Edit assertion at row ${rowIndex}: ${assertion.expr} ${operator} ${assertion.value}`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
// Ensure assertions table is visible
|
||||
await expect(table.container()).toBeVisible();
|
||||
|
||||
// Wait for the row to exist
|
||||
await expect(table.row(rowIndex)).toBeVisible();
|
||||
|
||||
// Update the expression
|
||||
const exprInput = table.rowExprInput(rowIndex);
|
||||
await expect(exprInput).toBeVisible({ timeout: 2000 });
|
||||
await exprInput.click();
|
||||
// Clear the input and type new value - use triple-click to select all (works cross-platform)
|
||||
await exprInput.click({ clickCount: 3 });
|
||||
await page.keyboard.press('Backspace'); // Clear selection
|
||||
await page.keyboard.type(assertion.expr);
|
||||
|
||||
// Update the operator from dropdown (if provided)
|
||||
if (assertion.operator) {
|
||||
const operatorSelect = table.rowOperatorSelect(rowIndex);
|
||||
await operatorSelect.selectOption(assertion.operator);
|
||||
}
|
||||
|
||||
// Update the value (just the value, operator is already selected)
|
||||
// The value cell contains a SingleLineEditor, so we need to click and type
|
||||
const valueInput = table.rowValueInput(rowIndex);
|
||||
await valueInput.click({ clickCount: 3 });
|
||||
await page.keyboard.press('Backspace'); // Clear selection
|
||||
await page.keyboard.type(assertion.value);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete an assertion from the current request by row index
|
||||
* @param page - The page object
|
||||
* @param rowIndex - The row index of the assertion to delete
|
||||
* @returns void
|
||||
*/
|
||||
const deleteAssertion = async (page: Page, rowIndex: number) => {
|
||||
await test.step(`Delete assertion at row ${rowIndex}`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const table = locators.assertionsTable();
|
||||
|
||||
await expect(table.container()).toBeVisible();
|
||||
|
||||
const initialRowCount = await table.allRows().count();
|
||||
const deleteButton = table.rowDeleteButton(rowIndex);
|
||||
|
||||
await deleteButton.click();
|
||||
await expect(table.allRows()).toHaveCount(initialRowCount - 1);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the current request and verify success toast
|
||||
* @param page - The page object
|
||||
* @returns void
|
||||
*/
|
||||
const saveRequest = async (page: Page) => {
|
||||
await test.step('Save request', async () => {
|
||||
await page.keyboard.press('Meta+s');
|
||||
await expect(page.getByText('Request saved successfully').last()).toBeVisible({ timeout: 3000 });
|
||||
await page.waitForTimeout(200);
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
closeAllCollections,
|
||||
openCollection,
|
||||
@@ -732,7 +869,11 @@ export {
|
||||
switchResponseFormat,
|
||||
switchToPreviewTab,
|
||||
switchToEditorTab,
|
||||
clickResponseAction
|
||||
clickResponseAction,
|
||||
addAssertion,
|
||||
editAssertion,
|
||||
deleteAssertion,
|
||||
saveRequest
|
||||
};
|
||||
|
||||
export type { SandboxMode, EnvironmentType, EnvironmentVariable, ImportCollectionOptions, CreateRequestOptions, CreateUntitledRequestOptions };
|
||||
export type { SandboxMode, EnvironmentType, EnvironmentVariable, ImportCollectionOptions, CreateRequestOptions, CreateUntitledRequestOptions, AssertionInput };
|
||||
|
||||
@@ -102,6 +102,51 @@ export const buildCommonLocators = (page: Page) => ({
|
||||
locationInput: () => page.locator('#collection-location'),
|
||||
fileInput: () => page.locator('input[type="file"]'),
|
||||
envOption: (name: string) => page.locator('.dropdown-item').getByText(name, { exact: true })
|
||||
},
|
||||
/**
|
||||
* Build generic table locators for any table with a testId
|
||||
* @param testId - The testId of the table
|
||||
* @returns Table locators object
|
||||
*/
|
||||
table: (testId: string) => {
|
||||
const container = () => page.getByTestId(testId);
|
||||
const getBodyRow = (index?: number) => {
|
||||
const locator = container().locator('tbody tr');
|
||||
return index !== undefined ? locator.nth(index) : locator;
|
||||
};
|
||||
|
||||
return {
|
||||
container,
|
||||
row: (index?: number) => getBodyRow(index),
|
||||
rowCell: (columnKey: string, rowIndex?: number) => {
|
||||
const row = getBodyRow(rowIndex);
|
||||
return row.getByTestId(`column-${columnKey}`);
|
||||
},
|
||||
rowCheckbox: (rowIndex: number) => getBodyRow(rowIndex).getByTestId('column-checkbox'),
|
||||
rowDeleteButton: (rowIndex: number) => getBodyRow(rowIndex).getByTestId('column-delete'),
|
||||
allRows: () => container().locator('tbody tr')
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Assertions table locators (extends generic table with assertion-specific helpers)
|
||||
* @returns Assertions table locators object
|
||||
*/
|
||||
assertionsTable: () => {
|
||||
const baseTable = buildCommonLocators(page).table('assertions-table');
|
||||
return {
|
||||
...baseTable,
|
||||
// Assertion-specific helpers
|
||||
rowExprInput: (rowIndex: number) => {
|
||||
const cell = baseTable.rowCell('name', rowIndex);
|
||||
// Wait for the cell to be visible, then find the textbox
|
||||
return cell.getByRole('textbox').or(cell.locator('input[type="text"]'));
|
||||
},
|
||||
rowOperatorSelect: (rowIndex: number) => {
|
||||
const cell = baseTable.rowCell('operator', rowIndex);
|
||||
return cell.getByTestId('assertion-operator-select').or(cell.locator('select'));
|
||||
},
|
||||
rowValueInput: (rowIndex: number) => baseTable.rowCell('value', rowIndex)
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user