From 65865b76555b097d8606436d7b544ba0a330c8c9 Mon Sep 17 00:00:00 2001 From: JISOO LEE Date: Wed, 27 Sep 2023 13:19:31 -0400 Subject: [PATCH] Add test cases for Create CustomRuns --- src/api/customRuns.js | 6 - .../CreateCustomRun/CreateCustomRun.jsx | 219 ------------------ .../CreateCustomRun/CreateCustomRun.test.jsx | 161 +++++++++++++ src/containers/CustomRuns/CustomRuns.jsx | 21 +- src/nls/messages_de.json | 1 + src/nls/messages_en.json | 1 + src/nls/messages_es.json | 1 + src/nls/messages_fr.json | 1 + src/nls/messages_it.json | 1 + src/nls/messages_ja.json | 1 + src/nls/messages_ko.json | 1 + src/nls/messages_pt.json | 1 + src/nls/messages_zh-Hans.json | 1 + src/nls/messages_zh-Hant.json | 1 + 14 files changed, 177 insertions(+), 240 deletions(-) create mode 100644 src/containers/CreateCustomRun/CreateCustomRun.test.jsx diff --git a/src/api/customRuns.js b/src/api/customRuns.js index e5fc8c1fa9..224e109705 100644 --- a/src/api/customRuns.js +++ b/src/api/customRuns.js @@ -18,7 +18,6 @@ import { deleteRequest, get, patch, post } from './comms'; import { getQueryParams, getTektonAPI, - getTektonPipelinesAPIVersion, removeSystemAnnotations, removeSystemLabels, useCollection, @@ -43,10 +42,8 @@ function getCustomRunsAPI({ filters, isWebSocket, name, namespace }) { } export function getCustomRunPayload({ - kind, labels, namespace, - nodeSelector, params, serviceAccount, customName, @@ -76,9 +73,6 @@ export function getCustomRunPayload({ value: params[name] })); } - if (nodeSelector) { - payload.spec.podTemplate = { nodeSelector }; - } if (serviceAccount) { payload.spec.serviceAccountName = serviceAccount; } diff --git a/src/containers/CreateCustomRun/CreateCustomRun.jsx b/src/containers/CreateCustomRun/CreateCustomRun.jsx index 76ccb3c449..2636c175d3 100644 --- a/src/containers/CreateCustomRun/CreateCustomRun.jsx +++ b/src/containers/CreateCustomRun/CreateCustomRun.jsx @@ -59,16 +59,6 @@ const initialState = { workspaces: '' }; -const initialParamsState = paramSpecs => { - if (!paramSpecs) { - return {}; - } - return paramSpecs.reduce( - (acc, param) => ({ ...acc, [param.name]: param.default || '' }), - {} - ); -}; - const itemToString = ({ text }) => text; function CreateCustomRun() { @@ -98,11 +88,6 @@ function CreateCustomRun() { ); } - function isYAMLMode() { - const urlSearchParams = new URLSearchParams(location.search); - return urlSearchParams.get('mode') === 'yaml'; - } - const { kind: initialCustomKind, customName: customRefFromDetails } = getCustomDetails(); const [ @@ -130,7 +115,6 @@ function CreateCustomRun() { kind: initialCustomKind || 'Custom', namespace: getNamespace(), customRef: customRefFromDetails, - params: initialParamsState(null) }); const { data: custom, error: customError } = useTaskByKind( @@ -147,129 +131,6 @@ function CreateCustomRun() { }) }); - function switchToYamlMode() { - const queryParams = new URLSearchParams(location.search); - queryParams.set('mode', 'yaml'); - const browserURL = location.pathname.concat(`?${queryParams.toString()}`); - navigate(browserURL); - } - - function checkFormValidation() { - // Namespace, customRef, and Params must all have values - const validNamespace = !!namespace; - const validCustomRef = !!customRef; - - const paramSpecMap = keyBy(paramSpecs, 'name'); - const validParams = - !params || - Object.keys(params).reduce( - (acc, name) => - acc && - (!!params[name] || - typeof paramSpecMap[name]?.default !== 'undefined'), - true - ); - - // CustomRun name - const customRunNameTest = - !customRunName || - (resourceNameRegex.test(customRunName) && customRunName.length < 64); - setState(state => ({ ...state, validCustomRunName: customRunNameTest })); - - // Labels - let validLabels = true; - labels.forEach(label => { - ['key', 'value'].forEach(type => { - if (!isValidLabel(type, label[type])) { - validLabels = false; - setState(prevState => ({ - ...prevState, - invalidLabels: { - ...prevState.invalidLabels, - [`${label.id}-${type}`]: true - } - })); - } - }); - }); - - return ( - validNamespace && - validCustomRef && - validParams && - validLabels && - customRunNameTest - ); - } - - function handleClose() { - const { kind: customKind, customName } = getCustomDetails(); - let url = urls.customRuns.all(); - if (customName && namespace && namespace !== ALL_NAMESPACES) { - url = urls.customRuns[ - customKind === 'ClusterTask' ? 'byClusterTask' : 'byTask' - ]({ - namespace, - customName - }); - } else if (namespace && namespace !== ALL_NAMESPACES) { - url = urls.customRuns.byNamespace({ namespace }); - } - navigate(url); - } - - function handleAddLabel(prop) { - setState(prevState => ({ - ...prevState, - [prop]: [ - ...prevState[prop], - { - id: generateId(`label${prevState[prop].length}-`), - key: '', - keyPlaceholder: 'key', - value: '', - valuePlaceholder: 'value' - } - ] - })); - } - - function handleRemoveLabel(prop, invalidProp, index) { - setState(prevState => { - const newLabels = [...prevState[prop]]; - const newInvalidLabels = { ...prevState[invalidProp] }; - const removedLabel = newLabels[index]; - newLabels.splice(index, 1); - if (removedLabel.id in newInvalidLabels) { - delete newInvalidLabels[`${removedLabel.id}-key`]; - delete newInvalidLabels[`${removedLabel.id}-value`]; - } - return { - ...prevState, - [prop]: newLabels, - [invalidProp]: newInvalidLabels - }; - }); - } - - function handleChangeLabel(prop, invalidProp, { type, index, value }) { - setState(prevState => { - const newLabels = [...prevState[prop]]; - newLabels[index][type] = value; - const newInvalidLabels = { ...prevState[invalidProp] }; - if (!isValidLabel(type, value)) { - newInvalidLabels[`${newLabels[index].id}-${type}`] = true; - } else { - delete newInvalidLabels[`${newLabels[index].id}-${type}`]; - } - return { - ...prevState, - [prop]: newLabels, - [invalidProp]: newInvalidLabels - }; - }); - } - function handleCloseYAMLEditor() { let url = urls.customRuns.all(); if (defaultNamespace && defaultNamespace !== ALL_NAMESPACES) { @@ -288,86 +149,6 @@ function CreateCustomRun() { }); } - function handleNamespaceChange({ selectedItem }) { - const { text = '' } = selectedItem || {}; - if (text !== namespace) { - setState(state => ({ - ...state, - ...initialState, - kind: state.kind, - namespace: text - })); - - const queryParams = new URLSearchParams(location.search); - if (text) { - queryParams.set('namespace', text); - } else { - queryParams.delete('namespace'); - } - queryParams.delete('customName'); - const browserURL = location.pathname.concat(`?${queryParams.toString()}`); - navigate(browserURL); - } - } - - function handleKindChange({ selectedItem }) { - const { text = '' } = selectedItem || {}; - if (text !== kind) { - setState(state => ({ - ...state, - ...initialState, - kind: text - })); - - const queryParams = new URLSearchParams(location.search); - queryParams.set('kind', text); - queryParams.delete('namespace'); - queryParams.delete('customName'); - const browserURL = location.pathname.concat(`?${queryParams.toString()}`); - navigate(browserURL); - } - } - - function handleParamChange(key, value) { - setState(state => ({ - ...state, - params: { - ...state.params, - [key]: value - } - })); - } - - function handleCustomChange({ selectedItem }) { - const { text } = selectedItem || {}; - - const queryParams = new URLSearchParams(location.search); - if (text) { - queryParams.set('customName', text); - } else { - queryParams.delete('customName'); - } - const browserURL = location.pathname.concat(`?${queryParams.toString()}`); - navigate(browserURL); - - if (text && text !== customRef) { - setState(state => { - return { - ...state, - customRef: text, - params: initialParamsState(paramSpecs) - }; - }); - return; - } - // Reset params when no Task is selected - setState(state => ({ - ...state, - ...initialState, - namespace: state.namespace - })); - } - function handleSubmit(event) { event.preventDefault(); diff --git a/src/containers/CreateCustomRun/CreateCustomRun.test.jsx b/src/containers/CreateCustomRun/CreateCustomRun.test.jsx new file mode 100644 index 0000000000..03be65fe87 --- /dev/null +++ b/src/containers/CreateCustomRun/CreateCustomRun.test.jsx @@ -0,0 +1,161 @@ +/* +Copyright 2023 The Tekton Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { fireEvent, waitFor } from '@testing-library/react'; + +import { renderWithRouter } from '../../utils/test'; + +import CreateCustomRun from './CreateCustomRun'; +import * as APIUtils from '../../api/utils'; +import * as CustomRunsAPI from '../../api/customRuns'; + +const submitButton = allByText => allByText('Create')[0]; + +const customRunRawGenerateName = { + apiVersion: 'tekton.dev/v1beta1', + kind: 'CustomRun', + metadata: { + annotations: {}, + generateName: 'test-custom-run-name-', + labels: {}, + namespace: 'test-namespace' + }, + spec: { + customRef: { + apiVersion: 'example.dev/v1beta1' + } + } +}; + +const expectedCustomRun = `apiVersion: tekton.dev/v1beta1 +kind: CustomRun +metadata: + name: run-1111111111 + namespace: test-namespace + labels: {} +spec: + customRef: + apiVersion: '' + kind: Custom + params: []`; + +const expectedCustomRunOneLine = expectedCustomRun.replace(/\r?\n|\r/g, ''); + +const findNameRegexp = /name: run-\S+/; + +describe('CreateCustomRun yaml mode', () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(window.history, 'pushState'); + + // Workaround for codemirror vs jsdom https://github.com/jsdom/jsdom/issues/3002#issuecomment-1118039915 + // for textRange(...).getClientRects is not a function + Range.prototype.getBoundingClientRect = () => ({ + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0 + }); + Range.prototype.getClientRects = () => ({ + item: () => null, + length: 0, + [Symbol.iterator]: vi.fn() + }); + }); + + it('renders with namespace', async () => { + vi.spyOn(CustomRunsAPI, 'createCustomRunRaw').mockImplementation(() => + Promise.resolve({ data: {} }) + ); + vi.spyOn(CustomRunsAPI, 'useCustomRun').mockImplementation(() => ({ + data: customRunRawGenerateName + })); + + const { getByRole, queryAllByText } = renderWithRouter( + , + { + path: '/customruns/create', + route: '/customruns/create?mode=yaml&namespace=test-namespace' + } + ); + + await waitFor( + () => { + expect(queryAllByText(/Loading/).length).toBe(0); + }, + { + timeout: 3000 + } + ); + await waitFor(() => { + expect(queryAllByText(/Loading/).length).toBe(0); + }); + await waitFor(() => { + expect(getByRole(/textbox/)).toBeTruthy(); + }); + let actual = getByRole(/textbox/).textContent; + actual = actual.replace(findNameRegexp, 'name: run-1111111111'); + expect(actual.trim()).toEqual(expectedCustomRunOneLine); + }); + + it('handle submit with customrun and namespace', async () => { + vi.spyOn(CustomRunsAPI, 'createCustomRunRaw').mockImplementation(() => + Promise.resolve({ data: {} }) + ); + vi.spyOn(CustomRunsAPI, 'useCustomRun').mockImplementation(() => ({ + data: customRunRawGenerateName + })); + + const { queryAllByText } = renderWithRouter(, { + path: '/customruns/create', + route: + '/customruns/create?mode=yaml&customRunName=test-custom-run-name&namespace=test-namespace' + }); + + await waitFor( + () => { + expect(queryAllByText(/Loading/).length).toBe(0); + }, + { timeout: 3000 } + ); + expect(submitButton(queryAllByText)).toBeTruthy(); + + fireEvent.click(submitButton(queryAllByText)); + + await waitFor(() => { + expect(CustomRunsAPI.createCustomRunRaw).toHaveBeenCalledTimes(1); + }); + expect(CustomRunsAPI.createCustomRunRaw).toHaveBeenCalledWith( + expect.objectContaining({ + namespace: 'test-namespace', + payload: customRunRawGenerateName + }) + ); + await waitFor(() => { + expect(window.history.pushState).toHaveBeenCalledTimes(2); + }); + }); + + it('handles onClose event', () => { + vi.spyOn(APIUtils, 'useSelectedNamespace') + .mockImplementation(() => ({ selectedNamespace: 'namespace-1' })); + vi.spyOn(window.history, 'pushState'); + const { getByText } = renderWithRouter(); + fireEvent.click(getByText(/cancel/i)); + // will be called once for render (from test utils) and once on navigation + expect(window.history.pushState).toHaveBeenCalledTimes(2); + }); +}); diff --git a/src/containers/CustomRuns/CustomRuns.jsx b/src/containers/CustomRuns/CustomRuns.jsx index 90b7c7d8e3..11c603e78c 100644 --- a/src/containers/CustomRuns/CustomRuns.jsx +++ b/src/containers/CustomRuns/CustomRuns.jsx @@ -114,11 +114,6 @@ function CustomRuns() { const params = useParams(); const filters = getFilters(location); - const customFilter = filters.find(f => f.indexOf(`${CUSTOM}=`) !== -1) || ''; - const customName = customFilter.replace(`${labels.CUSTOM}=`, ''); - - const kind = 'Custom'; - useTitleSync({ page: 'CustomRuns' }); const { selectedNamespace } = useSelectedNamespace(); @@ -250,7 +245,7 @@ function CustomRuns() { }, { actionText: intl.formatMessage({ - id: 'dashboard.cancelCustomRun.actionText', + id: 'dashboard.cancelTaskRun.actionText', defaultMessage: 'Stop' }), action: cancel, @@ -322,16 +317,12 @@ function CustomRuns() { : [ { onClick: () => { - let queryString; - if (namespace !== ALL_NAMESPACES) { - queryString = new URLSearchParams({ - ...(namespace !== ALL_NAMESPACES && { namespace }) - }).toString(); - } - navigate( + const queryString = new URLSearchParams({ + ...(namespace !== ALL_NAMESPACES && { namespace }), // currently default is yaml mode - urls.customRuns.create() + (queryString ? `?${queryString}&mode=yaml` : '?mode=yaml') - ); + mode: 'yaml' + }).toString(); + navigate(urls.customRuns.create() + `?${queryString}`); }, text: intl.formatMessage({ id: 'dashboard.actions.createButton', diff --git a/src/nls/messages_de.json b/src/nls/messages_de.json index 0aa8bf0e4d..3358ad5d8c 100644 --- a/src/nls/messages_de.json +++ b/src/nls/messages_de.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "", "dashboard.clusterTriggerBinding.noParams": "", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "", "dashboard.createPipelineRun.disabled": "", "dashboard.createPipelineRun.enabled": "", diff --git a/src/nls/messages_en.json b/src/nls/messages_en.json index cb5b98e5ec..3960773896 100644 --- a/src/nls/messages_en.json +++ b/src/nls/messages_en.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "Select ClusterTask", "dashboard.clusterTriggerBinding.noParams": "No parameters found for this ClusterTriggerBinding.", "dashboard.create.yamlModeButton": "YAML Mode", + "dashboard.createCustomRun.title": "Create CustomRun", "dashboard.createPipelineRun.createError": "Error creating PipelineRun", "dashboard.createPipelineRun.disabled": "Disabled", "dashboard.createPipelineRun.enabled": "Enabled", diff --git a/src/nls/messages_es.json b/src/nls/messages_es.json index 06357b1f35..32d3a69e5e 100644 --- a/src/nls/messages_es.json +++ b/src/nls/messages_es.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "", "dashboard.clusterTriggerBinding.noParams": "", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "", "dashboard.createPipelineRun.disabled": "", "dashboard.createPipelineRun.enabled": "", diff --git a/src/nls/messages_fr.json b/src/nls/messages_fr.json index f7a9156c23..fcf1b5a856 100644 --- a/src/nls/messages_fr.json +++ b/src/nls/messages_fr.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "", "dashboard.clusterTriggerBinding.noParams": "", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "", "dashboard.createPipelineRun.disabled": "", "dashboard.createPipelineRun.enabled": "", diff --git a/src/nls/messages_it.json b/src/nls/messages_it.json index 80dd3e5939..bf61ae5407 100644 --- a/src/nls/messages_it.json +++ b/src/nls/messages_it.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "", "dashboard.clusterTriggerBinding.noParams": "", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "", "dashboard.createPipelineRun.disabled": "", "dashboard.createPipelineRun.enabled": "", diff --git a/src/nls/messages_ja.json b/src/nls/messages_ja.json index 350130280e..826c076181 100644 --- a/src/nls/messages_ja.json +++ b/src/nls/messages_ja.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "ClusterTaskを選択", "dashboard.clusterTriggerBinding.noParams": "このClusterTriggerBindingのパラメータが見つかりません", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "PipelineRunの作成中にエラーが発生しました", "dashboard.createPipelineRun.disabled": "", "dashboard.createPipelineRun.enabled": "", diff --git a/src/nls/messages_ko.json b/src/nls/messages_ko.json index 551b212798..db08a36b8d 100644 --- a/src/nls/messages_ko.json +++ b/src/nls/messages_ko.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "ClusterTask 선택", "dashboard.clusterTriggerBinding.noParams": "이 ClusterTriggerBinding에 대한 매개변수를 찾을 수 없습니다.", "dashboard.create.yamlModeButton": "YAML 모드", + "dashboard.createCustomRun.title": "CustomRun 만들기", "dashboard.createPipelineRun.createError": "PipelineRun 생성 오류", "dashboard.createPipelineRun.disabled": "비활성화", "dashboard.createPipelineRun.enabled": "활성화", diff --git a/src/nls/messages_pt.json b/src/nls/messages_pt.json index 98cade6c95..62d2d77211 100644 --- a/src/nls/messages_pt.json +++ b/src/nls/messages_pt.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "", "dashboard.clusterTriggerBinding.noParams": "", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "", "dashboard.createPipelineRun.disabled": "", "dashboard.createPipelineRun.enabled": "", diff --git a/src/nls/messages_zh-Hans.json b/src/nls/messages_zh-Hans.json index 59e475644c..c3777c612f 100644 --- a/src/nls/messages_zh-Hans.json +++ b/src/nls/messages_zh-Hans.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "选择 ClusterTask", "dashboard.clusterTriggerBinding.noParams": "未找到该 ClusterTriggerBinding 的参数。", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "创建 PipelineRun 时失败", "dashboard.createPipelineRun.disabled": "禁用", "dashboard.createPipelineRun.enabled": "启用", diff --git a/src/nls/messages_zh-Hant.json b/src/nls/messages_zh-Hant.json index ad49b137df..d24773a4f5 100644 --- a/src/nls/messages_zh-Hant.json +++ b/src/nls/messages_zh-Hant.json @@ -49,6 +49,7 @@ "dashboard.clusterTasksDropdown.label": "", "dashboard.clusterTriggerBinding.noParams": "", "dashboard.create.yamlModeButton": "", + "dashboard.createCustomRun.title": "", "dashboard.createPipelineRun.createError": "", "dashboard.createPipelineRun.disabled": "", "dashboard.createPipelineRun.enabled": "",