Skip to content

Commit

Permalink
Merge pull request #7571 from tdesveaux/react-base/force-build-fields…
Browse files Browse the repository at this point in the history
…-fixes

react-base: Minor fix and improvement to ForceBuildModal fields
  • Loading branch information
p12tic committed May 20, 2024
2 parents cbc53f5 + a4eda76 commit 4199678
Show file tree
Hide file tree
Showing 14 changed files with 1,269 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix ``ChoiceStringParameter`` fields with ``multiple`` would not store the selected values
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
This file is part of Buildbot. Buildbot is free software: you can
redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Copyright Buildbot Team Members
*/

import renderer from 'react-test-renderer';
import { FieldBoolean } from "./FieldBoolean";
import { ForceSchedulerFieldBoolean } from 'buildbot-data-js';
import { ForceBuildModalFieldsState } from '../ForceBuildModalFieldsState';

function assertRenderToSnapshot(defaultValue: boolean, stateValue?: boolean, updateValue: boolean = false) {
const field: ForceSchedulerFieldBoolean = {
name: 'dummy',
fullName: 'fullDummy',
label: 'dummyLabel',
tablabel: 'dummyTabLabel',
type: 'bool',
default: defaultValue,
multiple: false,
regex: null,
hide: false,
maxsize: null,
autopopulate: null,
}
const state = new ForceBuildModalFieldsState();
state.createNewField(field.fullName, field.default);
if (stateValue !== undefined) {
state.setValue(field.fullName, stateValue);
}

const component = renderer.create(
<FieldBoolean field={field} fieldsState={state} />
);
expect(component.toJSON()).toMatchSnapshot();

if (updateValue) {
const previousState = state.getValue(field.fullName);
const expectedState = !previousState;
renderer.act(() => {
const elements = component.root.findAllByProps({'data-bb-test-id': `force-field-${field.fullName}`}, {deep: true});
expect(elements.length).toBe(1);
const checkbox = elements[0];
checkbox.props.onChange({target: {checked: expectedState}});
});
expect(state.getValue(field.fullName)).toBe(expectedState);
}
}

