|
1 | 1 | import { mount } from '@vue/test-utils' |
2 | 2 | import { expect, test, vi } from 'vite-plus/test' |
3 | 3 | import { createApp } from 'vue' |
4 | | -import { z } from 'zod' |
5 | 4 | import Counter from '..' |
6 | 5 | import { delay, trigger } from '../../utils/test' |
7 | 6 | import VarCounter from '../Counter' |
@@ -439,31 +438,359 @@ test('counter onIncrement & onDecrement', async () => { |
439 | 438 |
|
440 | 439 | wrapper.unmount() |
441 | 440 | }) |
442 | | -test('counter validation with zod', async () => { |
| 441 | + |
| 442 | +test('counter validate on increment trigger', async () => { |
| 443 | + const wrapper = mount({ |
| 444 | + ...Wrapper, |
| 445 | + template: `<var-counter ref="counter" :rules="[v => v < 1 || 'Too big']" v-model="value" />`, |
| 446 | + }) |
| 447 | + |
| 448 | + await wrapper.find('.var-counter__increment-button').trigger('click') |
| 449 | + await delay(16) |
| 450 | + |
| 451 | + expect(wrapper.find('.var-form-details__error-message').text()).toBe('Too big') |
| 452 | + |
| 453 | + wrapper.unmount() |
| 454 | +}) |
| 455 | + |
| 456 | +test('counter validate on decrement trigger', async () => { |
| 457 | + const wrapper = mount({ |
| 458 | + ...Wrapper, |
| 459 | + template: `<var-counter ref="counter" :rules="[v => v >= 0 || 'Too small']" v-model="value" />`, |
| 460 | + }) |
| 461 | + |
| 462 | + await wrapper.find('.var-counter__decrement-button').trigger('click') |
| 463 | + await delay(16) |
| 464 | + |
| 465 | + expect(wrapper.find('.var-form-details__error-message').text()).toBe('Too small') |
| 466 | + |
| 467 | + wrapper.unmount() |
| 468 | +}) |
| 469 | + |
| 470 | +test('counter press increment release stops timers', async () => { |
| 471 | + vi.useFakeTimers() |
| 472 | + const wrapper = mount(Wrapper) |
| 473 | + |
| 474 | + await trigger(wrapper.find('.var-counter__increment-button'), 'touchstart') |
| 475 | + vi.advanceTimersByTime(700) |
| 476 | + |
| 477 | + const current = wrapper.vm.value |
| 478 | + expect(current).toBeGreaterThan(0) |
| 479 | + |
| 480 | + await trigger(wrapper.find('.var-counter__increment-button'), 'touchend') |
| 481 | + vi.advanceTimersByTime(300) |
| 482 | + |
| 483 | + expect(wrapper.vm.value).toBe(current) |
| 484 | + |
| 485 | + wrapper.unmount() |
| 486 | + vi.useRealTimers() |
| 487 | +}) |
| 488 | + |
| 489 | +test('counter validate on increment and decrement methods', async () => { |
| 490 | + const wrapper = mount({ |
| 491 | + ...Wrapper, |
| 492 | + template: `<var-counter :rules="[v => v === 0 || 'Invalid']" v-model="value" />`, |
| 493 | + }) |
| 494 | + |
| 495 | + const counter = wrapper.findComponent(VarCounter) |
| 496 | + |
| 497 | + counter.vm.increment() |
| 498 | + await delay(16) |
| 499 | + expect(wrapper.find('.var-form-details__error-message').text()).toBe('Invalid') |
| 500 | + |
| 501 | + counter.vm.decrement() |
| 502 | + await delay(16) |
| 503 | + expect(wrapper.find('.var-form-details__error-message').exists()).toBe(false) |
| 504 | + |
| 505 | + wrapper.unmount() |
| 506 | +}) |
| 507 | + |
| 508 | +test('counter press increment and release methods', () => { |
| 509 | + vi.useFakeTimers() |
| 510 | + const wrapper = mount(Wrapper) |
| 511 | + const counter = wrapper.findComponent(VarCounter) |
| 512 | + |
| 513 | + counter.vm.pressIncrement() |
| 514 | + vi.advanceTimersByTime(700) |
| 515 | + |
| 516 | + const current = wrapper.vm.value |
| 517 | + counter.vm.releaseIncrement() |
| 518 | + vi.advanceTimersByTime(300) |
| 519 | + |
| 520 | + expect(wrapper.vm.value).toBe(current) |
| 521 | + |
| 522 | + wrapper.unmount() |
| 523 | + vi.useRealTimers() |
| 524 | +}) |
| 525 | + |
| 526 | +test('counter press decrement and release methods', () => { |
| 527 | + vi.useFakeTimers() |
| 528 | + const wrapper = mount(Wrapper) |
| 529 | + const counter = wrapper.findComponent(VarCounter) |
| 530 | + |
| 531 | + counter.vm.pressDecrement() |
| 532 | + vi.advanceTimersByTime(700) |
| 533 | + |
| 534 | + const current = wrapper.vm.value |
| 535 | + counter.vm.releaseDecrement() |
| 536 | + vi.advanceTimersByTime(300) |
| 537 | + |
| 538 | + expect(wrapper.vm.value).toBe(current) |
| 539 | + |
| 540 | + wrapper.unmount() |
| 541 | + vi.useRealTimers() |
| 542 | +}) |
| 543 | + |
| 544 | +test('counter touchcancel on increment', async () => { |
| 545 | + const wrapper = mount(Wrapper) |
| 546 | + |
| 547 | + await trigger(wrapper.find('.var-counter__increment-button'), 'touchstart') |
| 548 | + await delay(100) |
| 549 | + |
| 550 | + await trigger(wrapper.find('.var-counter__increment-button'), 'touchcancel') |
| 551 | + await delay(100) |
| 552 | + |
| 553 | + expect(wrapper.vm.value).toBe(0) |
| 554 | + |
| 555 | + wrapper.unmount() |
| 556 | +}) |
| 557 | + |
| 558 | +test('counter touchcancel on decrement', async () => { |
| 559 | + const wrapper = mount(Wrapper) |
| 560 | + |
| 561 | + await trigger(wrapper.find('.var-counter__decrement-button'), 'touchstart') |
| 562 | + await delay(100) |
| 563 | + |
| 564 | + await trigger(wrapper.find('.var-counter__decrement-button'), 'touchcancel') |
| 565 | + await delay(100) |
| 566 | + |
| 567 | + expect(wrapper.vm.value).toBe(0) |
| 568 | + |
| 569 | + wrapper.unmount() |
| 570 | +}) |
| 571 | + |
| 572 | +test('counter lazy change with decrement validation', async () => { |
| 573 | + const onBeforeChange = vi.fn((value, change) => change(value)) |
| 574 | + |
| 575 | + const wrapper = mount({ |
| 576 | + ...Wrapper, |
| 577 | + methods: { |
| 578 | + onBeforeChange, |
| 579 | + }, |
| 580 | + template: `<var-counter lazy-change :rules="[v => v >= 0 || 'Invalid']" v-model="value" @before-change="onBeforeChange" />`, |
| 581 | + }) |
| 582 | + |
| 583 | + await wrapper.find('.var-counter__decrement-button').trigger('click') |
| 584 | + await delay(16) |
| 585 | + |
| 586 | + expect(onBeforeChange).toHaveBeenCalled() |
| 587 | + expect(wrapper.find('.var-form-details__error-message').text()).toBe('Invalid') |
| 588 | + |
| 589 | + wrapper.unmount() |
| 590 | +}) |
| 591 | + |
| 592 | +test('counter lazy change with increment validation', async () => { |
| 593 | + const onBeforeChange = vi.fn((value, change) => change(value)) |
| 594 | + |
| 595 | + const wrapper = mount({ |
| 596 | + ...Wrapper, |
| 597 | + methods: { |
| 598 | + onBeforeChange, |
| 599 | + }, |
| 600 | + template: `<var-counter lazy-change :rules="[v => v <= 0 || 'Invalid']" v-model="value" @before-change="onBeforeChange" />`, |
| 601 | + }) |
| 602 | + |
| 603 | + await wrapper.find('.var-counter__increment-button').trigger('click') |
| 604 | + await delay(16) |
| 605 | + |
| 606 | + expect(onBeforeChange).toHaveBeenCalled() |
| 607 | + expect(wrapper.find('.var-form-details__error-message').text()).toBe('Invalid') |
| 608 | + |
| 609 | + wrapper.unmount() |
| 610 | +}) |
| 611 | + |
| 612 | +test('counter press increment long enough to set incrementTimer then release', async () => { |
443 | 613 | const wrapper = mount({ |
444 | 614 | ...Wrapper, |
445 | 615 | data: () => ({ |
446 | 616 | value: 0, |
447 | | - rules: z.number().min(1, 'The value must be more than zero'), |
448 | 617 | }), |
449 | | - template: `<var-counter ref="counter" :rules="[v => v > 0 || 'The value must be more than zero']" v-model="value" />`, |
| 618 | + template: `<var-counter press v-model="value" />`, |
450 | 619 | }) |
451 | 620 |
|
452 | | - const { counter } = wrapper.vm.$refs |
| 621 | + // Press and hold long enough for continuedIncrement to be called |
| 622 | + // DELAY is 600ms, SPEED is 100ms, so after 800ms incrementTimer should be set |
| 623 | + await trigger(wrapper.find('.var-counter__increment-button'), 'touchstart') |
| 624 | + await delay(800) |
453 | 625 |
|
454 | | - counter.validate() |
| 626 | + const current = wrapper.vm.value |
| 627 | + expect(current).toBeGreaterThan(0) |
| 628 | + |
| 629 | + // Release should clear both incrementTimer and incrementDelayTimer (line 319-320) |
| 630 | + await trigger(wrapper.find('.var-counter__increment-button'), 'touchend') |
| 631 | + await delay(200) |
| 632 | + |
| 633 | + // Value should not change after release |
| 634 | + expect(wrapper.vm.value).toBe(current) |
| 635 | + |
| 636 | + wrapper.unmount() |
| 637 | +}) |
| 638 | + |
| 639 | +test('counter press decrement long enough to set decrementTimer then release', async () => { |
| 640 | + const wrapper = mount({ |
| 641 | + ...Wrapper, |
| 642 | + data: () => ({ |
| 643 | + value: 0, |
| 644 | + }), |
| 645 | + template: `<var-counter press v-model="value" />`, |
| 646 | + }) |
| 647 | + |
| 648 | + // Press and hold long enough for continuedDecrement to be called |
| 649 | + await trigger(wrapper.find('.var-counter__decrement-button'), 'touchstart') |
| 650 | + await delay(800) |
| 651 | + |
| 652 | + const current = wrapper.vm.value |
| 653 | + expect(current).toBeLessThan(0) |
| 654 | + |
| 655 | + // Release should clear both decrementTimer and decrementDelayTimer (line 314-315) |
| 656 | + await trigger(wrapper.find('.var-counter__decrement-button'), 'touchend') |
| 657 | + await delay(200) |
| 658 | + |
| 659 | + // Value should not change after release |
| 660 | + expect(wrapper.vm.value).toBe(current) |
| 661 | + |
| 662 | + wrapper.unmount() |
| 663 | +}) |
| 664 | + |
| 665 | +test('counter reset with min resets to min', async () => { |
| 666 | + // reset() takes the `min != null ? toNumber(min) : 0` true branch at line 184 |
| 667 | + const wrapper = mount({ |
| 668 | + ...Wrapper, |
| 669 | + data: () => ({ |
| 670 | + value: 5, |
| 671 | + }), |
| 672 | + template: `<var-counter ref="counter" :min="2" v-model="value" />`, |
| 673 | + }) |
| 674 | + |
| 675 | + const { counter } = wrapper.vm.$refs |
| 676 | + counter.reset() |
455 | 677 | await delay(16) |
| 678 | + expect(wrapper.vm.value).toBe(2) |
456 | 679 |
|
457 | | - expect(wrapper.html()).toMatchSnapshot() |
458 | | - expect(wrapper.find('.var-form-details__error-message').text()).toBe('The value must be more than zero') |
| 680 | + wrapper.unmount() |
| 681 | +}) |
| 682 | + |
| 683 | +test('counter lazy change via input typing', async () => { |
| 684 | + // handleChange takes the lazyChange branch of the ternary at line 214 |
| 685 | + const onBeforeChange = vi.fn((value, change) => change(value)) |
| 686 | + |
| 687 | + const wrapper = mount({ |
| 688 | + ...Wrapper, |
| 689 | + data: () => ({ |
| 690 | + value: 0, |
| 691 | + }), |
| 692 | + methods: { |
| 693 | + onBeforeChange, |
| 694 | + }, |
| 695 | + template: `<var-counter lazy-change v-model="value" @before-change="onBeforeChange" />`, |
| 696 | + }) |
| 697 | + |
| 698 | + wrapper.find('.var-counter__input').setValue('5') |
| 699 | + await wrapper.find('.var-counter__input').trigger('change') |
| 700 | + |
| 701 | + expect(onBeforeChange).toHaveBeenCalledWith(5, expect.any(Function)) |
| 702 | + expect(wrapper.vm.value).toBe(5) |
| 703 | + |
| 704 | + wrapper.unmount() |
| 705 | +}) |
| 706 | + |
| 707 | +test('counter increment at max returns early', async () => { |
| 708 | + // increment() hits the `if (isMax.value) return` guard at line 272 |
| 709 | + const wrapper = mount({ |
| 710 | + ...Wrapper, |
| 711 | + data: () => ({ |
| 712 | + value: 10, |
| 713 | + }), |
| 714 | + template: `<var-counter :max="10" v-model="value" />`, |
| 715 | + }) |
459 | 716 |
|
| 717 | + await wrapper.find('.var-counter__increment-button').trigger('click') |
| 718 | + expect(wrapper.vm.value).toBe(10) |
| 719 | + |
| 720 | + wrapper.unmount() |
| 721 | +}) |
| 722 | + |
| 723 | +test('counter decrement at min returns early', async () => { |
| 724 | + // decrement() hits the `if (isMin.value) return` guard at line 237 |
| 725 | + const wrapper = mount({ |
| 726 | + ...Wrapper, |
| 727 | + data: () => ({ |
| 728 | + value: 0, |
| 729 | + }), |
| 730 | + template: `<var-counter :min="0" v-model="value" />`, |
| 731 | + }) |
| 732 | + |
| 733 | + await wrapper.find('.var-counter__decrement-button').trigger('click') |
| 734 | + expect(wrapper.vm.value).toBe(0) |
| 735 | + |
| 736 | + wrapper.unmount() |
| 737 | +}) |
| 738 | + |
| 739 | +test('counter press disabled does not increment or decrement', async () => { |
| 740 | + // pressIncrement (line 305) and pressDecrement (line 293) return early when press is false |
| 741 | + const wrapper = mount({ |
| 742 | + ...Wrapper, |
| 743 | + data: () => ({ |
| 744 | + value: 0, |
| 745 | + }), |
| 746 | + template: `<var-counter :press="false" v-model="value" />`, |
| 747 | + }) |
| 748 | + |
| 749 | + await trigger(wrapper.find('.var-counter__increment-button'), 'touchstart') |
| 750 | + await delay(800) |
| 751 | + expect(wrapper.vm.value).toBe(0) |
| 752 | + |
| 753 | + await trigger(wrapper.find('.var-counter__decrement-button'), 'touchstart') |
| 754 | + await delay(800) |
| 755 | + expect(wrapper.vm.value).toBe(0) |
| 756 | + |
| 757 | + wrapper.unmount() |
| 758 | +}) |
| 759 | + |
| 760 | +test('counter increment without lazyChange triggers validation', async () => { |
| 761 | + const wrapper = mount({ |
| 762 | + ...Wrapper, |
| 763 | + data: () => ({ |
| 764 | + value: 0, |
| 765 | + }), |
| 766 | + template: `<var-counter :rules="[v => v === 0 || 'error']" :lazy-change="false" v-model="value" />`, |
| 767 | + }) |
| 768 | + |
| 769 | + // Increment should trigger validateWithTrigger at line 285 |
460 | 770 | await wrapper.find('.var-counter__increment-button').trigger('click') |
461 | 771 | await delay(16) |
462 | | - expect(wrapper.find('.var-form-details__error-message').exists()).toBeFalsy() |
463 | 772 |
|
464 | | - counter.reset() |
| 773 | + // Validation should have been triggered |
| 774 | + expect(wrapper.find('.var-form-details__error-message').text()).toBe('error') |
| 775 | + |
| 776 | + wrapper.unmount() |
| 777 | +}) |
| 778 | + |
| 779 | +test('counter decrement without lazyChange triggers validation', async () => { |
| 780 | + const wrapper = mount({ |
| 781 | + ...Wrapper, |
| 782 | + data: () => ({ |
| 783 | + value: 0, |
| 784 | + }), |
| 785 | + template: `<var-counter :rules="[v => v === 0 || 'error']" :lazy-change="false" v-model="value" />`, |
| 786 | + }) |
| 787 | + |
| 788 | + // Decrement should trigger validateWithTrigger at line 250 |
| 789 | + await wrapper.find('.var-counter__decrement-button').trigger('click') |
465 | 790 | await delay(16) |
466 | | - expect(wrapper.vm.value).toBe(0) |
| 791 | + |
| 792 | + // Validation should have been triggered |
| 793 | + expect(wrapper.find('.var-form-details__error-message').text()).toBe('error') |
467 | 794 |
|
468 | 795 | wrapper.unmount() |
469 | 796 | }) |
0 commit comments