1
0
mirror of synced 2026-10-23 07:08:55 +03:00

Fix a flaky Jest test for the connection parameter value dropdown. (#10610)

This commit is contained in:
Dave Page
2026-10-15 15:06:27 +01:00
parent bbb5cc250b
commit 2477a94b32

View File

@@ -8,11 +8,13 @@
//////////////////////////////////////////////////////////////
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import VariableSchema, {getNodeVariableSchema} from '../../../pgadmin/browser/server_groups/servers/static/js/variable.ui';
import * as nodeAjax from '../../../pgadmin/browser/static/js/node_ajax';
import {addNewDatagridRow, genericBeforeEach, getCreateView, getEditView, getPropertiesView} from '../genericFunctions';
import { MappedCellControl } from '../../../pgadmin/static/js/SchemaView/MappedControl';
import {addNewDatagridRow, genericBeforeEach, getCreateView, getEditView, getPropertiesView, withBrowser} from '../genericFunctions';
/* Used to check collection mode */
class MockSchema extends BaseUISchema {
@@ -91,69 +93,44 @@ describe('VariableSchema', ()=>{
});
it('value options follow a change of enum variable name', async ()=>{
const varOptions = [{
/* The grid keeps the value cell's select mounted when the name changes,
so render the value cell on its own and re-render it with the props the
grid builds for each name. Driving the select menus through the full
grid was flaky: the grid focuses a new row from a timer and re-renders
on validation, and a focus change between opening a menu and clicking
an option closes the menu, so the click is lost. */
const schemaObj = new VariableSchema([{
value: 'sslmode', label: 'sslmode', vartype: 'enum',
enumvals: ['prefer', 'require'],
}, {
value: 'sslnegotiation', label: 'sslnegotiation', vartype: 'enum',
enumvals: ['postgres', 'direct'],
}];
let varCollObj = new MockSchema(
()=>new VariableSchema(varOptions, null, null, ['name', 'value'])
);
const {ctrl, user} = await getCreateView(varCollObj);
await user.click(ctrl.container.querySelector('[data-test="add-row"]'));
}], null, null, ['name', 'value']);
/* The variable types are loaded asynchronously by the constructor. */
await waitFor(()=>expect(Object.keys(schemaObj.varTypes)).toHaveLength(2));
const rowSelects = ()=>ctrl.container.querySelectorAll(
'[data-test="data-table-row"] input[role="combobox"]'
);
const hiddenVal = (name)=>ctrl.container.querySelector(
`[data-test="data-table-row"] input[type="hidden"][name="${name}"]`
const valueField = schemaObj.fields.find((f)=>f.id == 'value');
const cellProps = (name, value)=>({
...valueField, ...valueField.cell({name}),
visible: true, value: value, onCellChange: ()=>{/* Not needed */},
});
const ValueCell = withBrowser(MappedCellControl);
const hiddenVal = ()=>ctrl.container.querySelector(
'input[type="hidden"][name="value"]'
)?.value;
/* Allow for slow CI runners re-rendering the grid row. */
const waitOpts = {timeout: 10000};
/* Open the select from the keyboard and wait for an option to show. A
freshly mounted select can drop the first open request on slow runners,
so it is re-sent, a bounded number of times, whilst no menu is shown. */
const findOption = async (selectIdx, label)=>{
for(let attempt = 0; attempt < 5; attempt++) {
if(screen.queryAllByRole('option').length == 0) {
const input = rowSelects()[selectIdx];
fireEvent.focus(input);
fireEvent.keyDown(input, {key: 'ArrowDown', keyCode: 40});
}
try {
return await screen.findByRole('option', {name: label}, {timeout: 2000});
} catch {
/* Not shown yet; retry. */
}
}
throw new Error(`Option '${label}' not shown; options: ${
screen.queryAllByRole('option').map((o)=>o.textContent).join(', ')
}`);
};
const closeMenu = ()=>{
fireEvent.keyDown(rowSelects()[1], {key: 'Escape', keyCode: 27});
};
const pickName = async (label)=>{
await user.click(await findOption(0, label));
await waitFor(()=>{
expect(hiddenVal('name')).toBe(label);
expect(hiddenVal('value')).toBe('');
expect(rowSelects().length).toBe(2);
}, waitOpts);
};
await waitFor(()=>expect(rowSelects().length).toBeGreaterThan(0), waitOpts);
await pickName('sslmode');
expect(await findOption(1, 'require')).toBeInTheDocument();
closeMenu();
await waitFor(()=>expect(screen.queryAllByRole('option')).toHaveLength(0), waitOpts);
const ctrl = render(<ValueCell {...cellProps('sslmode', 'require')}/>);
await waitFor(()=>expect(hiddenVal()).toBe('require'));
await pickName('sslnegotiation');
expect(await findOption(1, 'direct')).toBeInTheDocument();
/* A value is only shown if it is one of the loaded options, so this
fails if the options are not reloaded for the new name. */
ctrl.rerender(<ValueCell {...cellProps('sslnegotiation', 'direct')}/>);
await waitFor(()=>expect(hiddenVal()).toBe('direct'));
await userEvent.setup().click(ctrl.container.querySelector('input[role="combobox"]'));
expect(await screen.findByRole('option', {name: 'postgres'})).toBeInTheDocument();
expect(screen.queryByRole('option', {name: 'require'})).not.toBeInTheDocument();
}, 30000);
});
it('variable collection', async ()=>{
jest.spyOn(nodeAjax, 'getNodeAjaxOptions').mockReturnValue([]);