describe('ForceFieldBoolean component', function () {
it('render default value False', () => {
assertRenderToSnapshot(false);
});

it('render default value True', () => {
assertRenderToSnapshot(true);
});

it('render non-default value False', () => {
assertRenderToSnapshot(true, false);
});

it('render non-default value True', () => {
assertRenderToSnapshot(false, true);
});

it('change state on click', () => {
assertRenderToSnapshot(true, true, true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ export const FieldBoolean = observer(({field, fieldsState}: FieldBooleanProps) =
<div className="col-sm-10 col-sm-offset-2">
<div className="checkbox">
<label>
<input type="checkbox" checked={state.value === 'true'}
onChange={event => fieldsState.setValue(field.fullName,
event.target.checked ? 'true' : 'false')}/> {field.label}
<input
data-bb-test-id={`force-field-${field.fullName}`}
type="checkbox" checked={state.value}
onChange={event => fieldsState.setValue(field.fullName,
event.target.checked)} /> {field.label}
</label>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
This file is part of Buildbot. Buildbot is free software: you can
redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Copyright Buildbot Team Members
*/

import renderer from 'react-test-renderer';
import { FieldChoiceString } from "./FieldChoiceString";
import { ForceSchedulerFieldChoiceString } from 'buildbot-data-js';
import { ForceBuildModalFieldsState } from '../ForceBuildModalFieldsState';

type FieldChoiceStringTestOptions = {
defaultValue: string | string[];
stateValue?: string | string[];
updateValueTo?: string[];
multiple?: boolean;
strict?: boolean;
}

function assertRenderToSnapshot(options: FieldChoiceStringTestOptions) {
const field: ForceSchedulerFieldChoiceString = {
name: 'dummy',
fullName: 'fullDummy',
label: 'dummyLabel',
tablabel: 'dummyTabLabel',
type: 'list',
default: options.defaultValue,
multiple: options.multiple ?? false,
regex: null,
hide: false,
maxsize: null,
autopopulate: null,
choices: ['A', 'B', 'C'],
strict: options.strict ?? true,
}
const state = new ForceBuildModalFieldsState();
state.createNewField(field.fullName, field.default);
if (options.stateValue !== undefined) {
state.setValue(field.fullName, options.stateValue);
}

const component = renderer.create(
<FieldChoiceString field={field} fieldsState={state} />
);
expect(component.toJSON()).toMatchSnapshot();

if (options.updateValueTo !== undefined) {
if (!field.multiple) {
expect(options.updateValueTo.length).toBe(1);
}

const expectedState = options.updateValueTo;

const toClick: string[] = [];
if (!field.multiple) {
expect(expectedState.length).toBe(1);
toClick.push(expectedState[0]);
}
else {
const currentValue: string[] = state.getValue(field.fullName);
// click elements not in expected to unselect them
toClick.push(...currentValue.filter(e => !(expectedState.includes(e))));
// then click elements not currently selected
toClick.push(...expectedState.filter(e => !(currentValue.includes(e))));
}

renderer.act(() => {
const elements = component.root.findAll(
(node) => {
return node.props['data-bb-test-id'] === `force-field-${field.fullName}` && node.type !== 'select';
},
{ deep: true }
);
expect(elements.length).toBe(1);
const select = elements[0];
for (const element of toClick) {
select.props.onChange({ target: { value: element } });
}
});

if (field.multiple) {
const stateValue: string[] = state.getValue(field.fullName);
stateValue.sort();
expectedState.sort();
expect(stateValue).toStrictEqual(expectedState);
}
else {
expect(state.getValue(field.fullName)).toBe(expectedState[0]);
}
}
}

describe('ForceFieldChoiceString component', function () {
it('render default value', () => {
assertRenderToSnapshot({ defaultValue: 'A' });
assertRenderToSnapshot({ defaultValue: 'B' });
});

it('render multiple default value', () => {
assertRenderToSnapshot({ defaultValue: ['A'], multiple: true });
assertRenderToSnapshot({ defaultValue: ['A', 'B'], multiple: true });
});

it('render non-default value', () => {
assertRenderToSnapshot({ defaultValue: 'A', stateValue: 'B' });
});

it('render multiple non-default value', () => {
assertRenderToSnapshot({ defaultValue: ['A'], stateValue: ['B', 'C'], multiple: true });
});

it('change state on click', () => {
assertRenderToSnapshot({ defaultValue: 'A', stateValue: 'B', updateValueTo: ['C'] });
});

it('change multiple state on click', () => {
assertRenderToSnapshot({ defaultValue: ['A'], stateValue: ['B', 'C'], updateValueTo: ['A', 'C'], multiple: true });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,35 @@ type FieldChoiceStringProps = {
export const FieldChoiceString = observer(({field, fieldsState}: FieldChoiceStringProps) => {
const state = fieldsState.fields.get(field.fullName)!;

const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (!field.multiple) {
fieldsState.setValue(field.fullName, event.target.value);
}
else {
const currentValue = new Set(state.value);
if (currentValue.has(event.target.value)) {
currentValue.delete(event.target.value);
}
else {
currentValue.add(event.target.value);
}
fieldsState.setValue(field.fullName, [...currentValue]);
}
};

return (
<FieldBase field={field} fieldsState={fieldsState}>
<Form.Label className="col-sm-10">{field.label}</Form.Label>
<div className="col-sm-10">
<Form.Control as="select" multiple={field.multiple} value={state.value}
onChange={event => fieldsState.setValue(field.fullName, event.target.value)}>
{field.choices.map(choice => (<option>{choice}</option>))}
<Form.Control data-bb-test-id={`force-field-${field.fullName}`}
as="select" multiple={field.multiple} value={state.value}
onChange={onChange}>
{field.choices.map(choice => (<option key={`force-field-${field.fullName}-option-${choice}`}>{choice}</option>))}
</Form.Control>
{ !field.strict && !field.multiple
? <input data-bb-test-id={`force-field-${field.fullName}`}
? <input data-bb-test-id={`force-field-non-strict-${field.fullName}`}
className="select-editable form-control" type="text" value={state.value}
onChange={event => fieldsState.setValue(field.fullName, event.target.value)}/>
onChange={onChange}/>
: <></>
}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
This file is part of Buildbot. Buildbot is free software: you can
redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Copyright Buildbot Team Members
*/

import renderer from 'react-test-renderer';
import { FieldInt } from "./FieldInt";
import { ForceSchedulerFieldInt } from 'buildbot-data-js';
import { ForceBuildModalFieldsState } from '../ForceBuildModalFieldsState';

function assertRenderToSnapshot(defaultValue: number, stateValue?: number, updateValue?: number) {
const field: ForceSchedulerFieldInt = {
name: 'dummy',
fullName: 'fullDummy',
label: 'dummyLabel',
tablabel: 'dummyTabLabel',
type: 'int',
default: defaultValue,
multiple: false,
regex: null,
hide: false,
maxsize: null,
autopopulate: null,
size: 0,
}
const state = new ForceBuildModalFieldsState();
state.createNewField(field.fullName, field.default);
if (stateValue !== undefined) {
state.setValue(field.fullName, stateValue);
}

const component = renderer.create(
<FieldInt field={field} fieldsState={state} />
);
expect(component.toJSON()).toMatchSnapshot();

if (updateValue !== undefined) {
const expectedState = updateValue;
renderer.act(() => {
const elements = component.root.findAllByProps({'data-bb-test-id': `force-field-${field.fullName}`}, {deep: true});
expect(elements.length).toBe(1);
const input = elements[0];
input.props.onChange({target: {value: expectedState}});
});
expect(state.getValue(field.fullName)).toBe(expectedState);
}
}

describe('ForceFieldInt component', function () {
it('render default value', () => {
assertRenderToSnapshot(0);
assertRenderToSnapshot(-0);
assertRenderToSnapshot(150);
assertRenderToSnapshot(-150);
});

it('render non-default value', () => {
assertRenderToSnapshot(0, -0);
assertRenderToSnapshot(0, 150);
assertRenderToSnapshot(0, -150);
});

it('change state on click', () => {
assertRenderToSnapshot(0, -150, 350);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export const FieldInt = observer(({field, fieldsState}: FieldIntProps) => {
<label htmlFor={field.fullName} className="control-label col-sm-10">{field.label}</label>
<div className="col-sm-10">
<input data-bb-test-id={`force-field-${field.fullName}`}
type="text" className="form-control" value={state.value}
onChange={event => fieldsState.setValue(field.fullName, event.target.value)}/>
type="number" className="form-control" value={state.value}
onChange={event => fieldsState.setValue(field.fullName, Number.parseInt(event.target.value))}/>
</div>
</FieldBase>
);
Expand Down
Loading

0 comments on commit 4199678

Please sign in to comment.