Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ dist-ssr
/playwright/.cache/

.eslintcache
*.tsbuildinfo
6 changes: 4 additions & 2 deletions e2e/pom/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ const locator = {

const assert = {
isIndexPage: async (page: Page) => {
await expect(page).toHaveURL((url) => url.pathname.endsWith('/routes'));
await expect(page).toHaveURL((url) => url.pathname.endsWith('/routes'), {
timeout: 30000,
});
const title = page.getByRole('heading', { name: 'Routes' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
isAddPage: async (page: Page) => {
await expect(page).toHaveURL((url) => url.pathname.endsWith('/routes/add'));
Expand Down
30 changes: 16 additions & 14 deletions e2e/pom/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,29 @@ const locator = {

const assert = {
isIndexPage: async (page: Page) => {
await expect(page).toHaveURL((url) => url.pathname.endsWith('/services'));
await expect(page).toHaveURL((url) => url.pathname.endsWith('/services'), {
timeout: 30000,
});
const title = page.getByRole('heading', { name: 'Services' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
isAddPage: async (page: Page) => {
await expect(page).toHaveURL((url) =>
url.pathname.endsWith('/services/add')
);
const title = page.getByRole('heading', { name: 'Add Service' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
isDetailPage: async (page: Page) => {
await expect(page).toHaveURL((url) =>
url.pathname.includes('/services/detail')
);
// Check for Layout (Tabs) first
await expect(
page.getByRole('tab', { name: 'Service Detail' })
).toBeVisible({ timeout: 30000 });
const title = page.getByRole('heading', { name: 'Service Detail' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
// Service routes assertions
isServiceRoutesPage: async (page: Page) => {
Expand All @@ -63,10 +69,8 @@ const assert = {
url.pathname.includes('/services/detail') &&
url.pathname.includes('/routes')
);
// Wait for page to load completely
await page.waitForLoadState('networkidle');
const title = page.getByRole('heading', { name: 'Routes' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
isServiceRouteAddPage: async (page: Page) => {
await expect(page).toHaveURL(
Expand All @@ -75,7 +79,7 @@ const assert = {
url.pathname.includes('/routes/add')
);
const title = page.getByRole('heading', { name: 'Add Route' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
isServiceRouteDetailPage: async (page: Page) => {
await expect(page).toHaveURL(
Expand All @@ -84,7 +88,7 @@ const assert = {
url.pathname.includes('/routes/detail')
);
const title = page.getByRole('heading', { name: 'Route Detail' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
// Service stream routes assertions
isServiceStreamRoutesPage: async (page: Page) => {
Expand All @@ -93,10 +97,8 @@ const assert = {
url.pathname.includes('/services/detail') &&
url.pathname.includes('/stream_routes')
);
// Wait for page to load completely
await page.waitForLoadState('networkidle');
const title = page.getByRole('heading', { name: 'Stream Routes' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
isServiceStreamRouteAddPage: async (page: Page) => {
await expect(page).toHaveURL(
Expand All @@ -105,7 +107,7 @@ const assert = {
url.pathname.includes('/stream_routes/add')
);
const title = page.getByRole('heading', { name: 'Add Stream Route' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
isServiceStreamRouteDetailPage: async (page: Page) => {
await expect(page).toHaveURL(
Expand All @@ -114,7 +116,7 @@ const assert = {
url.pathname.includes('/stream_routes/detail')
);
const title = page.getByRole('heading', { name: 'Stream Route Detail' });
await expect(title).toBeVisible();
await expect(title).toBeVisible({ timeout: 30000 });
},
};

Expand Down
24 changes: 12 additions & 12 deletions e2e/pom/stream_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,28 @@ const assert = {
isIndexPage: async (page: Page) => {
await expect(page).toHaveURL(
(url) => url.pathname.endsWith('/stream_routes'),
{ timeout: 15000 }
{ timeout: 30000 }
);
const title = page.getByRole('heading', { name: 'Stream Routes' });
await expect(title).toBeVisible({ timeout: 15000 });
await expect(title).toBeVisible({ timeout: 30000 });
},
isAddPage: async (page: Page) => {
await expect(
page,
{ timeout: 15000 }
).toHaveURL((url) => url.pathname.endsWith('/stream_routes/add'));
await expect(page).toHaveURL(
(url) => url.pathname.endsWith('/stream_routes/add'),
{ timeout: 30000 }
);
const title = page.getByRole('heading', { name: 'Add Stream Route' });
await expect(title).toBeVisible({ timeout: 15000 });
await expect(title).toBeVisible({ timeout: 30000 });
},
isDetailPage: async (page: Page) => {
await expect(
page,
{ timeout: 20000 }
).toHaveURL((url) => url.pathname.includes('/stream_routes/detail'));
await expect(page).toHaveURL(
(url) => url.pathname.includes('/stream_routes/detail'),
{ timeout: 30000 }
);
const title = page.getByRole('heading', {
name: 'Stream Route Detail',
});
await expect(title).toBeVisible({ timeout: 20000 });
await expect(title).toBeVisible({ timeout: 30000 });
},
};

Expand Down
19 changes: 8 additions & 11 deletions e2e/server/apisix_conf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,16 @@ apisix:
- 9100
udp:
- 9200

deployment:
admin:
allow_admin: # https://nginx.org/en/docs/http/ngx_http_access_module.html#allow
- 0.0.0.0/0 # We need to restrict ip access rules for security. 0.0.0.0/0 is for test.

allow_admin:
- 0.0.0.0/0
admin_key:
- name: "admin"
- name: admin
key: edd1c9f034335f136f87ad84b625c8f1
role: admin # admin: manage all configuration data

role: admin
etcd:
host: # it's possible to define multiple etcd hosts addresses of the same etcd cluster.
- "http://etcd:2379" # multiple etcd address
prefix: "/apisix" # apisix configurations prefix
timeout: 30 # 30 seconds
host:
- http://etcd:2379
prefix: /apisix
timeout: 30
3 changes: 2 additions & 1 deletion e2e/tests/consumer_groups.list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const consumerGroups: APISIXType['ConsumerGroupPut'][] = Array.from(

test.describe('page and page_size should work correctly', () => {
test.describe.configure({ mode: 'serial' });

test.beforeAll(async () => {
await deleteAllConsumerGroups(e2eReq);
await Promise.all(
Expand Down Expand Up @@ -77,6 +78,6 @@ test.describe('page and page_size should work correctly', () => {
items: consumerGroups,
filterItemsNotInPage,
getCell: (page, item) =>
page.getByRole('cell', { name: item.id }).first(),
page.getByRole('cell', { name: item.id, exact: true }).first(),
});
});
14 changes: 7 additions & 7 deletions e2e/tests/consumers.credentials.list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ test('should only show credentials for current consumer', async ({ page }) => {

// Credentials from another consumer should not be visible
await expect(
page.getByRole('cell', { name: anotherConsumerCredential.id })
page.getByRole('cell', { name: anotherConsumerCredential.id, exact: true })
).toBeHidden();

// Only credentials belonging to current consumer should be visible
for (const credential of credentials) {
await expect(
page.getByRole('cell', { name: credential.id })
page.getByRole('cell', { name: credential.id, exact: true })
).toBeVisible();
}
});
Expand All @@ -167,13 +167,13 @@ test('should only show credentials for current consumer', async ({ page }) => {

// Should only see the other consumer's credential
await expect(
page.getByRole('cell', { name: anotherConsumerCredential.id })
page.getByRole('cell', { name: anotherConsumerCredential.id, exact: true })
).toBeVisible();

// Should not see test consumer's credentials
for (const credential of credentials) {
await expect(
page.getByRole('cell', { name: credential.id })
page.getByRole('cell', { name: credential.id, exact: true })
).toBeHidden();
}
});
Expand All @@ -190,7 +190,7 @@ test('should display credentials list under consumer', async ({ page }) => {
// Verify all created credentials are displayed
for (const credential of credentials) {
await expect(
page.getByRole('cell', { name: credential.id })
page.getByRole('cell', { name: credential.id, exact: true })
).toBeVisible();
await expect(
page.getByRole('cell', { name: credential.desc || '' })
Expand Down Expand Up @@ -290,7 +290,7 @@ test('should be able to delete credential', async ({ page }) => {

await test.step('verify temporary credential exists', async () => {
await expect(
page.getByRole('cell', { name: tempCredential.id })
page.getByRole('cell', { name: tempCredential.id, exact: true })
).toBeVisible();
});

Expand All @@ -317,7 +317,7 @@ test('should be able to delete credential', async ({ page }) => {

// Verify the credential no longer appears
await expect(
page.getByRole('cell', { name: tempCredential.id })
page.getByRole('cell', { name: tempCredential.id, exact: true })
).toBeHidden();
});
});
Expand Down
3 changes: 2 additions & 1 deletion e2e/tests/consumers.list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const consumers: APISIXType['ConsumerPut'][] = Array.from({ length: 11 }, (_, i)

test.describe('page and page_size should work correctly', () => {
test.describe.configure({ mode: 'serial' });

test.beforeAll(async () => {
await deleteAllConsumers(e2eReq);
await Promise.all(consumers.map((d) => putConsumerReq(e2eReq, d)));
Expand Down Expand Up @@ -76,6 +77,6 @@ test.describe('page and page_size should work correctly', () => {
items: consumers,
filterItemsNotInPage,
getCell: (page, item) =>
page.getByRole('cell', { name: item.username }).first(),
page.getByRole('cell', { name: item.username, exact: true }).first(),
});
});
2 changes: 1 addition & 1 deletion e2e/tests/global_rules.list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,6 @@ test.describe('page and page_size should work correctly', () => {
pom: globalRulePom,
items: globalRules,
filterItemsNotInPage,
getCell: (page, item) => page.getByRole('cell', { name: item.id }).first(),
getCell: (page, item) => page.getByRole('cell', { name: item.id, exact: true }).first(),
});
});
56 changes: 40 additions & 16 deletions e2e/tests/hot-path.upstream-service-route.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ test.afterAll(async () => {
});

test('can create upstream -> service -> route', async ({ page }) => {
test.setTimeout(120000); // Increase timeout for this comprehensive test
const selectPluginsBtn = page.getByRole('button', {
name: 'Select Plugins',
});
Expand All @@ -61,6 +62,7 @@ test('can create upstream -> service -> route', async ({ page }) => {
scheme: 'https',
nodes: [{ host: 'httpbin.org', port: 443 }],
};

await test.step('create upstream', async () => {
// Navigate to the upstream list page
await upstreamsPom.toIndex(page);
Expand Down Expand Up @@ -158,6 +160,7 @@ test('can create upstream -> service -> route', async ({ page }) => {
},
},
} satisfies Partial<APISIXType['Service']>;

await test.step('create service', async () => {
// upstream id should be set
expect(service.upstream_id).not.toBeUndefined();
Expand Down Expand Up @@ -275,6 +278,7 @@ test('can create upstream -> service -> route', async ({ page }) => {
},
},
};

await test.step('create route', async () => {
// service id should be set
expect(route.service_id).not.toBeUndefined();
Expand Down Expand Up @@ -359,23 +363,36 @@ test('can create upstream -> service -> route', async ({ page }) => {
// Verify upstream exists in list
await upstreamsPom.toIndex(page);
await upstreamsPom.isIndexPage(page);
await expect(page.getByRole('cell', { name: upstream.name })).toBeVisible();
await page.waitForLoadState('load');
await expect(page.getByRole('cell', { name: upstream.name })).toBeVisible({
timeout: 30000,
});

// Verify service exists in list
await page.getByRole('link', { name: 'Services' }).click();
await expect(page.getByRole('heading', { name: 'Services' })).toBeVisible();
await expect(page.getByRole('cell', { name: service.name })).toBeVisible();
await page.waitForLoadState('load');
await expect(page.getByRole('heading', { name: 'Services' })).toBeVisible({
timeout: 15000,
});
await expect(page.getByRole('cell', { name: service.name })).toBeVisible({
timeout: 15000,
});

// Verify route exists in list
await routesPom.toIndex(page);
await routesPom.isIndexPage(page);
await expect(page.getByRole('cell', { name: route.name })).toBeVisible();
await page.waitForLoadState('load');
await expect(page.getByRole('cell', { name: route.name })).toBeVisible({
timeout: 30000,
});

// Navigate to route detail to verify service and plugin
await page
.getByRole('row', { name: route.name })
.getByRole('button', { name: 'View' })
.click();
const routeRow = page.getByRole('row', { name: route.name });
await routeRow.scrollIntoViewIfNeeded();
await expect(routeRow.getByRole('button', { name: 'View' })).toBeVisible({
timeout: 15000,
});
await routeRow.getByRole('button', { name: 'View' }).click();
await routesPom.isDetailPage(page);

// Verify URI
Expand All @@ -399,10 +416,14 @@ test('can create upstream -> service -> route', async ({ page }) => {
// Navigate to service detail to verify upstream and plugin
await servicesPom.toIndex(page);
await servicesPom.isIndexPage(page);
await page
.getByRole('row', { name: service.name })
.getByRole('button', { name: 'View' })
.click();
await page.waitForLoadState('load');
const serviceRow = page.getByRole('row', { name: service.name });
await serviceRow.scrollIntoViewIfNeeded();
await expect(serviceRow.getByRole('button', { name: 'View' })).toBeVisible({
timeout: 15000,
});
await serviceRow.getByRole('button', { name: 'View' }).click();
await page.waitForLoadState('load');

// Verify limit-count plugin is present
await expect(page.getByText(servicePluginName)).toBeVisible();
Expand All @@ -420,10 +441,13 @@ test('can create upstream -> service -> route', async ({ page }) => {
// Navigate to upstream detail to verify nodes
await upstreamsPom.toIndex(page);
await upstreamsPom.isIndexPage(page);
await page
.getByRole('row', { name: upstream.name })
.getByRole('button', { name: 'View' })
.click();
await page.waitForLoadState('load');
const upstreamRow = page.getByRole('row', { name: upstream.name });
await upstreamRow.scrollIntoViewIfNeeded();
await expect(upstreamRow.getByRole('button', { name: 'View' })).toBeVisible({
timeout: 15000,
});
await upstreamRow.getByRole('button', { name: 'View' }).click();

// Verify nodes are present
await expect(
Expand Down
Loading
Loading