From c3cdb02b293e304ad6743c05a2745f8e78960de2 Mon Sep 17 00:00:00 2001 From: Leopoldthecoder Date: Thu, 24 Aug 2023 16:45:29 +0800 Subject: [PATCH] feat(plugin): support ordering --- package.json | 1 + src/assets/icon-descendent.svg | 4 + src/composables/useDocsLink.ts | 10 +- src/locales/en.json | 20 +- src/pages/ca-certificates/List.vue | 4 +- src/pages/certificates/List.vue | 4 +- src/pages/consumers/List.vue | 2 +- src/pages/key-sets/List.vue | 2 +- src/pages/keys/List.vue | 2 +- src/pages/plugins/Detail.vue | 38 ++- src/pages/plugins/List.vue | 18 +- src/pages/plugins/PluginOrdering.vue | 221 +++++++++++++ src/pages/plugins/PluginOrderingEmpty.vue | 78 +++++ src/pages/plugins/PluginOrderingForm.vue | 309 ++++++++++++++++++ src/pages/plugins/types.ts | 18 + src/pages/routes/List.vue | 2 +- src/pages/services/List.vue | 2 +- src/pages/snis/List.vue | 2 +- src/pages/upstreams/List.vue | 2 +- src/pages/vaults/List.vue | 2 +- src/router.ts | 20 ++ src/utils/index.ts | 29 ++ .../plugins/01-PluginOrdering.spec.ts | 217 ++++++++++++ .../specs-ee/plugins/01-Plugins.spec.ts | 11 - 24 files changed, 987 insertions(+), 31 deletions(-) create mode 100644 src/assets/icon-descendent.svg create mode 100644 src/pages/plugins/PluginOrdering.vue create mode 100644 src/pages/plugins/PluginOrderingEmpty.vue create mode 100644 src/pages/plugins/PluginOrderingForm.vue create mode 100644 src/pages/plugins/types.ts create mode 100644 tests/playwright/specs-ee/plugins/01-PluginOrdering.spec.ts delete mode 100644 tests/playwright/specs-ee/plugins/01-Plugins.spec.ts diff --git a/package.json b/package.json index b144094f..b029bbc3 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "stylelint": "stylelint --allow-empty-input 'src/**/*.{css,scss,sass,less,styl,vue}'", "postinstall": "husky install" }, + "license": "Apache-2.0", "lint-staged": { "src/**/*.{ts,js,vue}": "eslint --fix", "src/**/*.{css,scss,sass,less,styl,vue}": "stylelint --allow-empty-input --fix 'src/**/*.{css,scss,sass,less,styl,vue}'" diff --git a/src/assets/icon-descendent.svg b/src/assets/icon-descendent.svg new file mode 100644 index 00000000..774b153d --- /dev/null +++ b/src/assets/icon-descendent.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/composables/useDocsLink.ts b/src/composables/useDocsLink.ts index 0defe7b0..f3b7aa4f 100644 --- a/src/composables/useDocsLink.ts +++ b/src/composables/useDocsLink.ts @@ -2,7 +2,7 @@ import { computed } from 'vue' import { formatVersion } from '@/utils' import { config } from 'config' -export const useDocsLink = (entity: string) => { +export const useDocsLink = (entity?: string) => { const version = computed(() => { if (!config.GATEWAY_VERSION) { return 'latest' @@ -11,15 +11,15 @@ export const useDocsLink = (entity: string) => { return `${formatVersion(config.GATEWAY_VERSION)}.x` }) - const docsBase = computed(() => `https://docs.konghq.com/gateway/${version.value}/admin-api`) + const docsBase = computed(() => `https://docs.konghq.com/gateway/${version.value}`) const docsLink = computed(() => { switch (entity) { case 'key-set': - return `${docsBase.value}/#key-sets-entity` + return `${docsBase.value}/admin-api/#key-sets-entity` default: - return `${docsBase.value}/#${entity}-object` + return `${docsBase.value}/admin-api/#${entity}-object` } }) - return docsLink + return { docsBase, docsLink } } diff --git a/src/locales/en.json b/src/locales/en.json index b2dce353..ab292888 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -6,7 +6,9 @@ "back": "Back", "edit": "Edit", "save": "Save", - "create": "Create" + "create": "Create", + "update": "Update", + "cancel": "Cancel" }, "learn.more": "Learn more" }, @@ -123,7 +125,21 @@ "disabled": "Plugin {name} is successfully disabled!", "list.title": "Plugins", "create.form.title": "New Plugin", - "edit.form.title": "Edit Plugin" + "edit.form.title": "Edit Plugin", + "ordering.title": "Ordering", + "ordering.disabled": "Consumer-scoped plugins don't support dynamic ordering", + "doc.dynamic": "Dynamic plugin ordering - {docLink}", + "doc.default": "Default plugin execution order - {docLink}", + "empty.ordering.title": "No dynamic ordering applied", + "empty.ordering.cta.text": "Add ordering to override the static priority for Plugin execution", + "empty.ordering.cta.button": "Add ordering", + "ordering.plugin.label": "Plugin {index}", + "ordering.add.plugin": "Add Plugin", + "ordering.before.label": "Before access", + "ordering.before.desc": "Dynamic plugin ordering for 'before.access' phase", + "ordering.after.label": "After access", + "ordering.after.desc": "Dynamic plugin ordering for 'after.access' phase", + "ordering.updated": "Plugin ordering successfully updated" }, "upstream": { "description": "An Upstream represents a virtual hostname and can be used to load balance incoming requests over multiple Services.", diff --git a/src/pages/ca-certificates/List.vue b/src/pages/ca-certificates/List.vue index 29cd7db3..fe43e167 100644 --- a/src/pages/ca-certificates/List.vue +++ b/src/pages/ca-certificates/List.vue @@ -38,7 +38,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('ca-certificate') +const { docsLink } = useDocsLink('ca-certificate') const createRoute = computed(() => { return { name: 'ca-certificate-create' } @@ -59,6 +59,8 @@ const caCertificateListConfig = reactive({ createRoute, getViewRoute, getEditRoute, + // CA Certificates only support exact match for both CE and EE + isExactMatch: true, }) const canCreate = async () => true diff --git a/src/pages/certificates/List.vue b/src/pages/certificates/List.vue index 0a0f3d44..79a2c2cf 100644 --- a/src/pages/certificates/List.vue +++ b/src/pages/certificates/List.vue @@ -38,7 +38,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('certificate') +const { docsLink } = useDocsLink('certificate') const createRoute = computed(() => { return { name: 'certificate-create' } @@ -59,6 +59,8 @@ const certificateListConfig = reactive({ createRoute, getViewRoute, getEditRoute, + // Certificates only support exact match for both CE and EE + isExactMatch: true, }) const canCreate = async () => true diff --git a/src/pages/consumers/List.vue b/src/pages/consumers/List.vue index f66af91f..4460da74 100644 --- a/src/pages/consumers/List.vue +++ b/src/pages/consumers/List.vue @@ -39,7 +39,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('consumer') +const { docsLink } = useDocsLink('consumer') const createRoute = computed(() => { return { name: 'consumer-create' } diff --git a/src/pages/key-sets/List.vue b/src/pages/key-sets/List.vue index 60445bfb..77ba046f 100644 --- a/src/pages/key-sets/List.vue +++ b/src/pages/key-sets/List.vue @@ -39,7 +39,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('key-set') +const { docsLink } = useDocsLink('key-set') const filterSchema = computed(() => { return { diff --git a/src/pages/keys/List.vue b/src/pages/keys/List.vue index b6a926d3..ef0687dd 100644 --- a/src/pages/keys/List.vue +++ b/src/pages/keys/List.vue @@ -45,7 +45,7 @@ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const route = useRoute() const { t } = useI18n() -const docsLink = useDocsLink('keys') +const { docsLink } = useDocsLink('keys') const keySetId = computed(() => (route.params?.id ?? '') as string) const cacheIdentifier = computed(() => `routes-${keySetId.value}`) diff --git a/src/pages/plugins/Detail.vue b/src/pages/plugins/Detail.vue index 3fd5663e..9e45fbe8 100644 --- a/src/pages/plugins/Detail.vue +++ b/src/pages/plugins/Detail.vue @@ -21,7 +21,26 @@ }" /> + + + + { return `${(route.query?.entity_type as string).split('_')[0]}s` }) -const entityId = computed(() => route.query?.entity_id) +const entityId = computed(() => (route.query?.entity_id ?? '') as string) const pluginDetailConfig = reactive({ ...useDetailGeneralConfig(), diff --git a/src/pages/plugins/List.vue b/src/pages/plugins/List.vue index b10f9c51..8366f4a1 100644 --- a/src/pages/plugins/List.vue +++ b/src/pages/plugins/List.vue @@ -47,7 +47,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('plugin') +const { docsLink } = useDocsLink('plugin') const route = useRoute() const cacheIdentifier = computed(() => `plugins-${route.params?.id}`) const entityType = computed(() => route.meta?.scopedIn as EntityType) @@ -97,6 +97,20 @@ const getViewRoute = (plugin: Pick) => { } } +const getConfigureDynamicOrderingRoute = (plugin: Pick) => { + return { + name: 'plugin-ordering', + params: { + id: plugin.id, + pluginType: plugin.name, + }, + query: { + ...scopedQuery.value, + ...createRedirectRouteQuery(), + }, + } +} + const getEditRoute = (plugin: EntityRow) => ({ name: 'plugin-edit', params: { @@ -130,7 +144,7 @@ const pluginListConfig = reactive({ getViewRoute, getEditRoute, getScopedEntityViewRoute, - getConfigureDynamicOrderingRoute: getViewRoute, + getConfigureDynamicOrderingRoute, filterSchema, }) diff --git a/src/pages/plugins/PluginOrdering.vue b/src/pages/plugins/PluginOrdering.vue new file mode 100644 index 00000000..d4d7cdd8 --- /dev/null +++ b/src/pages/plugins/PluginOrdering.vue @@ -0,0 +1,221 @@ + + + + + diff --git a/src/pages/plugins/PluginOrderingEmpty.vue b/src/pages/plugins/PluginOrderingEmpty.vue new file mode 100644 index 00000000..a6673d9d --- /dev/null +++ b/src/pages/plugins/PluginOrderingEmpty.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/src/pages/plugins/PluginOrderingForm.vue b/src/pages/plugins/PluginOrderingForm.vue new file mode 100644 index 00000000..0dff2987 --- /dev/null +++ b/src/pages/plugins/PluginOrderingForm.vue @@ -0,0 +1,309 @@ + + + + + diff --git a/src/pages/plugins/types.ts b/src/pages/plugins/types.ts new file mode 100644 index 00000000..65d74c76 --- /dev/null +++ b/src/pages/plugins/types.ts @@ -0,0 +1,18 @@ +export interface Ordering { + before?: { + access: string[]; + }; + after?: { + access: string[]; + }; +} + +export type PluginSchema = Record & { + id: string, + tags?: string[], + consumer?: { id: string }, + route?: { id: string }, + service?: { id: string }, + ordering: Ordering, + instance_name?: string, +} diff --git a/src/pages/routes/List.vue b/src/pages/routes/List.vue index 3eb220fe..36ccdb5c 100644 --- a/src/pages/routes/List.vue +++ b/src/pages/routes/List.vue @@ -42,7 +42,7 @@ defineOptions({ name: 'RouteList' }) const toaster = useToaster() const route = useRoute() const { t } = useI18n() -const docsLink = useDocsLink('route') +const { docsLink } = useDocsLink('route') const { createRedirectRouteQuery } = useListRedirect() const serviceId = computed(() => (route.params?.id ?? '') as string) diff --git a/src/pages/services/List.vue b/src/pages/services/List.vue index e948470a..f6e7c92c 100644 --- a/src/pages/services/List.vue +++ b/src/pages/services/List.vue @@ -39,7 +39,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('service') +const { docsLink } = useDocsLink('service') const createRoute = computed(() => { return { name: 'service-create' } diff --git a/src/pages/snis/List.vue b/src/pages/snis/List.vue index 46081bb4..2b683833 100644 --- a/src/pages/snis/List.vue +++ b/src/pages/snis/List.vue @@ -39,7 +39,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('sni') +const { docsLink } = useDocsLink('sni') const createRoute = computed(() => { return { name: 'sni-create' } diff --git a/src/pages/upstreams/List.vue b/src/pages/upstreams/List.vue index ac758c04..9c90843b 100644 --- a/src/pages/upstreams/List.vue +++ b/src/pages/upstreams/List.vue @@ -39,7 +39,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('upstream') +const { docsLink } = useDocsLink('upstream') const createRoute = computed(() => { return { name: 'upstream-create' } diff --git a/src/pages/vaults/List.vue b/src/pages/vaults/List.vue index c0cb195e..57cfeb89 100644 --- a/src/pages/vaults/List.vue +++ b/src/pages/vaults/List.vue @@ -39,7 +39,7 @@ defineOptions({ const { createRedirectRouteQuery } = useListRedirect() const toaster = useToaster() const { t } = useI18n() -const docsLink = useDocsLink('vaults') +const { docsLink } = useDocsLink('vaults') const createRoute = computed(() => { return { name: 'vault-create' } diff --git a/src/router.ts b/src/router.ts index a3c155f0..73177ab0 100644 --- a/src/router.ts +++ b/src/router.ts @@ -256,6 +256,26 @@ const routes: Array = [ entity: 'plugin', title: 'View Plugin', }, + children: [ + { + name: 'plugin-ordering', + path: 'ordering', + component: () => import('@/pages/plugins/PluginOrdering.vue'), + meta: { + entity: 'plugin', + title: 'Plugin Ordering', + }, + }, + ], + }, + { + name: 'plugin-ordering-update', + path: '/plugins/:pluginType/:id/ordering/update', + component: () => import('@/pages/plugins/PluginOrderingForm.vue'), + meta: { + entity: 'plugin', + title: 'Plugin Ordering Edit', + }, }, // upstream pages diff --git a/src/utils/index.ts b/src/utils/index.ts index fe4f6a09..a28c0f89 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,32 @@ +import type { AxiosError } from 'axios' + export const formatVersion = (version: string) => { return version.substring(0, (version.indexOf('-') > -1 && version.indexOf('-')) || version.length).split('.').slice(0, 2).join('.') } + +export function getMessageFromError ( + error?: AxiosError<{ message?: string }>, + defaultMessage = 'There was an error', +) { + if (!error) { + return defaultMessage + } + + if (error.response?.data) { + if (error.response.data.message) { + return error.response.data.message + } + + if (typeof error.response.data === 'string') { + return error.response.data + } + + if (typeof error.response.data === 'object') { + return Object.keys(error.response.data) + .map(key => `${key} ${error.response?.data[key]}`) + .join(', ') + } + } + + return error.message || defaultMessage +} diff --git a/tests/playwright/specs-ee/plugins/01-PluginOrdering.spec.ts b/tests/playwright/specs-ee/plugins/01-PluginOrdering.spec.ts new file mode 100644 index 00000000..bb13529b --- /dev/null +++ b/tests/playwright/specs-ee/plugins/01-PluginOrdering.spec.ts @@ -0,0 +1,217 @@ +import { expect } from '@playwright/test' +import baseTest from '@pw/base-test' +import { clearKongResources } from '@pw/commands/clearKongResources' +import { clickEntityListAction } from '@pw/commands/clickEntityListAction' +import { createKongResource } from '@pw/commands/createKongResource' +import { withNavigation } from '@pw/commands/withNavigation' +import { ConsumerListPage } from '@pw/pages/consumers' +import { PluginListPage } from '@pw/pages/plugins' +import { ServiceListPage } from '@pw/pages/services' + +const mockPluginName = 'prometheus' +const mockConsumerName = 'testUser' + +const test = baseTest().extend<{ + consumerListPage: ConsumerListPage, + pluginListPage: PluginListPage + serviceListPage: ServiceListPage +}>({ + consumerListPage: async ({ page }, use) => use(new ConsumerListPage(page)), + pluginListPage: async ({ page }, use) => use(new PluginListPage(page)), + serviceListPage: async ({ page }, use) => use(new ServiceListPage(page)), +}) + +test.describe('plugin orderings', () => { + test.beforeAll(async () => { + await clearKongResources('/plugins') + await clearKongResources('/consumers') + }) + + test.afterEach(async () => { + await clearKongResources('/plugins') + await clearKongResources('/consumers') + }) + + test('view plugin ordering - empty', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await expect(page.locator('.plugin-ordering-empty')).toHaveCount(1) + await expect(page.locator('.plugin-ordering-empty')).toContainText('No dynamic ordering applied') + }) + + test('view plugin ordering - with only `before.access`', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + ordering: { + before: { + access: ['basic-auth'], + }, + }, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await expect(page.locator('.plugin-ordering-name')).toContainText('basic-auth') + await expect(page.locator('.plugin-ordering-empty')).toHaveCount(1) + await expect(page.locator('.plugin-ordering-empty')).toContainText('No dynamic ordering applied') + }) + + test('view plugin ordering - with only `after.access`', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + ordering: { + after: { + access: ['basic-auth'], + }, + }, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await expect(page.locator('.plugin-ordering-name')).toContainText('basic-auth') + await expect(page.locator('.plugin-ordering-empty')).toHaveCount(1) + await expect(page.locator('.plugin-ordering-empty')).toContainText('No dynamic ordering applied') + }) + + test('view plugin ordering - with both `before.access` and `after.access`', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + ordering: { + before: { + access: ['cors'], + }, + after: { + access: ['basic-auth'], + }, + }, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await expect(page.locator('.plugin-ordering-name').nth(0)).toContainText('cors') + await expect(page.locator('.plugin-ordering-name').nth(1)).toContainText('basic-auth') + await expect(page.locator('.plugin-ordering-empty')).not.toBeVisible() + }) + + test('view plugin ordering - disabled when plugin is consumer-scoped', async ({ page, pluginListPage }) => { + const consumer = await createKongResource('/consumers', { + username: mockConsumerName, + }) + + await createKongResource('/plugins', { + name: 'datadog', + enabled: true, + consumer: { + id: consumer?.data.id, + }, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await expect(page.locator('.plugin-ordering-empty .plugin-ordering-empty-button')).toBeDisabled() + await expect(page.getByTestId('edit-ordering-button')).toBeDisabled() + }) + + test('edit plugin ordering - add', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await withNavigation(page, () => page.locator('.plugin-ordering-empty .plugin-ordering-empty-button').click()) + await page.click('[data-testid="plugin-ordering-before"] .ordering-plugin-add') + await page.click('#before-0') + await page.click('[data-testid="k-select-item-basic-auth"]') + await withNavigation(page, () => page.locator('.ordering-form-footer button.primary').click()) + await expect(page.locator('.plugin-ordering-name')).toContainText('basic-auth') + + await withNavigation(page, () => page.getByTestId('edit-ordering-button').click()) + await page.click('[data-testid="plugin-ordering-before"] .ordering-plugin-add') + await page.click('#before-1') + await page.click('[data-testid="k-select-item-cors"]') + await withNavigation(page, () => page.locator('.ordering-form-footer button.primary').click()) + await expect(page.locator('.plugin-ordering-name').nth(1)).toContainText('cors') + }) + + test('edit plugin ordering - change', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + ordering: { + before: { + access: ['cors'], + }, + }, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await withNavigation(page, () => page.locator('.plugin-ordering-empty .plugin-ordering-empty-button').click()) + await page.click('#before-0') + await page.click('[data-testid="k-select-item-basic-auth"]') + await withNavigation(page, () => page.locator('.ordering-form-footer button.primary').click()) + await expect(page.locator('.plugin-ordering-name')).toContainText('basic-auth') + }) + + test('edit plugin ordering - remove', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + ordering: { + before: { + access: ['cors'], + }, + }, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await withNavigation(page, () => page.getByTestId('edit-ordering-button').click()) + await page.click('[data-testid="plugin-ordering-before"] .ordering-plugin-select button.delete') + await withNavigation(page, () => page.locator('.ordering-form-footer button.primary').click()) + await expect(page.locator('.plugin-ordering-name')).toHaveCount(0) + }) + + test('edit plugin ordering - listing correct options', async ({ page, pluginListPage }) => { + await createKongResource('/plugins', { + name: mockPluginName, + enabled: true, + }) + + await pluginListPage.goto() + await withNavigation(page, () => clickEntityListAction(page, 'view')) + await withNavigation(page, () => page.locator('#ordering-tab').click()) + await withNavigation(page, () => page.getByTestId('edit-ordering-button').click()) + await page.click('[data-testid="plugin-ordering-before"] .ordering-plugin-add') + await page.click('#before-0') + await expect(page.locator(`[data-testid="before-0"] [data-testid="k-select-item-${mockPluginName}"] button`)).toBeDisabled() + await page.click('[data-testid="k-select-item-basic-auth"]') + await page.click('[data-testid="plugin-ordering-before"] .ordering-plugin-add') + await page.click('#before-1') + await expect(page.locator('[data-testid="before-1"] [data-testid="k-select-item-basic-auth"] button')).toBeDisabled() + await expect(page.locator('[data-testid="before-1"] [data-testid="k-select-item-cors"] button')).toBeEnabled() + await page.click('#before-1') + await page.click('[data-testid="plugin-ordering-after"] .ordering-plugin-add') + await page.click('#after-0') + await expect(page.locator('[data-testid="after-0"] [data-testid="k-select-item-basic-auth"] button')).toBeDisabled() + await expect(page.locator('[data-testid="after-0"] [data-testid="k-select-item-cors"] button')).toBeEnabled() + }) +}) diff --git a/tests/playwright/specs-ee/plugins/01-Plugins.spec.ts b/tests/playwright/specs-ee/plugins/01-Plugins.spec.ts deleted file mode 100644 index 7939ed47..00000000 --- a/tests/playwright/specs-ee/plugins/01-Plugins.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { expect } from '@playwright/test' -import baseTest from '@pw/base-test' - -const test = baseTest() - -test.describe('PLACEHOLDER', () => { - test('is enterprise edition', async ({ page }) => { - await page.goto('/') - await expect(page.locator('[aria-label="Gateway"] .info-list')).toContainText('enterprise') - }) -})