chore: initialize tuhui repository
107
frontend/cypress/e2e/auth.cy.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// 生成随机手机号
|
||||
const generatePhone = () => {
|
||||
const timestamp = Date.now().toString().slice(-8);
|
||||
return `138${timestamp}`;
|
||||
};
|
||||
|
||||
describe('用户认证测试', () => {
|
||||
let testPhone;
|
||||
let testPassword = 'Test123456';
|
||||
|
||||
beforeEach(() => {
|
||||
testPhone = generatePhone();
|
||||
cy.visit('/');
|
||||
cy.wait(1000); // 等待页面加载
|
||||
});
|
||||
|
||||
it('应该能够成功注册新用户', () => {
|
||||
// 点击注册按钮 - 使用class选择器
|
||||
cy.get('.btn-register').click();
|
||||
|
||||
// 等待注册模态框出现
|
||||
cy.get('.auth-modal', { timeout: 10000 }).should('be.visible');
|
||||
|
||||
// 填写注册表单
|
||||
cy.get('input[placeholder*="手机号"]').type(testPhone);
|
||||
cy.get('input[placeholder*="昵称"]').type('E2E测试用户');
|
||||
cy.get('input[placeholder*="设置密码"]').type(testPassword);
|
||||
cy.get('input[placeholder*="确认密码"]').type(testPassword);
|
||||
|
||||
// 点击提交注册
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
|
||||
// 验证注册成功 - 等待页面刷新并检查登录状态
|
||||
// 注册成功后会自动刷新页面,所以等待头像出现即可
|
||||
cy.get('.ant-avatar', { timeout: 15000 }).should('exist');
|
||||
|
||||
cy.log(`✅ 注册测试通过 - 手机号: ${testPhone}`);
|
||||
});
|
||||
|
||||
it('应该能够使用已注册账号登录', () => {
|
||||
// 先注册
|
||||
cy.get('.btn-register').click();
|
||||
cy.get('.auth-modal').should('be.visible');
|
||||
cy.get('input[placeholder*="手机号"]').type(testPhone);
|
||||
cy.get('input[placeholder*="设置密码"]').type(testPassword);
|
||||
cy.get('input[placeholder*="确认密码"]').type(testPassword);
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
|
||||
// 等待注册成功和页面刷新 - 等待头像出现
|
||||
cy.get('.ant-avatar', { timeout: 15000 }).should('exist');
|
||||
|
||||
// 退出登录
|
||||
cy.get('.ant-avatar').click();
|
||||
cy.contains('退出登录').click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 重新登录
|
||||
cy.visit('/');
|
||||
cy.wait(1000);
|
||||
cy.get('.btn-login').click();
|
||||
cy.get('.auth-modal').should('be.visible');
|
||||
cy.get('input[placeholder*="手机号"]').type(testPhone);
|
||||
cy.get('input[placeholder*="密码"]').first().type(testPassword);
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
|
||||
// 验证登录成功 - 等待页面刷新并检查登录状态
|
||||
cy.get('.ant-avatar', { timeout: 15000 }).should('exist');
|
||||
|
||||
cy.log(`✅ 登录测试通过 - 手机号: ${testPhone}`);
|
||||
});
|
||||
|
||||
it('应该验证手机号格式', () => {
|
||||
cy.get('.btn-register').click();
|
||||
cy.get('.auth-modal').should('be.visible');
|
||||
|
||||
// 输入无效手机号
|
||||
cy.get('input[placeholder*="手机号"]').type('123456');
|
||||
cy.get('input[placeholder*="设置密码"]').type(testPassword);
|
||||
cy.get('input[placeholder*="确认密码"]').type(testPassword);
|
||||
|
||||
// 尝试提交
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
|
||||
// 应该显示错误提示
|
||||
cy.contains('请输入正确的11位手机号').should('be.visible');
|
||||
|
||||
cy.log('✅ 手机号验证测试通过');
|
||||
});
|
||||
|
||||
it('应该验证密码确认', () => {
|
||||
cy.get('.btn-register').click();
|
||||
cy.get('.auth-modal').should('be.visible');
|
||||
|
||||
// 输入不匹配的密码
|
||||
cy.get('input[placeholder*="手机号"]').type(testPhone);
|
||||
cy.get('input[placeholder*="设置密码"]').type(testPassword);
|
||||
cy.get('input[placeholder*="确认密码"]').type('DifferentPassword123');
|
||||
|
||||
// 尝试提交
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
|
||||
// 应该显示密码不一致错误
|
||||
cy.contains('两次输入的密码不一致').should('be.visible');
|
||||
|
||||
cy.log('✅ 密码确认测试通过');
|
||||
});
|
||||
});
|
||||
124
frontend/cypress/e2e/purchase.cy.js
Normal file
@@ -0,0 +1,124 @@
|
||||
// 生成随机手机号
|
||||
const generatePhone = () => {
|
||||
const timestamp = Date.now().toString().slice(-8);
|
||||
return `138${timestamp}`;
|
||||
};
|
||||
|
||||
describe('购买流程测试', () => {
|
||||
let testPhone;
|
||||
let testPassword = 'Test123456';
|
||||
|
||||
beforeEach(() => {
|
||||
testPhone = generatePhone();
|
||||
|
||||
// 注册并登录
|
||||
cy.visit('/');
|
||||
cy.wait(1000);
|
||||
cy.get('.btn-register').click();
|
||||
cy.get('.auth-modal').should('be.visible');
|
||||
cy.get('input[placeholder*="手机号"]').type(testPhone);
|
||||
cy.get('input[placeholder*="设置密码"]').type(testPassword);
|
||||
cy.get('input[placeholder*="确认密码"]').type(testPassword);
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
cy.wait(3000); // 等待页面刷新
|
||||
});
|
||||
|
||||
it('未登录用户点击下载应该提示登录', () => {
|
||||
// 退出登录
|
||||
cy.get('.ant-avatar').click();
|
||||
cy.contains('退出登录').click();
|
||||
cy.wait(1000);
|
||||
|
||||
// 进入作品详情页
|
||||
cy.visit('/');
|
||||
cy.wait(1000);
|
||||
cy.get('.work-card', { timeout: 15000 }).first().click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 点击下载按钮
|
||||
cy.get('.download-btn').click();
|
||||
|
||||
// 应该显示登录提示
|
||||
cy.contains('请先登录', { timeout: 5000 }).should('exist');
|
||||
|
||||
cy.log('✅ 未登录下载提示测试通过');
|
||||
});
|
||||
|
||||
it('已登录用户应该能看到购买弹窗', () => {
|
||||
// 进入作品详情页
|
||||
cy.get('.work-card', { timeout: 15000 }).first().click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 点击下载按钮
|
||||
cy.get('.download-btn').click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 检查是否显示购买弹窗或直接下载
|
||||
cy.get('body').then($body => {
|
||||
if ($body.find('.ant-modal:visible').length > 0) {
|
||||
cy.get('.ant-modal').should('be.visible');
|
||||
cy.contains('购买作品').should('exist');
|
||||
cy.log('✅ 购买弹窗测试通过');
|
||||
} else {
|
||||
cy.log('✅ 已购买,直接下载测试通过');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('点击确定购买应该创建订单', () => {
|
||||
// 进入作品详情页
|
||||
cy.get('.work-card', { timeout: 15000 }).first().click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 点击下载按钮
|
||||
cy.contains('button', '我要下载').click();
|
||||
cy.wait(1000);
|
||||
|
||||
// 如果显示购买弹窗
|
||||
cy.get('body').then($body => {
|
||||
if ($body.find('.ant-modal:contains("购买")').length > 0) {
|
||||
// 拦截网络请求
|
||||
cy.intercept('POST', '**/orders/create').as('createOrder');
|
||||
cy.intercept('POST', '**/payment/create').as('createPayment');
|
||||
|
||||
// 点击确定购买
|
||||
cy.get('.ant-modal').contains('button', '确定购买').click();
|
||||
|
||||
// 等待请求完成
|
||||
cy.wait('@createOrder', { timeout: 10000 }).its('response.statusCode').should('be.oneOf', [200, 201]);
|
||||
|
||||
cy.log('✅ 购买流程测试通过');
|
||||
} else {
|
||||
cy.log('✅ 已购买作品,跳过购买测试');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('应该验证收藏功能', () => {
|
||||
// 进入作品详情页
|
||||
cy.get('.work-card', { timeout: 15000 }).first().click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 点击收藏按钮
|
||||
cy.get('.collect-btn').click();
|
||||
cy.wait(500);
|
||||
|
||||
// 应该显示消息
|
||||
cy.contains('收藏成功', { timeout: 5000 }).should('exist');
|
||||
cy.log('✅ 收藏功能测试通过');
|
||||
});
|
||||
|
||||
it('应该验证分享功能', () => {
|
||||
// 进入作品详情页
|
||||
cy.get('.work-card', { timeout: 15000 }).first().click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 点击分享按钮
|
||||
cy.get('.share-btn').click();
|
||||
cy.wait(500);
|
||||
|
||||
// 应该显示成功消息
|
||||
cy.contains('链接已复制', { timeout: 5000 }).should('exist');
|
||||
cy.log('✅ 分享功能测试通过');
|
||||
});
|
||||
});
|
||||
70
frontend/cypress/e2e/works.cy.js
Normal file
@@ -0,0 +1,70 @@
|
||||
describe('作品浏览测试', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('应该显示作品列表', () => {
|
||||
// 等待作品列表加载
|
||||
cy.get('.work-card', { timeout: 15000 }).should('have.length.at.least', 1);
|
||||
|
||||
// 获取作品数量
|
||||
cy.get('.work-card').then($cards => {
|
||||
cy.log(`✅ 作品列表测试通过 - 共 ${$cards.length} 个作品`);
|
||||
});
|
||||
});
|
||||
|
||||
it('应该能够查看作品详情', () => {
|
||||
// 等待作品列表加载
|
||||
cy.get('.work-card', { timeout: 15000 }).should('exist');
|
||||
|
||||
// 点击第一个作品
|
||||
cy.get('.work-card').first().click();
|
||||
|
||||
// 等待详情页加载
|
||||
cy.url().should('include', '/detail/');
|
||||
cy.get('.work-detail-page', { timeout: 10000 }).should('be.visible');
|
||||
|
||||
// 验证标题存在
|
||||
cy.get('h1, h2').should('exist');
|
||||
|
||||
// 获取作品标题
|
||||
cy.get('h1, h2').first().invoke('text').then(title => {
|
||||
cy.log(`✅ 作品详情测试通过 - 作品: ${title.trim()}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('应该显示作品信息', () => {
|
||||
// 进入作品详情页
|
||||
cy.get('.work-card', { timeout: 15000 }).first().click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 验证作品信息显示
|
||||
cy.get('.work-detail-page').should('be.visible');
|
||||
|
||||
// 验证关键元素
|
||||
cy.get('.work-title').should('exist');
|
||||
cy.get('.download-btn').should('exist');
|
||||
|
||||
cy.log('✅ 作品信息显示测试通过');
|
||||
});
|
||||
|
||||
it('应该显示相关作品', () => {
|
||||
// 进入作品详情页
|
||||
cy.get('.work-card', { timeout: 15000 }).first().click();
|
||||
cy.wait(2000);
|
||||
|
||||
// 向下滚动查看相关作品
|
||||
cy.scrollTo('bottom', { duration: 1000 });
|
||||
cy.wait(1000);
|
||||
|
||||
// 验证相关作品存在(如果有的话)
|
||||
cy.get('body').then($body => {
|
||||
if ($body.find('.related-works, .similar-works').length > 0) {
|
||||
cy.get('.related-works, .similar-works').should('be.visible');
|
||||
cy.log('✅ 相关作品显示测试通过');
|
||||
} else {
|
||||
cy.log('✅ 相关作品功能未实现(跳过)');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 89 KiB |
48
frontend/cypress/support/commands.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
|
||||
// -- This is a custom command for login --
|
||||
Cypress.Commands.add('login', (phone, password) => {
|
||||
cy.visit('/');
|
||||
cy.wait(1000);
|
||||
cy.get('.btn-login').click();
|
||||
cy.get('.auth-modal').should('be.visible');
|
||||
cy.get('input[placeholder*="手机号"]').type(phone);
|
||||
cy.get('input[placeholder*="密码"]').first().type(password);
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
cy.wait(3000);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('register', (phone, password, nickname = '测试用户') => {
|
||||
cy.visit('/');
|
||||
cy.wait(1000);
|
||||
cy.get('.btn-register').click();
|
||||
cy.get('.auth-modal').should('be.visible');
|
||||
cy.get('input[placeholder*="手机号"]').type(phone);
|
||||
if (nickname) {
|
||||
cy.get('input[placeholder*="昵称"]').type(nickname);
|
||||
}
|
||||
cy.get('input[placeholder*="设置密码"]').type(password);
|
||||
cy.get('input[placeholder*="确认密码"]').type(password);
|
||||
cy.get('.auth-modal .auth-submit-btn').click();
|
||||
cy.wait(3000);
|
||||
});
|
||||
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
20
frontend/cypress/support/e2e.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/e2e.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||