Skip to content

Commit

Permalink
feat: added E2E test for plugins (#1214)
Browse files Browse the repository at this point in the history
* fix(FE): delete global plugin failed (#1170)

* fix: delete global plugin failed

* fix: filter disable plugins

* fix: update online debug api protocol validation and error msg (#1166)

* fix: update validation and msg

* Update api/internal/handler/route_online_debug/route_online_debug.go

* fix: update refer to code review

Co-authored-by: 琚致远 <juzhiyuan@apache.org>

* feat(cli): Show GitHash for manager-api in branch v2.3 (backport #1162) (#1181)

* fix: correct Version and GitHash output for manager-api command (#1162)

* bug: fix Version and add GitHash for manager-api command

Signed-off-by: imjoey <majunjiev@gmail.com>

* feat: git hash support generating .githash for apache release

Signed-off-by: imjoey <majunjiev@gmail.com>

* feat: Add testcase for the new githash info

Signed-off-by: imjoey <majunjiev@gmail.com>

* feat: add test case for .githash content validation

Signed-off-by: imjoey <majunjiev@gmail.com>

* feat: Remove git command dependency for getting git hash

Signed-off-by: imjoey <majunjiev@gmail.com>

* feat: set VERSION to 2.3 in branch v2.3

Signed-off-by: imjoey <majunjiev@gmail.com>

* fix(fe): route search with status (#1205)

* fix(fe): route search with status

* fix: version and status select box allowclear

* fix: remove console

* fix: set create_time/update_time as omitempty (#1203)

Signed-off-by: imjoey <majunjiev@gmail.com>

* fix(FE): service issues (#1209)

* fix: omit checks when empty

* fix: desc search

* fix: omit checks when empty

* feat: remove desc search

* feat: add create service e2e test

* feat: update code

* feat: update code

* chore: sync json schema from Apache APISIX 2.2 (#1177)

* chore: sync json schema from Apache APISIX 2.2

* fix: remove schema of plugins that not enable by default

* fix test cases for plugin skywalking which is not enable by default

* chore: expose port for control API

* fix: control API config

* fix yaml format

* fix CI failed

* fix: log path

* fix: log path

Co-authored-by: 琚致远 <juzhiyuan@apache.org>

* fix: well handle with malformed auth token in request header (#1206) (#1210)

* fix: not panic if auth token is invalid

Signed-off-by: imjoey <majunjiev@gmail.com>

* do not record the false in log

Signed-off-by: imjoey <majunjiev@gmail.com>

Co-authored-by: Joey <majunjiev@gmail.com>

* fix: route list search query string (#1197)

* fix: route list search qurey string

* fix: well handle with malformed auth token in request header (#1206)

* fix: not panic if auth token is invalid

Signed-off-by: imjoey <majunjiev@gmail.com>

* do not record the false in log

Signed-off-by: imjoey <majunjiev@gmail.com>

* feat: add search lables e2e

* feat: add search route labels testcase

* feat: update code

* Update selector.json

* Update search-route.spec.js

Co-authored-by: Joey <majunjiev@gmail.com>
Co-authored-by: 琚致远 <juzhiyuan@apache.org>

* feat: init cypress with plugin

* style: codes format

* feat: added come testcases

* feat: use the correct api version

* feat: added tip

* feat: added tip

* feat: added test cases

* feat: added disable

* feat: added disable

* feat: added disable

* style: codes format

* feat: added ajv formats

* feat: remove useless codes

Co-authored-by: litesun <sunyi@apache.org>
Co-authored-by: liuxiran <belovedxixi@126.com>
Co-authored-by: Joey <majunjiev@gmail.com>
Co-authored-by: nic-chen <johz@163.com>
Co-authored-by: nic-chen <33000667+nic-chen@users.noreply.github.com>
  • Loading branch information
6 people authored Jan 7, 2021
1 parent 3f8060e commit 50fb547
Show file tree
Hide file tree
Showing 19 changed files with 1,566 additions and 75 deletions.
1,347 changes: 1,347 additions & 0 deletions web/cypress/fixtures/plugin-dataset.json

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions web/cypress/integration/plugin/schema-smocktest.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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('smoke test for plugin schema', () => {
beforeEach(() => {
cy.login();

cy.fixture('selector.json').as('selector');
cy.fixture('plugin-dataset.json').as('cases');
});

it('should visit plugin market', function () {
cy.visit('/');
cy.contains('Plugin').click();
cy.contains('Create').click();

const nameSelector = '[data-cy-plugin-name]';
cy.get(nameSelector).then(function (cards) {
[...cards].forEach((card) => {
const name = card.innerText;
const cases = this.cases[name] || [];
cases.forEach(({ shouldValid, data, type = '' }) => {
/**
* NOTE: This test is mainly for GlobalPlugin, which is using non-consumer-type schema.
*/
if (type === 'consumer') {
return true;
}

cy.contains(name)
.parents('.ant-card-bordered')
.within(() => {
cy.contains('Enable').click({
force: true,
});
});

// NOTE: wait for the Drawer to appear on the DOM
cy.wait(800);
const switchSelector = '#disable';
cy.get(switchSelector).click();

cy.window().then(({ codemirror }) => {
if (codemirror) {
codemirror.setValue(JSON.stringify(data));
}
});

cy.contains('Submit').click();

// NOTE: wait for the HTTP call
cy.wait(500);
if (shouldValid) {
const drawerSelector = '.ant-drawer-content';
cy.get(drawerSelector).should('not.exist');
} else {
cy.get(this.selector.notification).should('contain', 'Invalid plugin data');

cy.get('.anticon-close').click({
multiple: true,
});
cy.contains('Cancel').click({
force: true,
});
}
});
});
});
});
});
5 changes: 1 addition & 4 deletions web/cypress/integration/route/search-route.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ context('Create and Search Route', () => {
it('should delete the route', function () {
cy.visit('/routes/list');
for (let i = 0; i < 3; i += 1) {
cy.contains(`test${i}`)
.siblings()
.contains('Delete')
.click();
cy.contains(`test${i}`).siblings().contains('Delete').click();
cy.contains('button', 'Confirm').click();
cy.get(this.domSelector.notification).should('contain', 'Delete Route Successfully');
cy.wait(300);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@
/* eslint-disable no-undef */

context('create and delete service ', () => {

beforeEach(() => {
// init login
cy.login();
});

it('should create service', () => {

// go to create service page
cy.visit('/');
cy.contains('Service').click();
Expand All @@ -38,20 +36,18 @@ context('create and delete service ', () => {
cy.contains('Next').click();
cy.contains('Next').click();
cy.contains('Submit').click();
})
});

it('should delete the service', () => {
cy.visit('/');
cy.contains('Service').click();

cy.get('[title=Name]').type('service');
cy.contains('Search').click();

cy.contains('service').siblings().contains('Delete').click();
cy.contains('button', 'Confirm').click();
cy.fixture('selector.json').then(({
notification
}) => {
cy.fixture('selector.json').then(({ notification }) => {
cy.get(notification).should('contain', 'Delete Service Successfully');
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ context('Create and Delete Upstream', () => {
const sleepTime = 100; // the unit is milliseconds
const domSelectors = {
notification: '.ant-notification-notice-message',
selectItem: '.ant-select-item-option-content'
selectItem: '.ant-select-item-option-content',
};

beforeEach(() => {
Expand Down
3 changes: 2 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
"@rjsf/antd": "2.2.0",
"@rjsf/core": "2.2.0",
"@uiw/react-codemirror": "^3.0.1",
"ajv": "^7.0.0-rc.2",
"ajv": "^7.0.3",
"ajv-formats": "^1.5.1",
"antd": "^4.4.0",
"base-64": "^1.0.0",
"classnames": "^2.2.6",
Expand Down
50 changes: 37 additions & 13 deletions web/src/components/Plugin/PluginDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,40 @@
* limitations under the License.
*/
import React, { useEffect, useRef } from 'react';
import { Button, notification, PageHeader, Switch, Form, Select, Divider, Drawer, Alert } from 'antd';
import {
Button,
notification,
PageHeader,
Switch,
Form,
Select,
Divider,
Drawer,
Alert,
} from 'antd';
import { useIntl } from 'umi';
import CodeMirror from '@uiw/react-codemirror';
import { js_beautify } from 'js-beautify';
import { LinkOutlined } from '@ant-design/icons';

import Ajv, { DefinedError } from 'ajv';
import addFormats from 'ajv-formats';

import { fetchSchema } from './service';

type Props = {
name: string;
type?: 'global' | 'scoped';
schemaType: PluginComponent.Schema;
initialData: object;
pluginList: PluginComponent.Meta[],
pluginList: PluginComponent.Meta[];
readonly?: boolean;
visible: boolean;
onClose?: () => void;
onChange?: (data: any) => void;
};

const ajv = new Ajv();
addFormats(ajv);

const FORM_ITEM_LAYOUT = {
labelCol: {
Expand Down Expand Up @@ -69,17 +81,20 @@ const PluginDetail: React.FC<Props> = ({
pluginList = [],
readonly = false,
initialData = {},
onClose = () => { },
onChange = () => { },
onClose = () => {},
onChange = () => {},
}) => {
const { formatMessage } = useIntl();
const [form] = Form.useForm();
const ref = useRef<any>(null);
const data = initialData[name] || {};
const pluginType = pluginList.find(item => item.name === name)?.type
const pluginType = pluginList.find((item) => item.name === name)?.type;

useEffect(() => {
form.setFieldsValue({ disable: initialData[name] && !initialData[name].disable });
form.setFieldsValue({
disable: initialData[name] && !initialData[name].disable,
scope: 'global',
});
}, []);

const validateData = (pluginName: string, value: PluginComponent.Data) => {
Expand All @@ -92,7 +107,6 @@ const PluginDetail: React.FC<Props> = ({
} else {
injectDisableProperty(schema);
}

const validate = ajv.compile(schema);
if (validate(value)) {
resolve(value);
Expand Down Expand Up @@ -198,8 +212,8 @@ const PluginDetail: React.FC<Props> = ({
</Form.Item>
{type === 'global' && (
<Form.Item label="Scope" name="scope">
<Select disabled defaultValue="Global">
<Select.Option value="Global">Global</Select.Option>
<Select disabled>
<Select.Option value="global">Global</Select.Option>
</Select>
</Form.Item>
)}
Expand All @@ -208,8 +222,12 @@ const PluginDetail: React.FC<Props> = ({
<PageHeader
title=""
subTitle={
(pluginType === 'auth' && schemaType !== 'consumer') ? <Alert message={`${name} does not require configuration`} type="warning" />
: <>Current plugin: {name}</>}
pluginType === 'auth' && schemaType !== 'consumer' ? (
<Alert message={`${name} does not require configuration`} type="warning" />
) : (
<>Current plugin: {name}</>
)
}
ghost={false}
extra={[
<Button
Expand All @@ -228,7 +246,13 @@ const PluginDetail: React.FC<Props> = ({
]}
/>
<CodeMirror
ref={ref}
ref={(codemirror) => {
ref.current = codemirror;
if (codemirror) {
// NOTE: for debug & test
window.codemirror = codemirror.editor;
}
}}
value={JSON.stringify(data, null, 2)}
options={{
mode: 'json-ld',
Expand Down
6 changes: 4 additions & 2 deletions web/src/components/Plugin/PluginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ const PluginPage: React.FC<Props> = ({
textAlign: 'center',
}}
title={[
<div style={{ width: '100%', textAlign: 'center' }}>
<span key={2}>{item.name}</span>
<div style={{ width: '100%', textAlign: 'center' }} key={1}>
<span key={2} data-cy-plugin-name={item.name}>
{item.name}
</span>
</div>,
]}
style={{ height: 258, width: 200 }}
Expand Down
2 changes: 1 addition & 1 deletion web/src/pages/Plugin/PluginMarket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const PluginMarket: React.FC = () => {
});
setInitialData(plugins);
});
}
};

useEffect(() => {
initPageData();
Expand Down
12 changes: 7 additions & 5 deletions web/src/pages/Plugin/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ export const fetchList = (): Promise<{
plugins: Record<string, any>;
};
}>(`/global_rules/${DEFAULT_GLOBAL_RULE_ID}`).then(({ data }) => {
const plugins = Object.entries(data.plugins || {}).filter(([, value]) => !value.disable).map(([name, value]) => ({
id: name,
name,
value,
}));
const plugins = Object.entries(data.plugins || {})
.filter(([, value]) => !value.disable)
.map(([name, value]) => ({
id: name,
name,
value,
}));

return {
data: plugins,
Expand Down
8 changes: 6 additions & 2 deletions web/src/pages/Route/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,12 @@ const Page: React.FC = () => {

return (
<Select style={{ width: '100%' }} allowClear>
<option key={RouteStatus.Offline} value={RouteStatus.Offline}>{formatMessage({ id: 'page.route.unpublished' })}</option>
<option key={RouteStatus.Publish} value={RouteStatus.Publish}>{formatMessage({ id: 'page.route.published' })}</option>
<option key={RouteStatus.Offline} value={RouteStatus.Offline}>
{formatMessage({ id: 'page.route.unpublished' })}
</option>
<option key={RouteStatus.Publish} value={RouteStatus.Publish}>
{formatMessage({ id: 'page.route.published' })}
</option>
</Select>
);
},
Expand Down
Loading

0 comments on commit 50fb547

Please sign in to comment.