diff --git a/web/cypress/integration/route/create-and-delete-route.spec.js b/web/cypress/integration/route/create-edit-delete-route.spec.js similarity index 70% rename from web/cypress/integration/route/create-and-delete-route.spec.js rename to web/cypress/integration/route/create-edit-delete-route.spec.js index f7fa054c1b..167700c4f1 100644 --- a/web/cypress/integration/route/create-and-delete-route.spec.js +++ b/web/cypress/integration/route/create-edit-delete-route.spec.js @@ -18,23 +18,25 @@ context('Create and Delete Route', () => { const name = `routeName${new Date().valueOf()}`; + const newName = `newName${new Date().valueOf()}`; + const sleepTime = 100; beforeEach(() => { // init login cy.login(); }); - it('create route', () => { + it('should create route', () => { // go to route create page cy.visit('/'); cy.contains('Route').click(); cy.contains('Create').click(); - // input Name And Description + // input name and description cy.get('#name').type(name); cy.get('#desc').type('desc'); - // input Request Basic Define + // input request basic define cy.get('#hosts_0').type('11.11.11.11'); cy.get('[data-cy=addHost]').click(); cy.get('#hosts_1').type('12.12.12.12'); @@ -47,7 +49,7 @@ context('Create and Delete Route', () => { .contains('Create') .click(); - // create Advanced Routing Matching Conditions + // create advanced routing matching conditions cy.get('#position').click(); cy.contains('Cookie').click(); cy.get('.ant-modal').within(() => { @@ -60,6 +62,7 @@ context('Create and Delete Route', () => { // go to step2 cy.contains('Next').click(); + cy.wait(sleepTime * 3); cy.get('#nodes_0_host').type('12.12.12.12'); // go to step3 @@ -74,18 +77,40 @@ context('Create and Delete Route', () => { // go to step4 cy.contains('Next').click(); cy.contains('Submit').click(); - cy.contains('SubmitSuccessfully'); + cy.contains('Submit Successfully'); // back to route list page - cy.contains('Return Route List').click(); + cy.contains('Goto List').click(); cy.url().should('contains', 'routes/list'); }); - it('delete the route', () => { - cy.visit('/routes/list'); + it('should edit the route', () => { + cy.visit('/'); + cy.contains('Route').click(); + cy.get('[title=Name]').type(name); cy.contains('Search').click(); - cy.contains(name).siblings().contains('Delete').click(); + cy.wait(1000); + cy.contains(name).siblings().contains('Edit').click(); + + cy.get('#name').clear().type(newName); + cy.get('#desc').clear().type('new desc'); + cy.contains('Next').click(); + cy.wait(1000); + cy.contains('Next').click(); + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.contains('Submit Successfully'); + cy.contains('Goto List').click(); + cy.url().should('contains', 'routes/list'); + cy.contains(newName).siblings().should('contain', 'new desc'); + }); + + it('should delete the route', () => { + cy.visit('/routes/list'); + cy.get('[title=Name]').type(newName); + cy.contains('Search').click(); + cy.contains(newName).siblings().contains('Delete').click(); cy.contains('button', 'Confirm').click(); cy.get('.ant-notification-notice-message').should('contain', 'Delete Route Successfully'); }); diff --git a/web/cypress/integration/route/search-route.spec.js b/web/cypress/integration/route/search-route.spec.js new file mode 100644 index 0000000000..2ab5d10da6 --- /dev/null +++ b/web/cypress/integration/route/search-route.spec.js @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* eslint-disable no-undef */ + +context('Create and Search Route', () => { + beforeEach(() => { + // init login + cy.login(); + }); + + it('should create route test1, test2, test3', () => { + // go to route create page + cy.visit('/'); + cy.contains('Route').click(); + for (let i = 0; i < 3; i++) { + cy.contains('Create').click(); + cy.get('#name').type('test' + i); + cy.get('#desc').type('desc' + i); + cy.get('#hosts_0').type('11.11.11.11'); + cy.contains('Next').click(); + cy.wait(400); + cy.get('#nodes_0_host').type('12.12.12.12', { + timeout: 500, + }); + cy.contains('Next').click(); + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.contains('Submit Successfully'); + // back to route list page + cy.contains('Goto List').click(); + cy.url().should('contains', 'routes/list'); + } + }); + + it('should search the route', () => { + cy.visit('/'); + cy.contains('Route').click(); + // full match + cy.get('[title=Name]').type('test1'); + cy.contains('Search').click(); + cy.contains('test1').siblings().should('contain', 'desc1'); + cy.contains('test0').should('not.exist'); + cy.contains('test2').should('not.exist'); + // partial match + cy.reload(); + cy.get('[title=Name]').type('test'); + cy.contains('Search').click(); + cy.contains('test0').siblings().should('contain', 'desc0'); + cy.contains('test1').siblings().should('contain', 'desc1'); + cy.contains('test2').siblings().should('contain', 'desc2'); + // no match + cy.reload(); + cy.get('[title=Name]').type('testx'); + cy.contains('Search').click(); + cy.contains('test0').should('not.exist'); + cy.contains('test1').should('not.exist'); + cy.contains('test2').should('not.exist'); + }); + + it('should delete the route', () => { + cy.visit('/routes/list'); + for (let i = 0; i < 3; i++) { + cy.contains('test' + i) + .siblings() + .contains('Delete') + .click(); + cy.contains('button', 'Confirm').click(); + cy.get('.ant-notification-notice-message').should('contain', 'Delete Route Successfully'); + cy.wait(300); + } + }); +}); diff --git a/web/src/hooks/useForceIntl.ts b/web/src/hooks/useForceIntl.ts new file mode 100644 index 0000000000..8f67bef9c1 --- /dev/null +++ b/web/src/hooks/useForceIntl.ts @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { useEffect } from 'react'; +import { getIntl } from 'umi'; + +/** + * Force convert some texts with i18n + */ +const useForceIntl = () => { + useEffect(() => { + const hasProTable = Boolean(document.querySelector('.ant-pro-table-search')); + + if (!hasProTable) { + return; + } + + const { locale } = getIntl(); + if (locale === 'zh-cn') { + return; + } + + // NOTE: i18n in https://procomponents.ant.design/components/table/ is not working + const i18nMapper = [ + ['//span[text()="查 询"]', 'Search'], + ['//span[text()="重 置"]', 'Reset'], + ]; + + i18nMapper.forEach(([XPathExpression, targetText]) => { + const ele = document.evaluate(XPathExpression, document).iterateNext() as HTMLElement; + if (!ele) { + return; + } + ele.innerText = targetText; + }); + }, []); +}; + +export default useForceIntl; diff --git a/web/src/pages/Consumer/List.tsx b/web/src/pages/Consumer/List.tsx index 08f3dd611a..6bec2d2860 100644 --- a/web/src/pages/Consumer/List.tsx +++ b/web/src/pages/Consumer/List.tsx @@ -22,9 +22,13 @@ import { history, useIntl } from 'umi'; import { PlusOutlined } from '@ant-design/icons'; import { timestampToLocaleString } from '@/helpers'; +import useForceIntl from '@/hooks/useForceIntl'; + import { fetchList, remove } from './service'; const Page: React.FC = () => { + useForceIntl(); + const ref = useRef(); const { formatMessage } = useIntl(); diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx index 304ad3b179..3ac4019bea 100644 --- a/web/src/pages/Route/List.tsx +++ b/web/src/pages/Route/List.tsx @@ -22,12 +22,15 @@ import { history, useIntl } from 'umi'; import { PlusOutlined, BugOutlined } from '@ant-design/icons'; import { timestampToLocaleString } from '@/helpers'; +import useForceIntl from '@/hooks/useForceIntl'; import { fetchList, remove, fetchLabelList, updateRouteStatus } from './service'; import { DebugDrawView } from './components/DebugViews'; const { OptGroup, Option } = Select; const Page: React.FC = () => { + useForceIntl(); + const ref = useRef(); const { formatMessage } = useIntl(); diff --git a/web/src/pages/Route/components/ResultView/ResultView.tsx b/web/src/pages/Route/components/ResultView/ResultView.tsx index b70e035439..ee3ea70fc5 100644 --- a/web/src/pages/Route/components/ResultView/ResultView.tsx +++ b/web/src/pages/Route/components/ResultView/ResultView.tsx @@ -27,7 +27,7 @@ const ResultView: React.FC = (props) => { return ( { + useForceIntl(); const tableRef = useRef(); const { formatMessage } = useIntl(); diff --git a/web/src/pages/Service/List.tsx b/web/src/pages/Service/List.tsx index c14d11cb5a..4ec8d8536e 100644 --- a/web/src/pages/Service/List.tsx +++ b/web/src/pages/Service/List.tsx @@ -21,9 +21,13 @@ import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; import { PlusOutlined } from '@ant-design/icons'; import { Button, notification, Popconfirm, Space } from 'antd'; +import useForceIntl from '@/hooks/useForceIntl'; + import { fetchList, remove } from './service'; const Page: React.FC = () => { + useForceIntl(); + const ref = useRef(); const { formatMessage } = useIntl(); diff --git a/web/src/pages/Upstream/List.tsx b/web/src/pages/Upstream/List.tsx index 5af67af50b..b670967719 100644 --- a/web/src/pages/Upstream/List.tsx +++ b/web/src/pages/Upstream/List.tsx @@ -21,10 +21,13 @@ import { Popconfirm, Button, notification } from 'antd'; import { history, useIntl } from 'umi'; import { PlusOutlined } from '@ant-design/icons'; import { timestampToLocaleString } from '@/helpers'; +import useForceIntl from '@/hooks/useForceIntl'; import { fetchList, remove } from './service'; const Page: React.FC = () => { + useForceIntl(); + const ref = useRef(); const { formatMessage } = useIntl();