Skip to content
Merged
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
10 changes: 10 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'cypress'

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:10086/#/',
specPattern: 'cypress/e2e/**/*.js',
},
viewportWidth: 414,
viewportHeight: 896,
})
67 changes: 67 additions & 0 deletions cypress/component/Actionsheet.cy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react'
import { mount } from 'cypress/react18'
import { ActionSheet } from '../../src/packages/actionsheet/actionsheet'

const menulist = [
{
name: '选项一',
description: '选项一的描述信息',
danger: true,
},
{
name: '选项二',
disabled: true,
},
{
name: '必填',
name1: '选项三',
},
]

it('props test options ', () => {
mount(
<ActionSheet
visible
title="弹层标题"
description="弹层描述信息"
cancelText="关闭弹层"
options={menulist}
/>
)
cy.get('.nut-actionsheet-list .nut-actionsheet-item').should('have.length', 3)
})

it('props test cancelText ', async () => {
mount(
<ActionSheet
visible
title="弹层标题"
description="弹层描述信息"
cancelText="关闭弹层"
options={menulist}
/>
)
cy.get('.nut-actionsheet-cancel').then(($el) => {
const el = cy.wrap($el)
el.should('have.text', '关闭弹层')
})
})

it('props test has value ', async () => {
mount(
<ActionSheet
visible
title="弹层标题"
description="弹层描述信息"
cancelText="关闭弹层"
options={menulist}
/>
)
cy.get('.nut-actionsheet-list .nut-actionsheet-item')
.eq(0)
.then(($el) => {
const el = cy.wrap($el)
el.should('have.text', '选项一选项一的描述信息')
el.should('have.class', 'danger')
})
})
6 changes: 6 additions & 0 deletions cypress/component/Button.cy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react'
import { Button } from '../../src/packages/button/button'

it('playground', () => {
cy.mount(<Button type="primary">123</Button>)
})
37 changes: 37 additions & 0 deletions cypress/component/Cell.cy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'
import { Cell } from '../../src/packages/cell/cell.taro'
import { Switch } from '../../src/packages/switch/switch.taro'

it('prop title extra description test', () => {
cy.mount(
<Cell
data-testid="prop"
title="我是标题"
description="我是描述"
extra="描述文字"
/>
)
cy.get('.nut-cell-title').should('contain.text', '我是标题')
cy.get('.nut-cell-description').should('contain.text', '我是描述')
cy.get('.nut-cell-extra').should('contain.text', '描述文字')
})
it('prop ', () => {
cy.mount(<Cell title="URL 跳转" extra="https://m.jd.com/" />)
cy.get('.nut-cell-extra').should('be.visible')
})

it('emit click event', () => {
const testClick = () => {}
cy.mount(<Cell data-testid="emit-click" onClick={() => testClick()} />)
cy.get('[data-testid="emit-click"]').click().trigger('testClick')
})

it('slot default test', () => {
cy.mount(<Cell title={<div>自定义内容</div>} extra="描述文字" />)
cy.root().should('contain.html', '<div>自定义内容</div>')
})

it('slot extra', () => {
cy.mount(<Cell title="Switch" extra={<Switch defaultChecked />} />)
cy.get('.nut-switch').should('be.visible')
})
8 changes: 8 additions & 0 deletions cypress/e2e/index.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import './taro/base.cy'
import './taro/layout.cy'
import './taro/nav.cy'
import './taro/dentry.cy'

Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
116 changes: 116 additions & 0 deletions cypress/e2e/taro/base.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
const getPath = (component) => `base/pages/${component}/index`

