From 0165b80839441bb8751df2fd214928753a4be0a6 Mon Sep 17 00:00:00 2001 From: Carolina Capetillo Date: Wed, 11 Dec 2024 12:49:28 -0800 Subject: [PATCH 1/7] adds getfilterlist utils --- src/utils/populateInstrumentsUtils.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/utils/populateInstrumentsUtils.js diff --git a/src/utils/populateInstrumentsUtils.js b/src/utils/populateInstrumentsUtils.js new file mode 100644 index 0000000..5014ab2 --- /dev/null +++ b/src/utils/populateInstrumentsUtils.js @@ -0,0 +1,20 @@ +import { fetchApiCall } from './api.js' +import { useConfigurationStore } from '../stores/configuration.js' + +export const getFilterList = async () => { + const configurationStore = useConfigurationStore() + const response = await fetchApiCall({ + url: `${configurationStore.observationPortalUrl}instruments`, + method: 'GET' + }) + const filterList = [] + Object.values(response).forEach((instrument) => { + if (instrument.class === '0m4' && instrument.optical_elements.filters) { + const schedulableFilters = instrument.optical_elements.filters + .filter(filter => filter.schedulable) + .map(filter => ({ name: filter.name, code: filter.code })) + filterList.push(...schedulableFilters) + } + }) + return filterList +} From c8e09796beac729a71b3ecaf72502719c65a41fb Mon Sep 17 00:00:00 2001 From: Carolina Capetillo Date: Wed, 11 Dec 2024 12:49:52 -0800 Subject: [PATCH 2/7] updates component to use the utils function to populate filter list --- .../RealTimeInterface/SessionStarted.vue | 22 +++-------------- .../Scheduling/SchedulingSettings.vue | 24 +++---------------- 2 files changed, 6 insertions(+), 40 deletions(-) diff --git a/src/components/RealTimeInterface/SessionStarted.vue b/src/components/RealTimeInterface/SessionStarted.vue index a2cd13f..a66bfd8 100644 --- a/src/components/RealTimeInterface/SessionStarted.vue +++ b/src/components/RealTimeInterface/SessionStarted.vue @@ -8,12 +8,11 @@ import { calcAltAz } from '../../utils/visibility.js' import { useRealTimeSessionsStore } from '../../stores/realTimeSessions' import sites from '../../utils/sites.JSON' import { fetchApiCall } from '../../utils/api' +import { getFilterList } from '../../utils/populateInstrumentsUtils' import { useConfigurationStore } from '../../stores/configuration' -import { useUserDataStore } from '../../stores/userData' const realTimeSessionsStore = useRealTimeSessionsStore() const configurationStore = useConfigurationStore() -const userDataStore = useUserDataStore() const isCapturingImages = computed(() => { if (configurationStore.demo == true) { @@ -144,21 +143,6 @@ const sendGoCommand = async () => { }) } -// This should change from configdbUrl/opticalelementgroups/128/ to -// https://observe.lco.global/api/instruments and map the instruments based on what telescope is being used -const getFilterList = async () => { - await fetchApiCall({ - url: configurationStore.configdbUrl + 'opticalelementgroups/128/', - method: 'GET', - successCallback: (data) => { - filterList.value = data.optical_elements - .filter(filter => filter.schedulable) - .map(filter => ({ name: filter.name, code: filter.code })) - }, - failCallback: (error) => { console.error('API failed with error', error) } - }) -} - function updateRenderGallery (value) { if (!value) { realTimeSessionsStore.updateImageCaptureState(false) @@ -174,9 +158,9 @@ watch(exposureTime, (newTime) => { exposureError.value = '' }) -onMounted(() => { +onMounted(async () => { loading.value = false - getFilterList() + filterList.value = await getFilterList() }) diff --git a/src/components/Scheduling/SchedulingSettings.vue b/src/components/Scheduling/SchedulingSettings.vue index 723645b..3304296 100644 --- a/src/components/Scheduling/SchedulingSettings.vue +++ b/src/components/Scheduling/SchedulingSettings.vue @@ -2,6 +2,7 @@ import { ref, reactive, computed, defineProps, defineEmits, onMounted } from 'vue' import { useConfigurationStore } from '../../stores/configuration.js' import { fetchApiCall } from '../../utils/api.js' +import { getFilterList } from '../../utils/populateInstrumentsUtils.js' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' const props = defineProps({ @@ -36,25 +37,6 @@ const settings = reactive({ count: '' }) -const getFilterList = async () => { - await fetchApiCall({ - url: configurationStore.observationPortalUrl + 'instruments', - method: 'GET', - successCallback: (data) => { - Object.values(data).forEach(instrument => { - // Checks if the instrument has a "class" of "0m4" and contains filters in optical_elements - if (instrument.class === '0m4' && instrument.optical_elements.filters) { - const schedulableFilters = instrument.optical_elements.filters - .filter(filter => filter.schedulable) - .map(filter => ({ name: filter.name, code: filter.code })) - filterList.value.push(...schedulableFilters) - } - }) - }, - failCallback: (error) => { console.error('API failed with error', error) } - }) -} - // State for enabling/disabling fields const targetEnabled = computed(() => { return props.showProjectField ? projectName.value.trim() !== '' : true @@ -139,8 +121,8 @@ const formatExposures = (exposures) => { return exposures.map(exposure => `${exposure.filterName} - ${exposure.exposureTime}s x ${exposure.count}`).join(', ') } -onMounted(() => { - getFilterList() +onMounted(async () => { + filterList.value = await getFilterList() }) From 07f66ae92575e4b673da1bbc497b4b2bd34e879f Mon Sep 17 00:00:00 2001 From: Carolina Capetillo Date: Wed, 11 Dec 2024 14:01:44 -0800 Subject: [PATCH 3/7] Adds test for getfilterlist --- .../utils/populateInstrumentsUtils.test.js | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/tests/unit/utils/populateInstrumentsUtils.test.js diff --git a/src/tests/unit/utils/populateInstrumentsUtils.test.js b/src/tests/unit/utils/populateInstrumentsUtils.test.js new file mode 100644 index 0000000..52971e1 --- /dev/null +++ b/src/tests/unit/utils/populateInstrumentsUtils.test.js @@ -0,0 +1,89 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest' +import { getFilterList } from '../../../utils/populateInstrumentsUtils.js' +import { fetchApiCall } from '../../../utils/api.js' +import { createTestStores } from '../../../utils/testUtils' + +vi.mock('@/utils/api.js', () => ({ + fetchApiCall: vi.fn() +})) + +describe('thumbnailsUtils.js', () => { + let configurationStore + + beforeEach(() => { + const { configurationStore: store } = createTestStores() + configurationStore = store + fetchApiCall.mockClear() + }) + + describe('getFilterList', () => { + beforeEach(() => { + fetchApiCall.mockClear() + }) + + it('fetches instruments and returns schedulable filters', async () => { + // Mock API response + fetchApiCall.mockResolvedValueOnce({ + '0M4-SCICAM-QHY600': { + class: '0m4', + optical_elements: { + filters: [ + { name: 'Filter A', code: 'FA', schedulable: true }, + { name: 'Filter B', code: 'FB', schedulable: false } + ] + } + }, + '0M4-SCICAM-FLI': { + class: '0m4', + optical_elements: { + filters: [ + { name: 'Filter D', code: 'FD', schedulable: true }, + { name: 'Filter E', code: 'FE', schedulable: true } + ] + } + }, + // This instrument should be ignored + '1M0-SCICAM': { + class: '1m0', + optical_elements: { + filters: [ + { name: 'Filter X', code: 'FX', schedulable: true } + ] + } + } + }) + + const result = await getFilterList() + + expect(result).toEqual([ + { name: 'Filter A', code: 'FA' }, + { name: 'Filter D', code: 'FD' }, + { name: 'Filter E', code: 'FE' } + ]) + + expect(fetchApiCall).toHaveBeenCalledWith( + expect.objectContaining({ + url: expect.stringContaining('instruments'), + method: 'GET' + }) + ) + }) + + it('returns an empty array if no schedulable filters are found', async () => { + fetchApiCall.mockResolvedValueOnce({ + '0M4-SCICAM-QHY600': { + class: '0m4', + optical_elements: { + filters: [ + { name: 'Filter A', code: 'FA', schedulable: false } + ] + } + } + }) + + const result = await getFilterList() + + expect(result).toEqual([]) + }) + }) +}) From 7a5e6179d446c5c2712d64a5a6b7499aefe1dd3e Mon Sep 17 00:00:00 2001 From: Carolina Capetillo Date: Wed, 11 Dec 2024 14:02:13 -0800 Subject: [PATCH 4/7] updates test to remove getfilterlist logic from this component test --- .../components/schedulingSettings.test.js | 75 ------------------- 1 file changed, 75 deletions(-) diff --git a/src/tests/integration/components/schedulingSettings.test.js b/src/tests/integration/components/schedulingSettings.test.js index 4f7f835..f613c91 100644 --- a/src/tests/integration/components/schedulingSettings.test.js +++ b/src/tests/integration/components/schedulingSettings.test.js @@ -16,31 +16,6 @@ describe('SchedulingSettings.vue', () => { fetchApiCall.mockClear() const { pinia } = createTestStores() - fetchApiCall.mockImplementationOnce(({ successCallback }) => { - if (successCallback) { - successCallback({ - '0M4-SCICAM-QHY600': { - 'class': '0m4', - 'optical_elements': { - 'filters': [ - { name: 'Filter A', code: 'FA', schedulable: true }, - { name: 'Filter B', code: 'FB', schedulable: false } - ] - } - }, - '0M4-SCICAM-FLI': { - 'class': '0m4', - 'optical_elements': { - 'filters': [ - { name: 'Filter D', code: 'FD', schedulable: true }, - { name: 'Filter E', code: 'FE', schedulable: true } - ] - } - } - }) - } - }) - wrapper = mount(SchedulingSettings, { global: { plugins: [pinia], @@ -53,28 +28,6 @@ describe('SchedulingSettings.vue', () => { }) }) - it('calls fetchApiCall to get filter list on mount', async () => { - expect(fetchApiCall).toHaveBeenCalledWith( - expect.objectContaining({ - url: 'http://mock-api.com/instruments', - method: 'GET', - successCallback: expect.any(Function), - failCallback: expect.any(Function) - }) - ) - - await flushPromises() - - // filters through the non-schedulable filters - expect(wrapper.vm.filterList).toEqual([ - { name: 'Filter A', code: 'FA' }, - { name: 'Filter D', code: 'FD' }, - { name: 'Filter E', code: 'FE' } - ]) - - expect(fetchApiCall).toHaveBeenCalledTimes(1) - }) - it('fetches RA and Dec based on the target name and updates state correctly', async () => { const mockRaDecResponse = { 'dec': '+41 16 07.50', @@ -116,32 +69,4 @@ describe('SchedulingSettings.vue', () => { expect(wrapper.vm.targetError).toBe('Target not found, try another target.') }) - - it('emits exposuresUpdated when exposures are added', async () => { - // Checks that filterList has been populated correctly - expect(wrapper.vm.filterList).toEqual([ - { name: 'Filter A', code: 'FA' }, - { name: 'Filter D', code: 'FD' }, - { name: 'Filter E', code: 'FE' } - ]) - - // Simulates selecting a filter from the dropdown - wrapper.vm.settings.filter = 'FA' - wrapper.vm.settings.filterName = 'Filter A' - wrapper.vm.settings.exposureTime = '30' - wrapper.vm.settings.count = '15' - - await wrapper.vm.addExposure() - - // Check that the event is emitted with the expected payload - expect(wrapper.emitted().exposuresUpdated).toBeTruthy() - expect(wrapper.emitted().exposuresUpdated[0][0]).toEqual([ - { - filter: 'FA', - filterName: 'Filter A', - exposureTime: '30', - count: '15' - } - ]) - }) }) From 258cfcbc2311244015ec506b7e5b120b32be27dc Mon Sep 17 00:00:00 2001 From: Carolina Capetillo Date: Wed, 11 Dec 2024 14:02:39 -0800 Subject: [PATCH 5/7] removes unused vars --- src/components/Scheduling/SchedulingSettings.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Scheduling/SchedulingSettings.vue b/src/components/Scheduling/SchedulingSettings.vue index 3304296..5e08d1a 100644 --- a/src/components/Scheduling/SchedulingSettings.vue +++ b/src/components/Scheduling/SchedulingSettings.vue @@ -1,7 +1,6 @@