182 lines
6.5 KiB
TypeScript
182 lines
6.5 KiB
TypeScript
|
|
import { expect, test } from './fixtures';
|
||
|
|
|
||
|
|
const testOrgId = '57753e11dff343b1ab95623933e8960b';
|
||
|
|
|
||
|
|
test.describe('课包商店', () => {
|
||
|
|
|
||
|
|
test('未登录访问首页显示课包商店', async ({ page }) => {
|
||
|
|
// Clear any stored auth
|
||
|
|
await page.goto('/');
|
||
|
|
await page.evaluate(() => localStorage.clear());
|
||
|
|
|
||
|
|
// Reload without auth
|
||
|
|
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||
|
|
await page.waitForTimeout(3000);
|
||
|
|
|
||
|
|
// The page should not show the empty login message
|
||
|
|
const loginHint = page.getByText('请返回微信小程序完成登录');
|
||
|
|
await expect(loginHint).not.toBeVisible();
|
||
|
|
|
||
|
|
// The course package store component should be present
|
||
|
|
const store = page.locator('.course-package-store');
|
||
|
|
await expect(store).toBeVisible({ timeout: 15_000 });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('课包卡片展示基本信息且不显示定价', async ({ page }) => {
|
||
|
|
// Mock the public API to return test packages
|
||
|
|
await page.route(/public-packages/, async route => {
|
||
|
|
await route.fulfill({
|
||
|
|
status: 200,
|
||
|
|
contentType: 'application/json',
|
||
|
|
body: JSON.stringify({
|
||
|
|
r: true,
|
||
|
|
d: [[
|
||
|
|
{
|
||
|
|
id: 'pkg-001',
|
||
|
|
package_name: '测试钢琴课包',
|
||
|
|
description: '这是一门测试用的钢琴课程包,包含10节课时',
|
||
|
|
total_lessons: 10,
|
||
|
|
original_price: 200000,
|
||
|
|
selling_price: 180000,
|
||
|
|
validity_days: 180,
|
||
|
|
package_status: 'ACTIVE',
|
||
|
|
cover_image_url: null,
|
||
|
|
sort_order: 1,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'pkg-002',
|
||
|
|
package_name: '声乐基础训练',
|
||
|
|
description: '从零开始学声乐',
|
||
|
|
total_lessons: 20,
|
||
|
|
original_price: 300000,
|
||
|
|
selling_price: 250000,
|
||
|
|
validity_days: 365,
|
||
|
|
package_status: 'ACTIVE',
|
||
|
|
cover_image_url: null,
|
||
|
|
sort_order: 2,
|
||
|
|
},
|
||
|
|
], 1, 2],
|
||
|
|
e: null,
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await page.evaluate(() => localStorage.clear());
|
||
|
|
|
||
|
|
// Navigate with org_id in URL so the component calls the public API
|
||
|
|
await page.goto(`/?org_id=${testOrgId}`, { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||
|
|
await page.waitForTimeout(3000);
|
||
|
|
|
||
|
|
// Package cards should be visible
|
||
|
|
const packageCards = page.locator('.package-card');
|
||
|
|
await expect(packageCards).toHaveCount(2, { timeout: 15_000 });
|
||
|
|
|
||
|
|
// Verify first card shows title
|
||
|
|
const firstTitle = packageCards.first().locator('.card-title');
|
||
|
|
await expect(firstTitle).toContainText('测试钢琴课包');
|
||
|
|
|
||
|
|
// Verify no price text ("¥" or "售价") is visible within cards
|
||
|
|
const cards = await packageCards.all();
|
||
|
|
for (const card of cards) {
|
||
|
|
const cardText = await card.innerText();
|
||
|
|
expect(cardText).not.toContain('¥');
|
||
|
|
expect(cardText).not.toContain('售价');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify lesson count IS shown
|
||
|
|
await expect(packageCards.first().locator('.card-meta')).toContainText('10 课次');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('试看按钮在未登录态弹出登录提示', async ({ page }) => {
|
||
|
|
// Mock the public API to return test packages
|
||
|
|
await page.route(/public-packages/, async route => {
|
||
|
|
await route.fulfill({
|
||
|
|
status: 200,
|
||
|
|
contentType: 'application/json',
|
||
|
|
body: JSON.stringify({
|
||
|
|
r: true,
|
||
|
|
d: [[
|
||
|
|
{
|
||
|
|
id: 'pkg-001',
|
||
|
|
package_name: '测试课包',
|
||
|
|
description: '测试用课包',
|
||
|
|
total_lessons: 10,
|
||
|
|
validity_days: 180,
|
||
|
|
package_status: 'ACTIVE',
|
||
|
|
},
|
||
|
|
], 1, 1],
|
||
|
|
e: null,
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await page.evaluate(() => localStorage.clear());
|
||
|
|
|
||
|
|
await page.goto(`/?org_id=${testOrgId}`, { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||
|
|
await page.waitForTimeout(3000);
|
||
|
|
|
||
|
|
// Wait for package cards to load
|
||
|
|
const previewBtn = page.locator('.preview-btn').first();
|
||
|
|
await expect(previewBtn).toBeVisible({ timeout: 15_000 });
|
||
|
|
await previewBtn.click();
|
||
|
|
await page.waitForTimeout(1000);
|
||
|
|
|
||
|
|
// Dialog should appear with login prompt
|
||
|
|
const dialog = page.locator('.van-dialog');
|
||
|
|
await expect(dialog).toBeVisible({ timeout: 10_000 });
|
||
|
|
await expect(dialog).toContainText('微信登录');
|
||
|
|
|
||
|
|
// Close dialog
|
||
|
|
const confirmBtn = page.locator('.van-dialog__confirm');
|
||
|
|
await confirmBtn.click();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('课包商店显示加载中状态', async ({ page }) => {
|
||
|
|
// Delay the API response to trigger loading skeleton
|
||
|
|
await page.route(/public-packages/, async route => {
|
||
|
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||
|
|
await route.fulfill({
|
||
|
|
status: 200,
|
||
|
|
contentType: 'application/json',
|
||
|
|
body: JSON.stringify({ r: true, d: [[], 0, 0], e: null }),
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await page.evaluate(() => localStorage.clear());
|
||
|
|
|
||
|
|
await page.goto(`/?org_id=${testOrgId}`, { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||
|
|
|
||
|
|
// Skeleton should be visible while loading (give it time to render)
|
||
|
|
await expect(page.locator('.van-skeleton').first()).toBeVisible({ timeout: 10_000 });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('课包商店API错误显示重试按钮', async ({ page }) => {
|
||
|
|
// Mock the API to return an error
|
||
|
|
await page.route(/public-packages/, async route => {
|
||
|
|
await route.fulfill({
|
||
|
|
status: 200,
|
||
|
|
contentType: 'application/json',
|
||
|
|
body: JSON.stringify({ r: false, d: null, e: '服务器错误' }),
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await page.evaluate(() => localStorage.clear());
|
||
|
|
|
||
|
|
await page.goto(`/?org_id=${testOrgId}`, { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||
|
|
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||
|
|
await page.waitForTimeout(3000);
|
||
|
|
|
||
|
|
// Error state should show retry button
|
||
|
|
const retryBtn = page.locator('.course-package-store').getByText('重试');
|
||
|
|
await expect(retryBtn).toBeVisible({ timeout: 15_000 });
|
||
|
|
});
|
||
|
|
});
|