Repository: Tencent/tdesign-mobile-vue
Pull request #314 (2022-08-26 08:40:24)

test: add tabbar test
<!--
首先，感谢你的贡献！😄
请阅读并遵循 [TDesign 贡献指南](https://github.com/Tencent/tdesign/blob/main/docs/contributing.md)，填写以下 pull request 的信息。
PR 在维护者审核通过后会合并，谢谢！
-->

### 🤔 这个 PR 的性质是？

- [ ] 日常 bug 修复
- [ ] 新特性提交
- [ ] 文档改进
- [ ] 演示代码改进
- [ ] 组件样式/交互改进
- [ ] CI/CD 改进
- [ ] 重构
- [ ] 代码风格优化
- [x] 测试用例
- [ ] 分支合并
- [ ] 其他

### 🔗 相关 Issue

#271 
<!--
1. 描述相关需求的来源，如相关的 issue 讨论链接。
-->

### 💡 需求背景和解决方案

<!--
1. 要解决的具体问题。
2. 列出最终的 API 实现和用法。
3. 涉及UI/交互变动需要有截图或 GIF。
-->

### 📝 更新日志

<!--
从用户角度描述具体变化，以及可能的 breaking change 和其他风险。
-->

- fix(组件名称): 处理问题或特性描述 ...

- [x] 本条 PR 不需要纳入 Changelog

### ☑️ 请求合并前的自查清单

⚠️ 请自检并全部**勾选全部选项**。⚠️

- [x] 文档已补充或无须补充
- [x] 代码演示已提供或无须提供
- [x] TypeScript 定义已补充或无须补充
- [x] Changelog 已提供或无须提供


github-actions[bot]: <a href="https://preview-pr314-tdesign-mobile-vue.surge.sh"><img height="96" alt="完成" src="https://user-images.githubusercontent.com/15634204/150816437-9f5bb788-cd67-4cbc-9897-b82d74e9aa65.png" /></a>
<!-- [工作流地址](https://github.com/Tencent/tdesign-mobile-vue/actions/runs/2932447171) -->
<!-- AUTO_PREVIEW_HOOK -->
anlyyao: icon类型是Slot / Function， 可以用

import { AppIcon as TIconApp } from 'tdesign-icons-vue-next';
const icon = () =>

的方式引用～

anlyyao: 没有生成徽标～ npm run update:coverage-badge

Patch:
diff --git a/site/docs.config.js b/site/docs.config.js
index 6a136ffd..328c56c4 100644
--- a/site/docs.config.js
+++ b/site/docs.config.js
@@ -70,7 +70,7 @@ export default {
           path: '/mobile-vue/components/grid',
           component: () => import('@/grid/grid.md'),
         },
-      ]
+      ],
     },
     {
       title: '导航',
diff --git a/site/vite.config.js b/site/vite.config.js
index f45a56ef..7d611c45 100644
--- a/site/vite.config.js
+++ b/site/vite.config.js
@@ -79,7 +79,7 @@ export default ({ mode }) => {
         },
       }),
       vueJsx({
-        isCustomElement
+        isCustomElement,
       }),
       createTDesignPlugin(),
     ],
