Files
nexu-io-open-design/apps/web/tests/utils/pluginRequiredInputs.test.ts
elihahah666 8920577f43 feat(web): simplify Home composer (drop plugin-inputs form & example pill, move Design Agent next to Send) (#3645)
* feat(web): simplify Home composer (drop plugin-inputs form & example pill, move Design Agent next to Send)

- Remove the inline plugin-inputs form (ARTIFACT KIND / AUDIENCE / TEMPLATE) from the Home composer; required inputs no longer gate Send (defaults still flow to the backend).
- Remove the dismissible "selected example" pill above the editor; picking an example still seeds the prompt.
- Move the Design Agent toggle from the footer-left group to sit directly left of the Send button.

* fix(web): keep client-side required-input gate on Home composer

Dropping the inline plugin-inputs form left pluginInputsAreValid() always
returning true, so a required field with no default that the user blanks out in
an editable Home scenario kept Send enabled and only failed at click-time apply
with a generic "Failed to apply …" error (the daemon's validateInputs rejects
missing required inputs).

Restore a client-side gate that mirrors the daemon's required-input rule and
names the missing field in the submit error, so these Home flows fail fast.
Extracts the pure check into utils/pluginRequiredInputs with unit coverage.

* fix(web): drop example from active-row mount condition

With the example pill removed from the Home composer active row, keeping
selectedPromptExample in showActiveContextRow mounted an empty .home-hero__active
row when an example was picked with no other active context. Remove it from the
condition so the row only renders when it has visible content.

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
Co-authored-by: lefarcen <935902669@qq.com>
2026-06-04 14:08:40 +00:00

50 lines
2.0 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { InputFieldSpec } from '@open-design/contracts';
import {
missingRequiredInputs,
pluginInputsAreValid,
} from '../../src/utils/pluginRequiredInputs';
function field(spec: Partial<InputFieldSpec> & { name: string }): InputFieldSpec {
return spec as InputFieldSpec;
}
describe('pluginRequiredInputs', () => {
it('flags a required field with no value and no default (the regressed case)', () => {
// The Home composer dropped its inline inputs form; a required field with
// no default that the user blanks out must still fail fast client-side, or
// the run only breaks later at daemon apply time with a generic error.
const fields = [field({ name: 'subject', label: 'Subject', required: true })];
expect(missingRequiredInputs(fields, {})).toEqual(['Subject']);
expect(missingRequiredInputs(fields, { subject: '' })).toEqual(['Subject']);
expect(pluginInputsAreValid(fields, { subject: '' })).toBe(false);
});
it('treats a provided value or a usable default as satisfied', () => {
const fields = [
field({ name: 'subject', label: 'Subject', required: true }),
field({ name: 'style', required: true, default: 'cinematic' }),
];
expect(pluginInputsAreValid(fields, { subject: 'a product shot' })).toBe(true);
expect(missingRequiredInputs(fields, { subject: 'a product shot' })).toEqual([]);
});
it('ignores optional fields entirely', () => {
const fields = [
field({ name: 'note', required: false }),
field({ name: 'extra' }),
];
expect(pluginInputsAreValid(fields, {})).toBe(true);
});
it('falls back to the field name when no label is set', () => {
const fields = [field({ name: 'subject', required: true })];
expect(missingRequiredInputs(fields, {})).toEqual(['subject']);
});
it('does not treat an empty-string default as a usable default', () => {
const fields = [field({ name: 'subject', label: 'Subject', required: true, default: '' })];
expect(pluginInputsAreValid(fields, {})).toBe(false);
});
});