describe('base components test', () => {
it('button successfully passes', () => {
cy.visit(getPath('button'))
cy.get('.applets-demo-header').contains('Button')
// Click Loading Test and loading icon shows
cy.contains('button', 'Click me').scrollIntoView().click()
cy.contains('button', 'Click me')
.parent()
.find('i.nut-icon-Loading')
.should('exist')
cy.wait(500)
})

it('cell successfully passes', () => {
cy.visit(getPath('cell'))
cy.get('.applets-demo-header').contains('Cell')
// Click Switch
cy.contains('自定义右侧区域').scrollIntoView()
cy.get('.nut-switch-button').click()
cy.get('.nut-switch-button')
.parent()
.should('have.class', 'nut-switch-close')
cy.get('.nut-switch-button').click()
cy.get('.nut-switch-button')
.parent()
.should('have.class', 'nut-switch-open')
// Jump URL
cy.contains('链接 | 分组用法').scrollIntoView()
cy.contains('/pages/index/index').click()
cy.wait(500)
cy.window().then((location) => {
expect(location.location.pathname).to.equal('/')
})
cy.wait(400)
})

it('ConfigProvider successfully passes', () => {
cy.visit(getPath('configprovider'))
cy.get('.applets-demo-header').contains('ConfigProvider')
// Expected Rate
cy.get('.nut-rate')
.first()
.find('.nut-rate-item')
.eq(-2)
.find('.nut-rate-item-icon')
.click()
cy.get('.nut-rate')
.first()
.find('.nut-rate-item .nut-rate-item-icon')
.filter('.nut-rate-item-icon-disabled')
.should('have.length', 1)
cy.get('.nut-rate')
.first()
.find('.nut-rate-item')
.first()
.find('.nut-rate-item-icon')
.click()
cy.get('.nut-rate')
.first()
.find('.nut-rate-item .nut-rate-item-icon')
.filter('.nut-rate-item-icon-disabled')
.should('have.length', 4)
cy.wait(400)
})

it('Icon successfully passes', () => {
cy.visit(getPath('icon'))
cy.get('.applets-demo-header').contains('Icon')
// V12 Icon Click Test
cy.contains('V12 Icon').scrollIntoView()
cy.get('i.nut-icon-thumbs-up').click()
cy.contains('<ThumbsUp />')
cy.get(
'i.nut-icon-heart-fill.nut-icon-am-breathe.nut-icon-am-infinite'
).click()
cy.contains(
`<HeartFill className='nut-icon-am-breathe nut-icon-am-infinite' />`
)
cy.wait(400)
})
it('Image successfully passes', () => {
cy.visit(getPath('image'))
cy.get('.applets-demo-header').contains('Image')
cy.wait(500)
// Lazy Load Scroll
cy.contains('图片懒加载').scrollIntoView()
cy.get('.taro-scroll-view__scroll-y .taro-img__mode-scaletofill')
.filter('[src]')
.should('have.length', 3)
cy.get('.taro-scroll-view__scroll-y').scrollTo('bottom', { duration: 1000 })
cy.get('.taro-scroll-view__scroll-y .taro-img__mode-scaletofill')
.filter('[src]')
.should('have.length.greaterThan', 3)
cy.wait(400)
})
it('Overlay successfully passes', () => {
cy.visit(getPath('overlay'))
cy.get('.applets-demo-header').contains('Overlay')
// Display Overlay
cy.contains('嵌套内容').scrollIntoView()
cy.get('.nut-button-success').click()
cy.contains('这里是正文')
cy.contains('这里是正文')
.parent()
.parent()
.should('have.class', 'nut-overlay')
.click()
cy.contains('这里是正文').should('not.exist')
cy.get('nut-overlay').should('not.exist')
})
})
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
45 changes: 45 additions & 0 deletions cypress/e2e/taro/dentry.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const componentTest = (comName, fn) => {
const dentryArr = [
'Address',
'Calendar',
'CalendarCard',
'Cascader',
'Checkbox',
'DatePicker',
'Form',
'Input',
'InputNumber',
]
const getPath = (component) =>
`${dentryArr.includes(component) ? 'dentry' : 'dentry1'}/pages/${component.toLowerCase()}/index`

it(`${comName} successfully passes`, () => {
cy.visit(getPath(comName))
cy.get('.applets-demo-header').contains(comName)
cy.wait(400)
fn()
})
}
Comment on lines +1 to +22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

测试工具函数需要改进

工具函数存在以下问题:

  1. dentryArr 数组不完整,与实际使用的组件列表不匹配。例如 'Menu'、'NumberKeyboard' 等组件未包含在数组中。
  2. 使用硬编码的等待时间 (400ms) 可能导致测试不稳定。
  3. 缺少对输入参数的类型检查和验证。