diff --git a/src/tab-bar/tab-bar-item.vue b/src/tab-bar/tab-bar-item.vue
index 837bdc04..d95ab673 100644
--- a/src/tab-bar/tab-bar-item.vue
+++ b/src/tab-bar/tab-bar-item.vue
@@ -91,6 +91,7 @@ export default defineComponent({
         isSpread.value = !isSpread.value;
         if (!isToggleCurrent.value) {
           updateChild([currentName]);
+          return;
         }
       }
       updateChild(currentName);
diff --git a/src/tab-bar/tab-bar.vue b/src/tab-bar/tab-bar.vue
index e26b570c..01d900c3 100644
--- a/src/tab-bar/tab-bar.vue
+++ b/src/tab-bar/tab-bar.vue
@@ -5,7 +5,7 @@
 </template>
 
 <script lang="ts">
-import { defineComponent, ref, provide, watch, Ref, computed, SetupContext } from 'vue';
+import { defineComponent, ref, provide, Ref, computed, SetupContext } from 'vue';
 import TabBarProps from './props';
 import config from '../config';
 import { useDefault } from '../shared';


Test patch:
diff --git a/site/web/test-coverage.js b/site/web/test-coverage.js
index d35a1fbb..ef34f280 100644
--- a/site/web/test-coverage.js
+++ b/site/web/test-coverage.js
@@ -1,1 +1,11 @@
-module.exports = {"button":{"statements":"98.57%","branches":"58.82%","functions":"100%","lines":"98.57%"},"checkbox":{"statements":"93.47%","branches":"75.8%","functions":"80%","lines":"93.47%"},"drawer":{"statements":"100%","branches":"93.75%","functions":"100%","lines":"100%"},"fab":{"statements":"100%","branches":"100%","functions":"100%","lines":"100%"},"overlay":{"statements":"92.55%","branches":"100%","functions":"20%","lines":"92.55%"},"popup":{"statements":"95%","branches":"64.7%","functions":"55.55%","lines":"95%"},"shared":{"statements":"70.58%","branches":"63.15%","functions":"25%","lines":"70.58%"},"switch":{"statements":"98.05%","branches":"92.85%","functions":"100%","lines":"98.05%"},"toast":{"statements":"100%","branches":"80%","functions":"100%","lines":"100%"}}
+module.exports = {
+  button: { statements: '98.57%', branches: '58.82%', functions: '100%', lines: '98.57%' },
+  checkbox: { statements: '93.47%', branches: '75.8%', functions: '80%', lines: '93.47%' },
+  drawer: { statements: '100%', branches: '93.75%', functions: '100%', lines: '100%' },
+  fab: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
+  overlay: { statements: '92.55%', branches: '100%', functions: '20%', lines: '92.55%' },
+  popup: { statements: '95%', branches: '64.7%', functions: '55.55%', lines: '95%' },
+  shared: { statements: '70.58%', branches: '63.15%', functions: '25%', lines: '70.58%' },
+  switch: { statements: '98.05%', branches: '92.85%', functions: '100%', lines: '98.05%' },
+  toast: { statements: '100%', branches: '80%', functions: '100%', lines: '100%' },
+};
diff --git a/src/tab-bar/__test__/__snapshots__/index.test.jsx.snap b/src/tab-bar/__test__/__snapshots__/index.test.jsx.snap
new file mode 100644
index 00000000..63a6bfcd
--- /dev/null
+++ b/src/tab-bar/__test__/__snapshots__/index.test.jsx.snap
@@ -0,0 +1,152 @@
+// Vitest Snapshot v1
+
+exports[`TabBar > props > snapshot 1`] = `
+<div
+  class="t-tab-bar t-tab-bar--bordered t-tab-bar--fixed"
+  role="tablist"
+>
+  
+  
+  <div
+    aria-label="TabBar"
+    aria-selected="false"
+    class="t-tab-bar-item t-no-border"
+    name="label_1"
+    role="tab"
+    text="标签一"
+  >
+    <div
+      class="t-tab-bar-item__content"
+    >
+      <div
+        class="t-tab-bar-item__icon"
+      >
+        <div
+          class="t-badge"
+        >
+          <div
+            class="t-badge__inner t-badge--has-children t-badge--medium t-badge--circle"
+            style="right: 0px; top: 5px;"
+          >
+            1
+          </div>
+          
+          <svg
+            class="t-icon t-icon-app"
+            fill="none"
+            height="1em"
+            viewBox="0 0 16 16"
+            width="1em"
+          >
+            <path
+              d="M11.25 1.75a3 3 0 100 6 3 3 0 000-6zm-2 3a2 2 0 114 0 2 2 0 01-4 0zM2 3a1 1 0 011-1h3.5a1 1 0 011 1v3.5a1 1 0 01-1 1H3a1 1 0 01-1-1V3zm1 0v3.5h3.5V3H3zM2 9.5a1 1 0 011-1h3.5a1 1 0 011 1V13a1 1 0 01-1 1H3a1 1 0 01-1-1V9.5zm1 0V13h3.5V9.5H3zM8.5 9.5a1 1 0 011-1H13a1 1 0 011 1V13a1 1 0 01-1 1H9.5a1 1 0 01-1-1V9.5zm1 3.5H13V9.5H9.5V13z"
+              fill="currentColor"
+              fill-opacity="0.9"
+            />
+          </svg>
+          
+        </div>
+      </div>
+      <!--v-if-->
+    </div>
+    <transition-stub>
+      <!--v-if-->
+    </transition-stub>
+  </div>
+  <div
+    aria-label="TabBar"
+    aria-selected="false"
+    class="t-tab-bar-item t-no-border"
+    name="label_2"
+    role="tab"
+    text="标签二"
+  >
+    <div
+      class="t-tab-bar-item__content"
+    >
+      <div
+        class="t-tab-bar-item__icon"
+      >
+        <div
+          class="t-badge"
+        >
+          <div
+            class="t-badge__inner t-badge--has-children t-badge--medium t-badge--circle"
+            style="right: 0px; top: 5px;"
+          >
+            1
+          </div>
+          
+          <svg
+            class="t-icon t-icon-app"
+            fill="none"
+            height="1em"
+            viewBox="0 0 16 16"
+            width="1em"
+          >
+            <path
+              d="M11.25 1.75a3 3 0 100 6 3 3 0 000-6zm-2 3a2 2 0 114 0 2 2 0 01-4 0zM2 3a1 1 0 011-1h3.5a1 1 0 011 1v3.5a1 1 0 01-1 1H3a1 1 0 01-1-1V3zm1 0v3.5h3.5V3H3zM2 9.5a1 1 0 011-1h3.5a1 1 0 011 1V13a1 1 0 01-1 1H3a1 1 0 01-1-1V9.5zm1 0V13h3.5V9.5H3zM8.5 9.5a1 1 0 011-1H13a1 1 0 011 1V13a1 1 0 01-1 1H9.5a1 1 0 01-1-1V9.5zm1 3.5H13V9.5H9.5V13z"
+              fill="currentColor"
+              fill-opacity="0.9"
+            />
+          </svg>
+          
+        </div>
+      </div>
+      <!--v-if-->
+    </div>
+    <transition-stub>
+      <!--v-if-->
+    </transition-stub>
+  </div>
+  <div
+    aria-haspopup="true"
+    aria-label="TabBar"
+    aria-selected="false"
+    class="t-tab-bar-item t-no-border"
+    name="label_3"
+    role="tab"
+    text="此处展开"
+  >
+    <div
+      class="t-tab-bar-item__content"
+    >
+      <div
+        class="t-tab-bar-item__icon"
+      >
+        <div
+          class="t-badge"
+        >
+          <div
+            class="t-badge__inner t-badge--has-children t-badge--medium t-badge--circle"
+            style="right: 0px; top: 5px;"
+          >
+            1
+          </div>
+          
+          <svg
+            class="t-icon t-icon-app"
+            fill="none"
+            height="1em"
+            viewBox="0 0 16 16"
+            width="1em"
+          >
+            <path
+              d="M11.25 1.75a3 3 0 100 6 3 3 0 000-6zm-2 3a2 2 0 114 0 2 2 0 01-4 0zM2 3a1 1 0 011-1h3.5a1 1 0 011 1v3.5a1 1 0 01-1 1H3a1 1 0 01-1-1V3zm1 0v3.5h3.5V3H3zM2 9.5a1 1 0 011-1h3.5a1 1 0 011 1V13a1 1 0 01-1 1H3a1 1 0 01-1-1V9.5zm1 0V13h3.5V9.5H3zM8.5 9.5a1 1 0 011-1H13a1 1 0 011 1V13a1 1 0 01-1 1H9.5a1 1 0 01-1-1V9.5zm1 3.5H13V9.5H9.5V13z"
+              fill="currentColor"
+              fill-opacity="0.9"
+            />
+          </svg>
+          
+        </div>
+      </div>
+      <!--v-if-->
+    </div>
+    <transition-stub>
+      <!--v-if-->
+    </transition-stub>
+  </div>
+  
+  
+</div>
+`;
diff --git a/src/tab-bar/__test__/index.test.jsx b/src/tab-bar/__test__/index.test.jsx
new file mode 100644
index 00000000..ed5468cb
--- /dev/null
+++ b/src/tab-bar/__test__/index.test.jsx
@@ -0,0 +1,149 @@
+import { describe, it, expect, vi } from 'vitest';
+import { mount } from '@vue/test-utils';
+import TabBar from '../tab-bar.vue'
+import TabBarItem from '../tab-bar-item.vue'
+import { nextTick, ref } from 'vue';
+import { AppIcon as TIconApp } from 'tdesign-icons-vue-next';
+
+const list = [
+  {
+    name: 'label_1',
+    text: '标签一',
+    icon: undefined,
+    value: '1'
+  },
+  {
+    name: 'label_2',
+    text: '标签二',
+    icon: undefined,
+    value: '2'
+  },
+  {
+    name: 'label_3',
+    text: '此处展开',
+    icon: undefined,
+    value: '3',
+    subTabBar: [
+      {
+        name: 'spread_1',
+        text: '展开项一',
+        icon: undefined,
+        value: '3-1',
+      },
+      {
+        name: 'spread_2',
+        text: '展开项二',
+        icon: undefined,
+        value: '3-2',
+      },
+      {
+        name: 'spread_3',
+        text: '展开项三',
+        icon: undefined,
+        value: '3-3',
+      },
+    ],
+  },
+];
+
+describe('TabBar', () => {
+  describe('props', () => {
+    it('bordered', async () => {
+      const wrapper = mount(TabBar, {
+        shallow: true
+      })
+      expect(wrapper.classes())
+        .toContain('t-tab-bar--bordered')
+
+      await wrapper.setProps({ 'bordered': false})
+      expect(wrapper.classes())
+        .not.toContain('t-tab-bar--bordered')
+    })
+
+    it('fixed', async () => {
+      const wrapper = mount(TabBar, {
+        shallow: true
+      })
+      expect(wrapper.classes())
+        .toContain('t-tab-bar--fixed')
+
+      await wrapper.setProps({ 'fixed': false})
+      expect(wrapper.classes())
+        .not.toContain('t-tab-bar--fixed')
+    })
+
+    it('defaultValue', async () => {
+      const wrapper = mount({
+        render: () => (
+          <TabBar defaultValue={"2"}>
+            {
+              list.map((item) => {
+                return <TabBarItem {...item}>{item.text}</TabBarItem>
+              })
+            }
+          </TabBar>
+        )
+      })
+
+      expect(wrapper.findAll('.t-tab-bar-item')).toHaveLength(list.length)
+      expect(wrapper.find('[aria-selected="true"]').attributes('text')).toEqual('标签二')
+    })
+
+    it ('snapshot', () => {
+      const icon = () => <TIconApp />
+      const wrapper = mount({
+        render: () => (
+          <TabBar>
+            {
+              list.map((item) => {
+                return <TabBarItem {...item} icon={icon} badgeProps={{count: 1}} />
+              })
+            }
+          </TabBar>
+        )
+      })
+      expect(wrapper.element).toMatchSnapshot();
+    })
+  })
+
+  describe('events', () => {
+    it ('click to change', async () => {
+      const onChange = vi.fn()
+      const value = ref('1')
+      const wrapper = mount({
+        render: () => (
+          <TabBar
+            v-model={value.value}
+            onChange={onChange}
+          >
+            {
+              list.map((item) => {
+                return <TabBarItem {...item} />
+              })
+            }
+          </TabBar>
+        )
+      })
+
+      // 从 bar1 切到 bar2
+      const bar2 = wrapper.find('[name="label_2"] > .t-tab-bar-item__content')
+      expect(onChange).toHaveBeenCalledTimes(0)
+      await bar2.trigger('click')
+      expect(onChange).toHaveBeenCalledTimes(1)
+      expect(onChange).toHaveBeenLastCalledWith('2')
+
+      // 从 bar2 切到 bar3
+      const bar3 = wrapper.find('[name="label_3"] > .t-tab-bar-item__content')
+      await bar3.trigger('click')
+      expect(onChange).toHaveBeenCalledTimes(2)
+      expect(onChange).toHaveBeenLastCalledWith(['3'])
+
+      // 点击 spread
+      const bar3_1 = wrapper.find('[name="label_3"] .t-tab-bar-item__spread-item')
+      await bar3_1.trigger('click')
+      expect(onChange).toHaveBeenCalledTimes(3)
+      expect(onChange).toHaveBeenLastCalledWith(['3', '3-1'])
+      expect(wrapper.find('[name="label_3"]').find('.t-tab-bar-item__spread-item').exists()).toBe(false)
+    })
+  })
+})