test(settings): cover the schema-engine edit/file/scope/search paths
Exercise the previously-untested branches of the settings category renderer: number-field commit/clamp/unparseable-revert, text-field commit, the file control's command button, the select raw-value fallback, scope move-to-app, field help text, and the cross-category search empty state. Restores the coverage floor after the D-101 facade migration shifted the ratio. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -192,6 +192,145 @@ void main() {
|
||||
await tester.pump();
|
||||
expect(f.services.settings.effectiveLayer('app.demo.flag'), isNull);
|
||||
});
|
||||
|
||||
testWidgets('moving an unset value to All clide writes it at app scope', (tester) async {
|
||||
// _other's key (app.other.x) is written by no other test → pristine here.
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: _other))));
|
||||
await tester.tap(find.bySemanticsLabel('OtherFlag scope: Default'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('All clide'));
|
||||
await tester.pump();
|
||||
expect(f.services.settings.effectiveLayer('app.other.x'), SettingsScope.app);
|
||||
});
|
||||
});
|
||||
|
||||
group('edit controls (T-448)', () {
|
||||
const numCat = SettingsCategory(
|
||||
id: 'n',
|
||||
title: 'N',
|
||||
sections: [
|
||||
SettingsSection(
|
||||
label: 'S',
|
||||
fields: [SettingsField(key: 'app.n.size', kind: SettingsFieldKind.number, label: 'Size', defaultValue: 4, min: 1, max: 8)],
|
||||
),
|
||||
],
|
||||
);
|
||||
const textCat = SettingsCategory(
|
||||
id: 't',
|
||||
title: 'T',
|
||||
sections: [
|
||||
SettingsSection(
|
||||
label: 'S',
|
||||
fields: [SettingsField(key: 'app.t.name', kind: SettingsFieldKind.text, label: 'Name', defaultValue: '')],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
testWidgets('a number field commits a valid value', (tester) async {
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: numCat))));
|
||||
await tester.enterText(find.byType(EditableText), '6');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
expect(f.services.settings.get<num>('app.n.size'), 6);
|
||||
});
|
||||
|
||||
testWidgets('a number field clamps above max and below min', (tester) async {
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: numCat))));
|
||||
await tester.enterText(find.byType(EditableText), '99');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
expect(f.services.settings.get<num>('app.n.size'), 8);
|
||||
await tester.enterText(find.byType(EditableText), '0');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
expect(f.services.settings.get<num>('app.n.size'), 1);
|
||||
});
|
||||
|
||||
testWidgets('a number field reverts unparseable input without writing', (tester) async {
|
||||
// Dedicated key — the store is shared across tests in this file, so a key
|
||||
// the commit/clamp tests touch would already be set.
|
||||
const revCat = SettingsCategory(
|
||||
id: 'nr',
|
||||
title: 'NR',
|
||||
sections: [
|
||||
SettingsSection(
|
||||
label: 'S',
|
||||
fields: [SettingsField(key: 'app.nr.size', kind: SettingsFieldKind.number, label: 'Size', defaultValue: 4, min: 1, max: 8)],
|
||||
),
|
||||
],
|
||||
);
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: revCat))));
|
||||
await tester.enterText(find.byType(EditableText), 'abc');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
// Unparseable input is reverted to the field's value, not committed; the
|
||||
// string 'abc' never reaches the store.
|
||||
expect(find.text('4'), findsOneWidget);
|
||||
expect(f.services.settings.get<Object>('app.nr.size'), isNot('abc'));
|
||||
});
|
||||
|
||||
testWidgets('a text field commits the trimmed value', (tester) async {
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: textCat))));
|
||||
await tester.enterText(find.byType(EditableText), ' hello ');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
expect(f.services.settings.get<String>('app.t.name'), 'hello');
|
||||
});
|
||||
|
||||
testWidgets('a select shows the raw stored value when it is not among the options', (tester) async {
|
||||
await tester.runAsync(() => f.services.settings.set('app.demo.level', 'trace'));
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: _category))));
|
||||
expect(find.text('trace'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a file field renders a button that runs its command', (tester) async {
|
||||
var ran = false;
|
||||
f.services.commands.register(
|
||||
CommandContribution(
|
||||
id: 'test.openfile',
|
||||
command: 'test.openfile',
|
||||
run: (_) async {
|
||||
ran = true;
|
||||
return IpcResponse.ok(id: '', data: const {});
|
||||
},
|
||||
),
|
||||
);
|
||||
const cat = SettingsCategory(
|
||||
id: 'fc',
|
||||
title: 'FC',
|
||||
sections: [
|
||||
SettingsSection(
|
||||
label: 'S',
|
||||
fields: [SettingsField(key: 'app.fc.cfg', kind: SettingsFieldKind.file, label: 'Edit config', fileCommand: 'test.openfile')],
|
||||
),
|
||||
],
|
||||
);
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: cat))));
|
||||
await tester.tap(find.widgetWithText(ClideButton, 'Edit config'));
|
||||
await tester.pump();
|
||||
expect(ran, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('a field renders its help text', (tester) async {
|
||||
const cat = SettingsCategory(
|
||||
id: 'h',
|
||||
title: 'H',
|
||||
sections: [
|
||||
SettingsSection(
|
||||
label: 'S',
|
||||
fields: [SettingsField(key: 'app.h.flag', kind: SettingsFieldKind.toggle, label: 'Flag', help: 'Toggle the thing', defaultValue: false)],
|
||||
),
|
||||
],
|
||||
);
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsCategoryView(category: cat))));
|
||||
expect(find.text('Toggle the thing'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('search results show an empty state when nothing matches', (tester) async {
|
||||
f.services.settingsRegistry.register(_category);
|
||||
await tester.pumpWidget(harness(f, _bounded(const SettingsSearchResults(query: 'zzznomatch'))));
|
||||
expect(find.text('No settings match your search.'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
group('SettingsModal with a registered category', () {
|
||||
|
||||
Reference in New Issue
Block a user