建议进行如下修改:

 const componentTest = (comName, fn) => {
+  if (typeof comName !== 'string' || typeof fn !== 'function') {
+    throw new Error('Invalid parameters: comName must be string, fn must be function')
+  }
   const dentryArr = [
     'Address',
     'Calendar',
     'CalendarCard',
     'Cascader',
     'Checkbox',
     'DatePicker',
     'Form',
     'Input',
     'InputNumber',
+    'Menu',
+    'NumberKeyboard',
+    'Picker',
+    'Radio',
+    'Range',
+    'Rate',
+    'SearchBar',
+    'ShortPassword',
+    'Signature',
+    'Switch',
+    'TextArea',
+    'Uploader'
   ]
   const getPath = (component) =>
     `${dentryArr.includes(component) ? 'dentry' : 'dentry1'}/pages/${component.toLowerCase()}/index`

   it(`${comName} successfully passes`, () => {
     cy.visit(getPath(comName))
     cy.get('.applets-demo-header').contains(comName)
-    cy.wait(400)
+    // 使用 Cypress 内置的等待机制替代固定时间等待
+    cy.get('.applets-demo-header').should('be.visible')
     fn()
   })
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const componentTest = (comName, fn) => {
const dentryArr = [
'Address',
'Calendar',
'CalendarCard',
'Cascader',
'Checkbox',
'DatePicker',
'Form',
'Input',
'InputNumber',
]
const getPath = (component) =>
`${dentryArr.includes(component) ? 'dentry' : 'dentry1'}/pages/${component.toLowerCase()}/index`
it(`${comName} successfully passes`, () => {
cy.visit(getPath(comName))
cy.get('.applets-demo-header').contains(comName)
cy.wait(400)
fn()
})
}
const componentTest = (comName, fn) => {
if (typeof comName !== 'string' || typeof fn !== 'function') {
throw new Error('Invalid parameters: comName must be string, fn must be function')
}
const dentryArr = [
'Address',
'Calendar',
'CalendarCard',
'Cascader',
'Checkbox',
'DatePicker',
'Form',
'Input',
'InputNumber',
'Menu',
'NumberKeyboard',
'Picker',
'Radio',
'Range',
'Rate',
'SearchBar',
'ShortPassword',
'Signature',
'Switch',
'TextArea',
'Uploader'
]
const getPath = (component) =>
`${dentryArr.includes(component) ? 'dentry' : 'dentry1'}/pages/${component.toLowerCase()}/index`
it(`${comName} successfully passes`, () => {
cy.visit(getPath(comName))
cy.get('.applets-demo-header').contains(comName)
// 使用 Cypress 内置的等待机制替代固定时间等待
cy.get('.applets-demo-header').should('be.visible')
fn()
})
}

describe('layout components test', () => {
componentTest('Address', () => {})
componentTest('Calendar', () => {})
componentTest('CalendarCard', () => {})
componentTest('Cascader', () => {})
componentTest('Checkbox', () => {})
componentTest('DatePicker', () => {})
componentTest('Form', () => {})
componentTest('Input', () => {})
componentTest('InputNumber', () => {})
componentTest('Menu', () => {})
componentTest('NumberKeyboard', () => {})
componentTest('Picker', () => {})
componentTest('Radio', () => {})
componentTest('Range', () => {})
componentTest('Rate', () => {})
componentTest('SearchBar', () => {})
componentTest('ShortPassword', () => {})
componentTest('Signature', () => {})
componentTest('Switch', () => {})
componentTest('TextArea', () => {})
componentTest('Uploader', () => {})
})
Comment on lines +23 to +45
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

测试用例实现不完整

当前的测试实现存在以下问题:

  1. 所有测试回调函数都是空的,没有实际的测试逻辑
  2. 缺少对组件核心功能的验证
  3. 没有测试错误场景和边界条件
  4. 缺少测试用例的文档说明

建议为每个组件添加具体的测试用例。我可以帮助生成详细的测试实现代码,包括:

  • 组件渲染验证
  • 交互功能测试
  • 错误场景处理
  • 边界条件测试

是否需要我为某个特定组件生成完整的测试用例实现?

78 changes: 78 additions & 0 deletions cypress/e2e/taro/layout.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const getPath = (component) => `layout/pages/${component}/index`

Comment on lines +1 to +2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议将 getPath 函数移至共享工具文件

考虑到该函数在多个测试文件中被重复使用,建议将其提取到一个共享的工具文件中(例如 cypress/support/utils.js),以提高代码的可维护性和复用性。

+// cypress/support/utils.js
+export const getPath = (component) => `layout/pages/${component}/index`

-// cypress/e2e/taro/layout.cy.js
-const getPath = (component) => `layout/pages/${component}/index`

Committable suggestion was skipped due to low confidence.

describe('layout components test', () => {
it('divider successfully passes', () => {
cy.visit(getPath('divider'))
cy.get('.applets-demo-header').contains('Divider')
// Click URL
cy.contains('垂直分割线').scrollIntoView()
cy.contains('链接').click()
cy.window().then((win) => {
const currentPath = win.location.pathname
expect(currentPath).to.equal('/')
})
cy.wait(400)
})
Comment on lines +4 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议优化测试稳定性

  1. 建议在滚动前添加元素可见性检查
  2. 建议使用 Cypress 的内置等待机制替代固定等待时间
 it('divider successfully passes', () => {
   cy.visit(getPath('divider'))
   cy.get('.applets-demo-header').contains('Divider')
   // Click URL
-  cy.contains('垂直分割线').scrollIntoView()
+  cy.contains('垂直分割线').should('be.visible').scrollIntoView()
   cy.contains('链接').click()
   cy.window().then((win) => {
     const currentPath = win.location.pathname
     expect(currentPath).to.equal('/')
   })
-  cy.wait(400)
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('divider successfully passes', () => {
cy.visit(getPath('divider'))
cy.get('.applets-demo-header').contains('Divider')
// Click URL
cy.contains('垂直分割线').scrollIntoView()
cy.contains('链接').click()
cy.window().then((win) => {
const currentPath = win.location.pathname
expect(currentPath).to.equal('/')
})
cy.wait(400)
})
it('divider successfully passes', () => {
cy.visit(getPath('divider'))
cy.get('.applets-demo-header').contains('Divider')
// Click URL
cy.contains('垂直分割线').should('be.visible').scrollIntoView()
cy.contains('链接').click()
cy.window().then((win) => {
const currentPath = win.location.pathname
expect(currentPath).to.equal('/')
})
})


it('grid successfully passes', () => {
cy.visit(getPath('grid'))
cy.get('.applets-demo-header').contains('Grid')
// GridItem Click
cy.contains('点击子项事件').scrollIntoView()
cy.get('h2').then((element) => {
if (element.has('点击子项事件')) {
cy.wrap(element)
.next()
.then((nextElement) => {
// 在这里对下一个节点进行操作
nextElement.find('.nut-grid-item').eq(-2).click()
cy.contains('点击了文字,第2个')
})
}
})
cy.wait(400)
})
Comment on lines +17 to +34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议简化测试逻辑并统一语言风格

  1. DOM 遍历逻辑过于复杂,可以使用更直接的选择器
  2. 测试描述应该使用中文以保持一致性
-  it('grid successfully passes', () => {
+  it('Grid 组件测试', () => {
     cy.visit(getPath('grid'))
     cy.get('.applets-demo-header').contains('Grid')
-    // GridItem Click
+    // 测试点击事件
     cy.contains('点击子项事件').scrollIntoView()
-    cy.get('h2').then((element) => {
-      if (element.has('点击子项事件')) {
-        cy.wrap(element)
-          .next()
-          .then((nextElement) => {
-            // 在这里对下一个节点进行操作
-            nextElement.find('.nut-grid-item').eq(-2).click()
-            cy.contains('点击了文字,第2个')
-          })
-      }
-    })
-    cy.wait(400)
+    cy.contains('点击子项事件')
+      .parent()
+      .find('.nut-grid-item')
+      .eq(1)
+      .click()
+    cy.contains('点击了文字,第2个').should('be.visible')
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('grid successfully passes', () => {
cy.visit(getPath('grid'))
cy.get('.applets-demo-header').contains('Grid')
// GridItem Click
cy.contains('点击子项事件').scrollIntoView()
cy.get('h2').then((element) => {
if (element.has('点击子项事件')) {
cy.wrap(element)
.next()
.then((nextElement) => {
// 在这里对下一个节点进行操作
nextElement.find('.nut-grid-item').eq(-2).click()
cy.contains('点击了文字,第2个')
})
}
})
cy.wait(400)
})
it('Grid 组件测试', () => {
cy.visit(getPath('grid'))
cy.get('.applets-demo-header').contains('Grid')
// 测试点击事件
cy.contains('点击子项事件').scrollIntoView()
cy.contains('点击子项事件')
.parent()
.find('.nut-grid-item')
.eq(1)
.click()
cy.contains('点击了文字,第2个').should('be.visible')
})


it('layout successfully passes', () => {
cy.visit(getPath('layout'))
cy.get('.applets-demo-header').contains('Layout')
// layout test
cy.contains('span:24').parent().children().should('have.length', 1)
cy.contains('span:12').parent().parent().children().should('have.length', 2)
cy.contains('span:8').parent().parent().children().should('have.length', 3)
cy.contains('span:6').parent().parent().children().should('have.length', 4)
cy.wait(400)
})

it('space successfully passes', () => {
cy.visit(getPath('space'))
cy.get('.applets-demo-header').contains('Space')
// Space Test
cy.contains('垂直')
.next()
.find('.nut-space')
.should('have.class', 'nut-space-vertical')
cy.wait(400)
})

it('sticky successfully passes', () => {
cy.visit(getPath('sticky'))
cy.get('.applets-demo-header').contains('Sticky')
// Scroll Sticky
cy.get('.demo').scrollTo('bottom')
cy.contains('距离顶部120px').parent().should('have.css', 'top', '120px')
cy.get('.demo').scrollTo('top')
cy.contains('距离底部0px').parent().should('have.css', 'bottom', '0px')
cy.wait(400)
})
Comment on lines +58 to +67
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议增强 Sticky 组件测试的稳定性

  1. 建议在滚动操作后添加等待条件
  2. 建议验证元素是否真正固定在视口中
   it('sticky successfully passes', () => {
     cy.visit(getPath('sticky'))
     cy.get('.applets-demo-header').contains('Sticky')
-    // Scroll Sticky
+    // 测试顶部固定效果
     cy.get('.demo').scrollTo('bottom')
+    cy.contains('距离顶部120px').should('be.visible')
     cy.contains('距离顶部120px').parent().should('have.css', 'top', '120px')
+    
+    // 测试底部固定效果
     cy.get('.demo').scrollTo('top')
+    cy.contains('距离底部0px').should('be.visible')
     cy.contains('距离底部0px').parent().should('have.css', 'bottom', '0px')
-    cy.wait(400)
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('sticky successfully passes', () => {
cy.visit(getPath('sticky'))
cy.get('.applets-demo-header').contains('Sticky')
// Scroll Sticky
cy.get('.demo').scrollTo('bottom')
cy.contains('距离顶部120px').parent().should('have.css', 'top', '120px')
cy.get('.demo').scrollTo('top')
cy.contains('距离底部0px').parent().should('have.css', 'bottom', '0px')
cy.wait(400)
})
it('sticky successfully passes', () => {
cy.visit(getPath('sticky'))
cy.get('.applets-demo-header').contains('Sticky')
// 测试顶部固定效果
cy.get('.demo').scrollTo('bottom')
cy.contains('距离顶部120px').should('be.visible')
cy.contains('距离顶部120px').parent().should('have.css', 'top', '120px')
// 测试底部固定效果
cy.get('.demo').scrollTo('top')
cy.contains('距离底部0px').should('be.visible')
cy.contains('距离底部0px').parent().should('have.css', 'bottom', '0px')
})


it('safearea successfully passes', () => {
cy.visit(getPath('safearea'))
cy.get('.applets-demo-header').contains('SafeArea')
// SafeArea Class
cy.get('.demo').scrollTo('bottom')
cy.get('.nut-safe-area').should('exist')
cy.get('.nut-safe-area-position-bottom').should('exist')
cy.wait(400)
})
})
Loading