diff --git a/src/tabbar/test/__snapshots__/index.legacy.js.snap b/src/tabbar/test/__snapshots__/index.legacy.js.snap
deleted file mode 100644
index 9904a7823..000000000
--- a/src/tabbar/test/__snapshots__/index.legacy.js.snap
+++ /dev/null
@@ -1,179 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`disable border 1`] = `
`;
-
-exports[`placeholder prop 1`] = `
-
-`;
-
-exports[`route mode 1`] = `
-
-`;
-
-exports[`route mode 2`] = `
-
-`;
-
-exports[`route mode 3`] = `
-
-`;
-
-exports[`route mode match by name 1`] = `
-
-`;
-
-exports[`route mode match by name 2`] = `
-
-`;
-
-exports[`watch tabbar value 1`] = `
-
-`;
diff --git a/src/tabbar/test/__snapshots__/index.spec.js.snap b/src/tabbar/test/__snapshots__/index.spec.js.snap
new file mode 100644
index 000000000..3077eddb4
--- /dev/null
+++ b/src/tabbar/test/__snapshots__/index.spec.js.snap
@@ -0,0 +1,10 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render placeholder element when using placeholder prop 1`] = `
+
+`;
diff --git a/src/tabbar/test/index.legacy.js b/src/tabbar/test/index.legacy.js
deleted file mode 100644
index 8788bc7e2..000000000
--- a/src/tabbar/test/index.legacy.js
+++ /dev/null
@@ -1,188 +0,0 @@
-import VueRouter from 'vue-router';
-import { mount, later, mockGetBoundingClientRect } from '../../../test';
-import Vue from 'vue';
-import Tabbar from '..';
-
-Vue.use(VueRouter);
-
-test('route mode', async () => {
- const router = new VueRouter();
- const wrapper = mount({
- router,
- template: `
-
-
- Tab
-
-
- Tab
-
-
- Tab
-
-
- Tab
-
-
- `,
- });
-
- expect(wrapper.html()).toMatchSnapshot();
-
- const items = wrapper.findAll('.van-tabbar-item');
-
- items.at(1).trigger('click');
- await later();
- expect(wrapper.html()).toMatchSnapshot();
-
- items.at(2).trigger('click');
- items.at(3).trigger('click');
- await later();
- expect(wrapper.html()).toMatchSnapshot();
-});
-
-test('route mode match by name', async () => {
- const Foo = { render: () => 'Foo' };
- const Bar = { render: () => 'Bar' };
- const router = new VueRouter({
- routes: [
- { path: '/foo', component: Foo, name: 'foo' },
- { path: '/bar', component: Bar, name: 'bar' },
- ],
- });
-
- const wrapper = mount({
- router,
- template: `
-
-
- Tab
-
-
- Tab
-
-
- `,
- });
-
- const items = wrapper.findAll('.van-tabbar-item');
- items.at(0).trigger('click');
- await later();
- expect(wrapper.html()).toMatchSnapshot();
-
- items.at(1).trigger('click');
- await later();
- expect(wrapper.html()).toMatchSnapshot();
-});
-
-test('router NavigationDuplicated', async (done) => {
- expect(async () => {
- const router = new VueRouter();
- const wrapper = mount({
- router,
- template: `
-
-
- Tab
-
-
- `,
- });
-
- const item = wrapper.find('.van-tabbar-item');
- item.trigger('click');
- item.trigger('click');
-
- await later();
- done();
- }).not.toThrow();
-});
-
-test('watch tabbar value', () => {
- const wrapper = mount({
- template: `
-
- Tab
- Tab
-
- `,
- data() {
- return {
- value: 0,
- };
- },
- });
-
- wrapper.setData({ value: 1 });
- expect(wrapper.html()).toMatchSnapshot();
-});
-
-test('click event', () => {
- const onClick = jest.fn();
- const onChange = jest.fn();
-
- const wrapper = mount({
- template: `
-
- Tab
-
- `,
- methods: {
- onClick,
- onChange,
- },
- });
-
- wrapper.find('.van-tabbar-item').trigger('click');
- expect(onClick).toHaveBeenCalledTimes(1);
- expect(onChange).toHaveBeenCalledTimes(0);
-});
-
-test('name prop', () => {
- const onChange = jest.fn();
- const wrapper = mount({
- template: `
-
- Tab
- Tab
-
- `,
- data() {
- return {
- value: 'a',
- };
- },
- methods: {
- onChange,
- },
- });
-
- wrapper.findAll('.van-tabbar-item').at(1).trigger('click');
-
- expect(onChange).toHaveBeenCalledWith('b');
-});
-
-test('disable border', () => {
- const wrapper = mount(Tabbar, {
- props: {
- border: false,
- },
- });
-
- expect(wrapper.html()).toMatchSnapshot();
-});
-
-test('placeholder prop', () => {
- const restore = mockGetBoundingClientRect({ height: 50 });
-
- const wrapper = mount(Tabbar, {
- props: {
- fixed: true,
- placeholder: true,
- },
- });
-
- expect(wrapper.html()).toMatchSnapshot();
-
- restore();
-});
diff --git a/src/tabbar/test/index.spec.js b/src/tabbar/test/index.spec.js
new file mode 100644
index 000000000..e83433f94
--- /dev/null
+++ b/src/tabbar/test/index.spec.js
@@ -0,0 +1,172 @@
+import { nextTick, reactive, ref } from 'vue';
+import { mount, later, mockGetBoundingClientRect } from '../../../test';
+import Tabbar from '..';
+import TabbarItem from '../../tabbar-item';
+
+const activeClass = 'van-tabbar-item--active';
+
+function getMockRouter() {
+ const $route = reactive({
+ name: '/',
+ path: '/',
+ });
+ const push = (val) => {
+ if (typeof val === 'string') {
+ $route.name = val;
+ $route.path = val;
+ } else {
+ Object.assign($route, val);
+ }
+ };
+ const $router = {
+ push,
+ replace: push,
+ };
+
+ return {
+ $route,
+ $router,
+ };
+}
+
+test('should match active tab by route path in route mode', async () => {
+ const wrapper = mount(
+ {
+ render: () => (
+
+
+ Tab
+
+
+ Tab
+
+
+ Tab
+
+ Tab
+
+ ),
+ },
+ {
+ global: {
+ mocks: getMockRouter(),
+ },
+ }
+ );
+
+ const items = wrapper.findAll('.van-tabbar-item');
+
+ expect(items[0].element.classList.contains(activeClass)).toBeTruthy();
+
+ await items[1].trigger('click');
+ expect(items[1].element.classList.contains(activeClass)).toBeTruthy();
+
+ await items[2].trigger('click');
+ expect(items[2].element.classList.contains(activeClass)).toBeTruthy();
+
+ await items[3].trigger('click');
+ expect(items[2].element.classList.contains(activeClass)).toBeTruthy();
+});
+
+test('should match active tab by route name in route mode', async () => {
+ const wrapper = mount(
+ {
+ render: () => (
+
+ Tab
+ Tab
+
+ ),
+ },
+ {
+ global: {
+ mocks: getMockRouter(),
+ },
+ }
+ );
+
+ const items = wrapper.findAll('.van-tabbar-item');
+
+ await items[0].trigger('click');
+ expect(items[0].element.classList.contains(activeClass)).toBeTruthy();
+
+ await items[1].trigger('click');
+ expect(items[1].element.classList.contains(activeClass)).toBeTruthy();
+});
+
+test('should watch model-value and update active tab', async () => {
+ const wrapper = mount({
+ setup() {
+ const active = ref(0);
+ const updateActive = () => {
+ active.value = 1;
+ };
+ return {
+ active,
+ updateActive,
+ };
+ },
+ render() {
+ return (
+
+ Tab
+ Tab
+
+ );
+ },
+ });
+
+ wrapper.vm.updateActive();
+ await nextTick();
+ const items = wrapper.findAll('.van-tabbar-item');
+ expect(items[1].element.classList.contains(activeClass)).toBeTruthy();
+});
+
+test('should match active tab by name when using name prop', () => {
+ const onChange = jest.fn();
+ const wrapper = mount({
+ setup() {
+ const active = ref('a');
+ return {
+ active,
+ };
+ },
+ render() {
+ return (
+
+ Tab
+ Tab
+
+ );
+ },
+ });
+
+ wrapper.findAll('.van-tabbar-item')[1].trigger('click');
+ expect(onChange).toHaveBeenCalledWith('b');
+});
+
+test('should not render border when border prop is false', () => {
+ const wrapper = mount(Tabbar, {
+ props: {
+ border: false,
+ },
+ });
+
+ expect(
+ wrapper.element.classList.contains('van-hairline--top-bottom')
+ ).toBeFalsy();
+});
+
+test('should render placeholder element when using placeholder prop', async () => {
+ const restore = mockGetBoundingClientRect({ height: 50 });
+ const wrapper = mount(Tabbar, {
+ props: {
+ fixed: true,
+ placeholder: true,
+ },
+ });
+
+ await later();
+ expect(wrapper.html()).toMatchSnapshot();
+ restore();
+});