From 94f70f19edcbff69c5fd70beeef1bee437a057fd Mon Sep 17 00:00:00 2001 From: Mandy Parson Date: Wed, 14 May 2025 09:44:45 -0600 Subject: [PATCH 1/7] MMT-3995: As a user, I can edit Visualization Information and Science Keywords --- .../components/MetadataForm/MetadataForm.jsx | 3 +- .../__tests__/MetadataForm.test.jsx | 23 ++++-- static/src/js/hooks/usePublishMutation.js | 5 +- .../schemas/uiSchemas/visualizations/index.js | 9 ++ .../visualizations/scienceKeywords.js | 37 +++++++++ .../visualizationInformation.js | 82 +++++++++++++++++++ static/src/js/utils/getUiSchema.js | 3 + static/src/js/utils/getUmmVersion.js | 2 + 8 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 static/src/js/schemas/uiSchemas/visualizations/index.js create mode 100644 static/src/js/schemas/uiSchemas/visualizations/scienceKeywords.js create mode 100644 static/src/js/schemas/uiSchemas/visualizations/visualizationInformation.js diff --git a/static/src/js/components/MetadataForm/MetadataForm.jsx b/static/src/js/components/MetadataForm/MetadataForm.jsx index 438568639..1eb287af1 100644 --- a/static/src/js/components/MetadataForm/MetadataForm.jsx +++ b/static/src/js/components/MetadataForm/MetadataForm.jsx @@ -45,6 +45,7 @@ import getFormSchema from '@/js/utils/getFormSchema' import getNextFormName from '@/js/utils/getNextFormName' import getUiSchema from '@/js/utils/getUiSchema' import getUmmSchema from '@/js/utils/getUmmSchema' +import getUmmVersion from '@/js/utils/getUmmVersion' import removeEmpty from '@/js/utils/removeEmpty' import toKebabCase from '@/js/utils/toKebabCase' @@ -204,7 +205,7 @@ const MetadataForm = () => { nativeId, providerId: fetchedMetadataProviderId || providerId, // TODO pull this version number from a config - ummVersion: '1.0.0' + ummVersion: getUmmVersion(derivedConceptType) }, onCompleted: (mutationData) => { const { ingestDraft } = mutationData diff --git a/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx b/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx index 4f8081b72..878dfa9a2 100644 --- a/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx +++ b/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx @@ -23,6 +23,7 @@ import * as router from 'react-router' import conceptTypeDraftQueries from '@/js/constants/conceptTypeDraftQueries' import errorLogger from '@/js/utils/errorLogger' +import getUmmVersion from '@/js/utils/getUmmVersion' import relatedUrlsUiSchema from '@/js/schemas/uiSchemas/tools/relatedUrls' import toolInformationUiSchema from '@/js/schemas/uiSchemas/tools/toolInformation' @@ -55,6 +56,14 @@ import { PUBLISH_DRAFT } from '@/js/operations/mutations/publishDraft' import MetadataForm from '../MetadataForm' +vi.mock('@/js/hooks/usePublishMutation', () => ({ + default: vi.fn(() => ({ + publishMutation: vi.fn(), + publishDraft: null, + error: null + })) +})) + vi.mock('@rjsf/core', () => ({ default: vi.fn(({ onChange, @@ -580,7 +589,7 @@ describe('MetadataForm', () => { }, nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c', providerId: 'MMT_2', - ummVersion: '1.0.0' + ummVersion: getUmmVersion('Tool') } }, result: { @@ -629,7 +638,7 @@ describe('MetadataForm', () => { }, nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c', providerId: 'MMT_2', - ummVersion: '1.0.0' + ummVersion: getUmmVersion('Tool') } }, result: { @@ -675,7 +684,7 @@ describe('MetadataForm', () => { }, nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c', providerId: 'MMT_2', - ummVersion: '1.0.0' + ummVersion: getUmmVersion('Tool') } }, result: { @@ -800,7 +809,7 @@ describe('MetadataForm', () => { metadata: ummMetadata, nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c', providerId: 'MMT_2', - ummVersion: '1.0.0' + ummVersion: getUmmVersion('Tool') } }, result: { @@ -817,7 +826,7 @@ describe('MetadataForm', () => { variables: { draftConceptId: 'TD1000000-MMT', nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c', - ummVersion: '1.2.0' + ummVersion: getUmmVersion('Tool') } }, result: { @@ -864,7 +873,7 @@ describe('MetadataForm', () => { }, nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c', providerId: 'MMT_2', - ummVersion: '1.0.0' + ummVersion: getUmmVersion('Tool') } }, error: new Error('An error occured') @@ -928,7 +937,7 @@ describe('MetadataForm', () => { }, nativeId: 'MMT_mock-uuid', providerId: 'MMT_1', - ummVersion: '1.0.0' + ummVersion: getUmmVersion('Collection') } }, result: { diff --git a/static/src/js/hooks/usePublishMutation.js b/static/src/js/hooks/usePublishMutation.js index 43b806f4a..40c40779c 100644 --- a/static/src/js/hooks/usePublishMutation.js +++ b/static/src/js/hooks/usePublishMutation.js @@ -32,10 +32,13 @@ const usePublishMutation = (queryName) => { conceptType, nativeId ) => { + // Can be removed once CMR-10545 is complete + const publishNativeId = conceptType === 'Visualization' ? `MMT_${crypto.randomUUID()}` : nativeId + await publishDraftMutation({ variables: { draftConceptId: conceptId, - nativeId, + nativeId: publishNativeId, ummVersion: getUmmVersion(conceptType) }, onCompleted: (getPublishedData) => { diff --git a/static/src/js/schemas/uiSchemas/visualizations/index.js b/static/src/js/schemas/uiSchemas/visualizations/index.js new file mode 100644 index 000000000..8dcc87af0 --- /dev/null +++ b/static/src/js/schemas/uiSchemas/visualizations/index.js @@ -0,0 +1,9 @@ +import scienceKeywordsUiSchema from './scienceKeywords' +import visualizationInformationUiSchema from './visualizationInformation' + +const visualizationUiSchema = { + 'visualization-information': visualizationInformationUiSchema, + 'science-keywords': scienceKeywordsUiSchema +} + +export default visualizationUiSchema diff --git a/static/src/js/schemas/uiSchemas/visualizations/scienceKeywords.js b/static/src/js/schemas/uiSchemas/visualizations/scienceKeywords.js new file mode 100644 index 000000000..6c1106851 --- /dev/null +++ b/static/src/js/schemas/uiSchemas/visualizations/scienceKeywords.js @@ -0,0 +1,37 @@ +const scienceKeywordsUiSchema = { + 'ui:heading-level': 'h3', + 'ui:field': 'layout', + 'ui:layout_grid': { + 'ui:row': [ + { + 'ui:group': 'Science Keywords', + 'ui:required': true, + 'ui:col': { + md: 12, + children: [ + { + 'ui:row': [ + { + 'ui:col': { + md: 12, + children: ['ScienceKeywords'] + } + } + ] + } + ] + } + } + ] + }, + ScienceKeywords: { + 'ui:field': 'keywordPicker', + 'ui:keyword_scheme': 'science_keywords', + 'ui:picker_title': 'SERVICE KEYWORD', + 'ui:keyword_scheme_column_names': ['sciencekeywords', 'category', 'topic', 'term', 'variable_level_1', 'variable_level_2', 'variable_level_3'], + 'ui:filter': 'EARTH SCIENCE', + 'ui:scheme_values': ['Category', 'Topic', 'Term', 'VariableLevel1', 'VariableLevel2', 'VariableLevel3'] + } +} + +export default scienceKeywordsUiSchema diff --git a/static/src/js/schemas/uiSchemas/visualizations/visualizationInformation.js b/static/src/js/schemas/uiSchemas/visualizations/visualizationInformation.js new file mode 100644 index 000000000..a60b5f6d5 --- /dev/null +++ b/static/src/js/schemas/uiSchemas/visualizations/visualizationInformation.js @@ -0,0 +1,82 @@ +const visualizationInformationUiSchema = { + 'ui:heading-level': 'h3', + 'ui:field': 'layout', + 'ui:layout_grid': { + 'ui:row': [ + { + 'ui:group': 'Visualization Information', + 'ui:required': true, + 'ui:col': { + md: 12, + children: [ + { + 'ui:row': [ + { + 'ui:col': { + md: 12, + children: ['Identifier'] + } + } + ] + }, + { + 'ui:row': [ + { + 'ui:col': { + md: 12, + children: ['Name'] + } + } + ] + }, + { + 'ui:row': [ + { + 'ui:col': { + md: 12, + children: ['Title'] + } + } + ] + }, + { + 'ui:row': [ + { + 'ui:col': { + md: 12, + children: ['Subtitle'] + } + } + ] + }, + { + 'ui:row': [ + { + 'ui:col': { + md: 12, + children: ['Description'] + } + } + ] + }, + { + 'ui:row': [ + { + 'ui:col': { + md: 12, + children: ['VisualizationType'] + } + } + ] + } + ] + } + } + ] + }, + Description: { + 'ui:widget': 'textarea' + } +} + +export default visualizationInformationUiSchema diff --git a/static/src/js/utils/getUiSchema.js b/static/src/js/utils/getUiSchema.js index bba151546..b10986961 100644 --- a/static/src/js/utils/getUiSchema.js +++ b/static/src/js/utils/getUiSchema.js @@ -2,6 +2,7 @@ import collectionsUiSchema from '../schemas/uiSchemas/collections' import serviceUiSchema from '../schemas/uiSchemas/services' import toolsUiSchema from '../schemas/uiSchemas/tools' import variableUiSchema from '../schemas/uiSchemas/variables' +import visualizationUiSchema from '../schemas/uiSchemas/visualizations' /** * Returns the UI Schema of the provided conceptType @@ -17,6 +18,8 @@ const getUiSchema = (conceptType) => { return toolsUiSchema case 'Variable': return variableUiSchema + case 'Visualization': + return visualizationUiSchema default: return null } diff --git a/static/src/js/utils/getUmmVersion.js b/static/src/js/utils/getUmmVersion.js index edf064812..959617b00 100644 --- a/static/src/js/utils/getUmmVersion.js +++ b/static/src/js/utils/getUmmVersion.js @@ -16,6 +16,8 @@ const getUmmVersion = (conceptType) => { return ummVersion.ummT case 'Variable': return ummVersion.ummV + case 'Visualization': + return ummVersion.ummVis default: return null } From dd67f1a3cf7c4362f0c45fc4127f6661a3a3284f Mon Sep 17 00:00:00 2001 From: Mandy Parson Date: Wed, 14 May 2025 11:05:38 -0600 Subject: [PATCH 2/7] MMT-3995: Fixing test --- .../__tests__/MetadataForm.test.jsx | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx b/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx index 878dfa9a2..185e0579f 100644 --- a/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx +++ b/static/src/js/components/MetadataForm/__tests__/MetadataForm.test.jsx @@ -57,11 +57,27 @@ import { PUBLISH_DRAFT } from '@/js/operations/mutations/publishDraft' import MetadataForm from '../MetadataForm' vi.mock('@/js/hooks/usePublishMutation', () => ({ - default: vi.fn(() => ({ - publishMutation: vi.fn(), - publishDraft: null, - error: null - })) + default: vi.fn(() => { + const [publishDraft, setPublishDraft] = React.useState(null) + const [error] = React.useState(null) + + const publishMutation = vi.fn(() => new Promise((resolve) => { + setTimeout(() => { + setPublishDraft({ + conceptId: 'T1000000-MMT', + revisionId: '1' + }) + + resolve() + }, 100) + })) + + return { + publishMutation, + publishDraft, + error + } + }) })) vi.mock('@rjsf/core', () => ({ @@ -847,8 +863,12 @@ describe('MetadataForm', () => { const button = screen.getByRole('button', { name: 'Save & Publish' }) await user.click(button) - expect(navigateSpy).toHaveBeenCalledTimes(2) - expect(navigateSpy).toHaveBeenCalledWith('/tools/T1000000-MMT') + await waitFor(() => { + expect(navigateSpy).toHaveBeenCalledTimes(2) + }, { timeout: 5000 }) + + expect(navigateSpy).toHaveBeenNthCalledWith(1, '/drafts/tools/TD1000000-MMT/tool-information?revisionId=1', expect.anything()) + expect(navigateSpy).toHaveBeenNthCalledWith(2, '/tools/T1000000-MMT') }) }) @@ -891,6 +911,10 @@ describe('MetadataForm', () => { }) describe('when saving a new draft', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + test('navigates to the current form and calls scrolls to the top', async () => { const navigateSpy = vi.fn() vi.spyOn(router, 'useNavigate').mockImplementation(() => navigateSpy) From 0028d73a8ccad18a8351d8aa7bafd9ef5b7added Mon Sep 17 00:00:00 2001 From: Mandy Parson Date: Wed, 14 May 2025 11:58:18 -0600 Subject: [PATCH 3/7] MMT-3995: Code Coverage --- .../src/js/utils/__tests__/getUiSchema.test.js | 16 ++++++++++++---- .../src/js/utils/__tests__/getUmmVersion.test.js | 13 ++++++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/static/src/js/utils/__tests__/getUiSchema.test.js b/static/src/js/utils/__tests__/getUiSchema.test.js index 8364de711..d26712506 100644 --- a/static/src/js/utils/__tests__/getUiSchema.test.js +++ b/static/src/js/utils/__tests__/getUiSchema.test.js @@ -1,7 +1,9 @@ -import collectionUiSchema from '../../schemas/uiSchemas/collections' -import serviceUiSchema from '../../schemas/uiSchemas/services' -import toolsUiSchema from '../../schemas/uiSchemas/tools' -import variableUiSchema from '../../schemas/uiSchemas/variables' +import collectionUiSchema from '@/js/schemas/uiSchemas/collections' +import serviceUiSchema from '@/js/schemas/uiSchemas/services' +import toolsUiSchema from '@/js/schemas/uiSchemas/tools' +import variableUiSchema from '@/js/schemas/uiSchemas/variables' +import visualizationUiSchema from '@/js/schemas/uiSchemas/visualizations' + import getUiSchema from '../getUiSchema' describe('getUiSchema', () => { @@ -29,6 +31,12 @@ describe('getUiSchema', () => { }) }) + describe('when the concept type is visualization-draft', () => { + test('returns the UMM-VIS schema', () => { + expect(getUiSchema('Visualization')).toEqual(visualizationUiSchema) + }) + }) + describe('when the concept type is not recognized', () => { test('returns null', () => { expect(getUiSchema('bad-draft')).toEqual(null) diff --git a/static/src/js/utils/__tests__/getUmmVersion.test.js b/static/src/js/utils/__tests__/getUmmVersion.test.js index e64f16a2f..06018d924 100644 --- a/static/src/js/utils/__tests__/getUmmVersion.test.js +++ b/static/src/js/utils/__tests__/getUmmVersion.test.js @@ -1,4 +1,5 @@ -import { getUmmVersionsConfig } from '../../../../../sharedUtils/getConfig' +import { getUmmVersionsConfig } from 'sharedUtils/getConfig' + import getUmmVersion from '../getUmmVersion' const ummVersion = getUmmVersionsConfig() @@ -27,8 +28,14 @@ describe('getUmmVersion', () => { }) }) - describe('when the concept type is Collection', () => { - test('returns correct UMM-C version', () => { + describe('when the concept type is Visualization', () => { + test('returns correct UMM-VIS version', () => { + expect(getUmmVersion('Visualization')).toEqual(ummVersion.ummVis) + }) + }) + + describe('when the concept type is a bad name', () => { + test('returns null', () => { expect(getUmmVersion('bad-name')).toEqual(null) }) }) From 05c4010709d340f940070166364cef865ddf69bb Mon Sep 17 00:00:00 2001 From: Mandy Parson Date: Thu, 15 May 2025 10:43:09 -0600 Subject: [PATCH 4/7] MMT-3995: Addressing PR feedback --- static/src/js/components/MetadataForm/MetadataForm.jsx | 1 - static/src/js/hooks/usePublishMutation.js | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/static/src/js/components/MetadataForm/MetadataForm.jsx b/static/src/js/components/MetadataForm/MetadataForm.jsx index 1eb287af1..04dd9ec7b 100644 --- a/static/src/js/components/MetadataForm/MetadataForm.jsx +++ b/static/src/js/components/MetadataForm/MetadataForm.jsx @@ -204,7 +204,6 @@ const MetadataForm = () => { metadata: removeEmpty(ummMetadata), nativeId, providerId: fetchedMetadataProviderId || providerId, - // TODO pull this version number from a config ummVersion: getUmmVersion(derivedConceptType) }, onCompleted: (mutationData) => { diff --git a/static/src/js/hooks/usePublishMutation.js b/static/src/js/hooks/usePublishMutation.js index 40c40779c..43b806f4a 100644 --- a/static/src/js/hooks/usePublishMutation.js +++ b/static/src/js/hooks/usePublishMutation.js @@ -32,13 +32,10 @@ const usePublishMutation = (queryName) => { conceptType, nativeId ) => { - // Can be removed once CMR-10545 is complete - const publishNativeId = conceptType === 'Visualization' ? `MMT_${crypto.randomUUID()}` : nativeId - await publishDraftMutation({ variables: { draftConceptId: conceptId, - nativeId: publishNativeId, + nativeId, ummVersion: getUmmVersion(conceptType) }, onCompleted: (getPublishedData) => { From 0a16880b20fe813d5f0aac0e0263560d08fb9a2e Mon Sep 17 00:00:00 2001 From: Mandy Parson Date: Thu, 15 May 2025 12:05:09 -0600 Subject: [PATCH 5/7] MMT-3995: Creating work around --- static/src/js/hooks/useIngestDraftMutation.js | 11 ++++++++++- static/src/js/hooks/usePublishMutation.js | 14 +++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/static/src/js/hooks/useIngestDraftMutation.js b/static/src/js/hooks/useIngestDraftMutation.js index f2b46e743..bd260a2dd 100644 --- a/static/src/js/hooks/useIngestDraftMutation.js +++ b/static/src/js/hooks/useIngestDraftMutation.js @@ -20,11 +20,20 @@ const useIngestDraftMutation = () => { }) const ingestMutation = useCallback(async (conceptType, metadata, nativeId, providerId) => { + let draftNativeId = nativeId + + if (conceptType === 'Visualization') { + // Add '-draft' to the end of nativeId if it doesn't already end with it + draftNativeId = nativeId.endsWith('-draft') + ? nativeId + : `${nativeId}-draft` + } + await ingestDraftMutation({ variables: { conceptType, metadata, - nativeId, + nativeId: draftNativeId, providerId, ummVersion: getUmmVersion(conceptType) }, diff --git a/static/src/js/hooks/usePublishMutation.js b/static/src/js/hooks/usePublishMutation.js index 43b806f4a..f43b3fba4 100644 --- a/static/src/js/hooks/usePublishMutation.js +++ b/static/src/js/hooks/usePublishMutation.js @@ -32,10 +32,22 @@ const usePublishMutation = (queryName) => { conceptType, nativeId ) => { + // Can be removed once CMR-10545 is complete + let publishNativeId = nativeId + + if (conceptType === 'Visualization') { + // Remove '-draft' from the end of nativeId if it exists + publishNativeId = nativeId.endsWith('-draft') + ? nativeId.slice(0, -6) + : nativeId + } + + console.log('🚀 ~ usePublishMutation ~ publishNativeId:', publishNativeId) + await publishDraftMutation({ variables: { draftConceptId: conceptId, - nativeId, + nativeId: publishNativeId, ummVersion: getUmmVersion(conceptType) }, onCompleted: (getPublishedData) => { From 271eaa22a3b55c063d42e0a3735c306e5e039cde Mon Sep 17 00:00:00 2001 From: Mandy Parson Date: Fri, 16 May 2025 12:01:07 -0600 Subject: [PATCH 6/7] MMT-3995: Adding test files --- .../__tests__/useIngestDraftMutation.test.jsx | 273 ++++++++++++++++++ .../__tests__/usePublishMutation.test.jsx | 267 +++++++++++++++++ static/src/js/hooks/useIngestDraftMutation.js | 3 +- static/src/js/hooks/usePublishMutation.js | 2 - 4 files changed, 542 insertions(+), 3 deletions(-) create mode 100644 static/src/js/hooks/__tests__/useIngestDraftMutation.test.jsx create mode 100644 static/src/js/hooks/__tests__/usePublishMutation.test.jsx diff --git a/static/src/js/hooks/__tests__/useIngestDraftMutation.test.jsx b/static/src/js/hooks/__tests__/useIngestDraftMutation.test.jsx new file mode 100644 index 000000000..67e060324 --- /dev/null +++ b/static/src/js/hooks/__tests__/useIngestDraftMutation.test.jsx @@ -0,0 +1,273 @@ +import React from 'react' +import { + render, + screen, + waitFor +} from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { MockedProvider } from '@apollo/client/testing' +import { + MemoryRouter, + Route, + Routes +} from 'react-router-dom' + +import { INGEST_DRAFT } from '@/js/operations/mutations/ingestDraft' +import getUmmVersion from '@/js/utils/getUmmVersion' +import useIngestDraftMutation from '../useIngestDraftMutation' + +vi.mock('../utils/getHumanizedNameFromTypeParam', () => ({ + default: () => 'humanizedName' +})) + +// eslint-disable-next-line react/prop-types +const TestComponent = ({ conceptType, customNativeId }) => { + const { + ingestMutation, ingestDraft, error, loading + } = useIngestDraftMutation() + + const handleIngest = () => { + ingestMutation( + conceptType, + '{"some": "metadata"}', + customNativeId || 'test-native-id', + 'TEST_PROVIDER' + ) + } + + return ( +
+ + {loading && Loading} + { + error && ( + + Error: + {' '} + {error.message} + + ) + } + { + ingestDraft && ( + + Ingested: + {' '} + {JSON.stringify(ingestDraft)} + + ) + } +
+ ) +} + +const setup = (overrideMocks = [], initialEntries = ['/']) => { + const user = userEvent.setup() + + render( + + + + } /> + + + + ) + + return { user } +} + +describe('useIngestDraftMutation', () => { + describe('when ingesting a concept', () => { + test('calls ingestDraftMutation with the correct variables', async () => { + const mocks = [{ + request: { + query: INGEST_DRAFT, + variables: { + conceptType: 'Tool', + metadata: '{"some": "metadata"}', + nativeId: 'test-native-id', + providerId: 'TEST_PROVIDER', + ummVersion: getUmmVersion('Tool') + } + }, + result: { + data: { + ingestDraft: { + conceptId: 'TD1000000-MMT', + revisionId: '1' + } + } + } + }] + + const { user } = setup(mocks) + + const ingestButton = screen.getByText('Ingest Tool') + await user.click(ingestButton) + + await waitFor(() => { + expect(screen.getByText(/Ingested:/)).toBeInTheDocument() + }) + + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText(/Error:/)).not.toBeInTheDocument() + }) + }) + + describe('when ingesting a Visualization concept', () => { + test('adds "-draft" to the nativeId if not present', async () => { + const mocks = [{ + request: { + query: INGEST_DRAFT, + variables: { + conceptType: 'Visualization', + metadata: '{"some": "metadata"}', + nativeId: 'test-native-id-draft', + providerId: 'TEST_PROVIDER', + ummVersion: getUmmVersion('Visualization') + } + }, + result: { + data: { + ingestDraft: { + conceptId: 'VISD1000000-MMT', + revisionId: '1' + } + } + } + }] + + const customSetup = (customMocks, initialEntries) => { + const user = userEvent.setup() + render( + + + + + ) + } + /> + + + + ) + + return { user } + } + + const { user } = customSetup(mocks, ['/']) + + const ingestButton = screen.getByText('Ingest Visualization') + await user.click(ingestButton) + + await waitFor(() => { + expect(screen.getByText(/Ingested:/)).toBeInTheDocument() + }) + + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText(/Error:/)).not.toBeInTheDocument() + }) + + test('does not modify nativeId for Visualization concepts when -draft is already present', async () => { + const mocks = [{ + request: { + query: INGEST_DRAFT, + variables: { + conceptType: 'Visualization', + metadata: '{"some": "metadata"}', + nativeId: 'test-native-id-draft', + providerId: 'TEST_PROVIDER', + ummVersion: getUmmVersion('Visualization') + } + }, + result: { + data: { + ingestDraft: { + conceptId: 'VISD1000000-MMT', + revisionId: '1' + } + } + } + }] + + const customSetup = (customMocks, initialEntries) => { + const user = userEvent.setup() + render( + + + + + ) + } + /> + + + + ) + + return { user } + } + + const { user } = customSetup(mocks, ['/']) + + const ingestButton = screen.getByText('Ingest Visualization') + await user.click(ingestButton) + + await waitFor(() => { + expect(screen.getByText(/Ingested:/)).toBeInTheDocument() + }) + + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText(/Error:/)).not.toBeInTheDocument() + }) + }) + + describe('when the ingestDraftMutation encounters an error', () => { + test('sets the error state', async () => { + const mocks = [{ + request: { + query: INGEST_DRAFT, + variables: { + conceptType: 'Tool', + metadata: '{"some": "metadata"}', + nativeId: 'test-native-id', + providerId: 'TEST_PROVIDER', + ummVersion: getUmmVersion('Tool') + } + }, + error: new Error('An error occurred') + }] + + const { user } = setup(mocks) + + const ingestButton = screen.getByText('Ingest Tool') + await user.click(ingestButton) + + await waitFor(() => { + expect(screen.getByText('Error: An error occurred')).toBeInTheDocument() + }) + + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText(/Ingested:/)).not.toBeInTheDocument() + }) + }) +}) diff --git a/static/src/js/hooks/__tests__/usePublishMutation.test.jsx b/static/src/js/hooks/__tests__/usePublishMutation.test.jsx new file mode 100644 index 000000000..d69e113d5 --- /dev/null +++ b/static/src/js/hooks/__tests__/usePublishMutation.test.jsx @@ -0,0 +1,267 @@ +import React from 'react' +import { + render, + screen, + waitFor +} from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { MockedProvider } from '@apollo/client/testing' +import { + MemoryRouter, + Route, + Routes +} from 'react-router-dom' + +import { PUBLISH_DRAFT } from '@/js/operations/mutations/publishDraft' +import getUmmVersion from '@/js/utils/getUmmVersion' + +import usePublishMutation from '../usePublishMutation' + +vi.mock('../utils/getHumanizedNameFromTypeParam', () => ({ + default: () => 'humanizedName' +})) + +// eslint-disable-next-line react/prop-types +const TestComponent = ({ queryName, customNativeId }) => { + const { + error, loading, publishDraft, publishMutation + } = usePublishMutation(queryName) + console.log('🚀 ~ TestComponent ~ loading:', loading) + + return ( +
+ + + {loading && Loading} + { + error && ( + + Error: + {' '} + {error.message} + + ) + } + { + publishDraft && ( + + Published: + {' '} + {publishDraft.conceptId} + + ) + } +
+ ) +} + +const setup = (overrideMocks = [], initialEntries = ['/drafts/TD1000000-MMT']) => { + const user = userEvent.setup() + + render( + + + + } /> + + + + ) + + return { user } +} + +describe('usePublishMutation', () => { + describe('when publishing a concept', () => { + test('calls publishDraftMutation with the correct variables', async () => { + const mocks = [{ + request: { + query: PUBLISH_DRAFT, + variables: { + draftConceptId: 'TD1000000-MMT', + nativeId: 'test-native-id', + ummVersion: getUmmVersion('Tool') + } + }, + result: { + data: { + publishDraft: { + conceptId: 'T1000000-MMT', + revisionId: '1' + } + } + } + }] + + const { user } = setup(mocks) + + const publishButton = screen.getByText('Publish Tool') + await user.click(publishButton) + + await waitFor(() => { + expect(screen.getByText('Published: T1000000-MMT')).toBeInTheDocument() + }) + + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText(/Error:/)).not.toBeInTheDocument() + }) + }) + + describe('when publishing a Visualization concept', () => { + test('removes "-draft" from the nativeId before calling publishDraftMutation', async () => { + const mocks = [{ + request: { + query: PUBLISH_DRAFT, + variables: { + draftConceptId: 'VISD1000000-MMT', + nativeId: 'test-native-id', + ummVersion: getUmmVersion('Visualization') + } + }, + result: { + data: { + publishDraft: { + conceptId: 'VIS1000000-MMT', + revisionId: '1' + } + } + } + }] + + const { user } = setup(mocks, ['/drafts/VISD1000000-MMT']) + + const publishButton = screen.getByText('Publish Visualization') + await user.click(publishButton) + + await waitFor(() => { + expect(screen.getByText('Published: VIS1000000-MMT')).toBeInTheDocument() + }) + + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText(/Error:/)).not.toBeInTheDocument() + }) + }) + + test('does not modify nativeId for Visualization concepts when -draft is not present', async () => { + const mocks = [{ + request: { + query: PUBLISH_DRAFT, + variables: { + draftConceptId: 'VISD1000000-MMT', + nativeId: 'test-native-id', // Note: no -draft suffix + ummVersion: getUmmVersion('Visualization') + } + }, + result: { + data: { + publishDraft: { + conceptId: 'VIS1000000-MMT', + revisionId: '1' + } + } + } + }] + + // We need to modify the setup to allow passing a custom nativeId + const customSetup = (customMocks, initialEntries) => { + const user = userEvent.setup() + render( + + + + + ) + } + /> + + + + ) + + return { user } + } + + const { user } = customSetup(mocks, ['/drafts/VISD1000000-MMT']) + + const publishButton = screen.getByText('Publish Visualization') + await user.click(publishButton) + + await waitFor(() => { + expect(screen.getByText('Published: VIS1000000-MMT')).toBeInTheDocument() + }) + }) + + describe('when the publishDraftMutation encounters an error', () => { + test('sets the error state', async () => { + const mocks = [{ + request: { + query: PUBLISH_DRAFT, + variables: { + draftConceptId: 'TD1000000-MMT', + nativeId: 'test-native-id', + ummVersion: getUmmVersion('Tool') + } + }, + error: new Error('An error occurred') + }] + + const { user } = setup(mocks) + + const publishButton = screen.getByText('Publish Tool') + await user.click(publishButton) + + await waitFor(() => { + expect(screen.getByText('Error: An error occurred')).toBeInTheDocument() + }) + + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText(/Published:/)).not.toBeInTheDocument() + }) + }) + + describe('error handling', () => { + test('sets error state when mutation fails', async () => { + const errorMessage = 'Test error message' + const mocks = [{ + request: { + query: PUBLISH_DRAFT, + variables: { + draftConceptId: 'TD1000000-MMT', + nativeId: 'test-native-id', + ummVersion: getUmmVersion('Tool') + } + }, + error: new Error(errorMessage) + }] + + const { user } = setup(mocks) + + const publishButton = screen.getByText('Publish Tool') + + // Click the button + await user.click(publishButton) + + // Wait for error to be displayed + await waitFor(() => { + expect(screen.getByText(`Error: ${errorMessage}`)).toBeInTheDocument() + }) + + // Ensure that loading state is false + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + + // Ensure that publishDraft state wasn't set + expect(screen.queryByText(/Published:/)).not.toBeInTheDocument() + }) + }) +}) diff --git a/static/src/js/hooks/useIngestDraftMutation.js b/static/src/js/hooks/useIngestDraftMutation.js index bd260a2dd..8df513407 100644 --- a/static/src/js/hooks/useIngestDraftMutation.js +++ b/static/src/js/hooks/useIngestDraftMutation.js @@ -23,7 +23,8 @@ const useIngestDraftMutation = () => { let draftNativeId = nativeId if (conceptType === 'Visualization') { - // Add '-draft' to the end of nativeId if it doesn't already end with it + // Add '-draft' to the end of nativeId if it doesn't already end with it. + // Can be removed after CMR-10545 is complete draftNativeId = nativeId.endsWith('-draft') ? nativeId : `${nativeId}-draft` diff --git a/static/src/js/hooks/usePublishMutation.js b/static/src/js/hooks/usePublishMutation.js index f43b3fba4..31cbc2cd2 100644 --- a/static/src/js/hooks/usePublishMutation.js +++ b/static/src/js/hooks/usePublishMutation.js @@ -42,8 +42,6 @@ const usePublishMutation = (queryName) => { : nativeId } - console.log('🚀 ~ usePublishMutation ~ publishNativeId:', publishNativeId) - await publishDraftMutation({ variables: { draftConceptId: conceptId, From 11c22c359c686f3fef9a2c805954d331c9b81499 Mon Sep 17 00:00:00 2001 From: Mandy Parson Date: Mon, 19 May 2025 12:37:28 -0600 Subject: [PATCH 7/7] MMT-3995: Removing console.log --- static/src/js/hooks/__tests__/usePublishMutation.test.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/src/js/hooks/__tests__/usePublishMutation.test.jsx b/static/src/js/hooks/__tests__/usePublishMutation.test.jsx index d69e113d5..989751a26 100644 --- a/static/src/js/hooks/__tests__/usePublishMutation.test.jsx +++ b/static/src/js/hooks/__tests__/usePublishMutation.test.jsx @@ -26,7 +26,6 @@ const TestComponent = ({ queryName, customNativeId }) => { const { error, loading, publishDraft, publishMutation } = usePublishMutation(queryName) - console.log('🚀 ~ TestComponent ~ loading:', loading) return (