From 7b754a099b6bf25ed1c5af771e7d486805f8cc67 Mon Sep 17 00:00:00 2001 From: Joe Russack Date: Thu, 16 Apr 2026 09:54:52 -0700 Subject: [PATCH] fix: default SpAppResource description to name on creation The frontend "New Resource" path constructed SpAppResource without a description, persisting NULL. Specify 6 hard-fails when opening labels backed by such rows. Mirror the backend report_runner behavior, which already defaults description to name. Refs ticket #824. --- .../lib/components/AppResources/Create.tsx | 35 +++++++++++++------ .../__tests__/CreateAppResource.test.tsx | 26 +++++++++++++- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx index 1f4b82d129c..3033b0ff7b1 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/Create.tsx @@ -259,17 +259,7 @@ function EditAppResource({ readonly templateFile: string | undefined; }): JSX.Element { const resource = React.useMemo( - () => - deserializeResource( - addMissingFields(type.tableName as 'SpAppResource', { - // I don't think this field is used anywhere - level: 0, - mimeType, - name: name.trim(), - specifyUser: userInformation.resource_uri, - spAppResourceDir: directory.resource_uri, - }) - ), + () => buildNewAppResource(type, name, mimeType, directory), [directory, name, type, mimeType] ); @@ -321,6 +311,29 @@ function EditAppResource({ ); } +/** + * S6 hard-fails when opening a label whose spappresource.Description is NULL, + * so default description to the resource name on creation. See ticket #824. + */ +const buildNewAppResource = ( + type: AppResourceType, + name: string, + mimeType: string | undefined, + directory: SerializedResource +) => + deserializeResource( + addMissingFields(type.tableName as 'SpAppResource', { + // I don't think this field is used anywhere + level: 0, + mimeType, + name: name.trim(), + description: name.trim(), + specifyUser: userInformation.resource_uri, + spAppResourceDir: directory.resource_uri, + }) + ); + export const exportsForTests = { getUrl, + buildNewAppResource, }; diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/CreateAppResource.test.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/CreateAppResource.test.tsx index cbf9483ddf8..b5aceab56b6 100644 --- a/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/CreateAppResource.test.tsx +++ b/specifyweb/frontend/js_src/lib/components/AppResources/__tests__/CreateAppResource.test.tsx @@ -6,10 +6,15 @@ import { requireContext } from '../../../tests/helpers'; import { mount } from '../../../tests/reactUtils'; import { TestComponentWrapperRouter } from '../../../tests/utils'; import { LoadingContext } from '../../Core/Contexts'; +import type { SerializedResource } from '../../DataModel/helperTypes'; +import type { SpAppResourceDir } from '../../DataModel/types'; import { UnloadProtectsContext } from '../../Router/UnloadProtect'; -import { CreateAppResource } from '../Create'; +import { CreateAppResource, exportsForTests } from '../Create'; +import { appResourceTypes } from '../types'; import { testAppResources } from './testAppResources'; +const { buildNewAppResource } = exportsForTests; + requireContext(); beforeEach(() => { @@ -98,3 +103,22 @@ describe('CreateAppResource', () => { ); }); }); + +describe('buildNewAppResource', () => { + const directory = { + resource_uri: '/api/specify/spappresourcedir/1/', + } as SerializedResource; + + // Specify 6 hard-fails on labels whose spappresource.Description is NULL, + // so a freshly created resource must carry a non-null description (#824). + test('defaults description to the trimmed name', () => { + const resource = buildNewAppResource( + appResourceTypes.appResources, + ' My Label ', + 'text/xml', + directory + ); + expect(resource.get('name')).toBe('My Label'); + expect(resource.get('description')).toBe('My Label'); + }); +});