@bedard/hexboard 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/index.js +387 -388
- package/package.json +1 -1
- package/src/lib/components/hexboard/Hexboard.vue +118 -97
- package/src/tests/hexboard.test.tsx +78 -39
- package/src/tests/utils.ts +28 -2
- package/vite.config.ts +18 -1
package/package.json
CHANGED
|
@@ -172,6 +172,7 @@ import {
|
|
|
172
172
|
type Component,
|
|
173
173
|
computed,
|
|
174
174
|
h,
|
|
175
|
+
onBeforeMount,
|
|
175
176
|
onMounted,
|
|
176
177
|
onUnmounted,
|
|
177
178
|
shallowRef,
|
|
@@ -462,6 +463,12 @@ const promotionPieces = computed(() => {
|
|
|
462
463
|
// lifecycle
|
|
463
464
|
//
|
|
464
465
|
|
|
466
|
+
onBeforeMount(() => {
|
|
467
|
+
if (props.autoselect) {
|
|
468
|
+
selectCurrentTargets()
|
|
469
|
+
}
|
|
470
|
+
})
|
|
471
|
+
|
|
465
472
|
onMounted(() => {
|
|
466
473
|
if (props.active) {
|
|
467
474
|
listen()
|
|
@@ -486,6 +493,8 @@ watch(
|
|
|
486
493
|
val => (val ? listen() : unlisten()),
|
|
487
494
|
)
|
|
488
495
|
|
|
496
|
+
watch(selected, selectCurrentTargets)
|
|
497
|
+
|
|
489
498
|
//
|
|
490
499
|
// methods
|
|
491
500
|
//
|
|
@@ -537,17 +546,25 @@ function attemptMove(san: San, evt?: MouseEvent) {
|
|
|
537
546
|
}
|
|
538
547
|
}
|
|
539
548
|
|
|
540
|
-
/**
|
|
541
|
-
function
|
|
542
|
-
const
|
|
549
|
+
/** cancel promotion and restore original selection */
|
|
550
|
+
function cancelPromotion() {
|
|
551
|
+
const from = staging.value.promotionFrom
|
|
543
552
|
|
|
544
|
-
|
|
545
|
-
|
|
553
|
+
staging.value = {
|
|
554
|
+
hexchess: null,
|
|
555
|
+
promotionEl: null,
|
|
556
|
+
promotionFrom: null,
|
|
557
|
+
promotionTo: null,
|
|
558
|
+
selected: null,
|
|
546
559
|
}
|
|
547
560
|
|
|
548
|
-
|
|
561
|
+
// Keep the original piece selected
|
|
562
|
+
if (typeof from === 'number') {
|
|
563
|
+
selected.value = from
|
|
564
|
+
}
|
|
549
565
|
|
|
550
|
-
|
|
566
|
+
pointerdownPosition.value = null
|
|
567
|
+
skipNextClick = true
|
|
551
568
|
}
|
|
552
569
|
|
|
553
570
|
/** get fill color of label */
|
|
@@ -566,6 +583,19 @@ function getLabelFill(text: string) {
|
|
|
566
583
|
return normalizedOptions.value.labelInactiveColor
|
|
567
584
|
}
|
|
568
585
|
|
|
586
|
+
/** check if user is playing the color at a position */
|
|
587
|
+
function isPlayingPosition(index: number): boolean {
|
|
588
|
+
const piece = props.hexchess?.board[index]
|
|
589
|
+
|
|
590
|
+
if (!piece) {
|
|
591
|
+
return false
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w'
|
|
595
|
+
|
|
596
|
+
return props.playing === true || props.playing === pieceColor
|
|
597
|
+
}
|
|
598
|
+
|
|
569
599
|
/** listen for events */
|
|
570
600
|
function listen() {
|
|
571
601
|
pointerCoords.value = { x: 0, y: 0 }
|
|
@@ -614,7 +644,6 @@ function onClickPosition(index: number, evt: MouseEvent) {
|
|
|
614
644
|
// If autoselect is enabled and clicking an unoccupied position, deselect
|
|
615
645
|
if (props.autoselect && !props.hexchess.board[index]) {
|
|
616
646
|
selected.value = null
|
|
617
|
-
targets.value = []
|
|
618
647
|
}
|
|
619
648
|
|
|
620
649
|
emit('clickPosition', index)
|
|
@@ -632,11 +661,20 @@ function onKeyupWindow(evt: KeyboardEvent) {
|
|
|
632
661
|
// Otherwise deselect if autoselect is enabled
|
|
633
662
|
if (props.autoselect) {
|
|
634
663
|
selected.value = null
|
|
635
|
-
targets.value = []
|
|
636
664
|
}
|
|
637
665
|
}
|
|
638
666
|
}
|
|
639
667
|
|
|
668
|
+
/** mouseenter position */
|
|
669
|
+
function onMouseenter(index: number) {
|
|
670
|
+
mouseoverPosition.value = index
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/** mouseleave position */
|
|
674
|
+
function onMouseleave() {
|
|
675
|
+
mouseoverPosition.value = null
|
|
676
|
+
}
|
|
677
|
+
|
|
640
678
|
/** handle piece move */
|
|
641
679
|
function onPieceMove(san: San) {
|
|
642
680
|
emit('move', san)
|
|
@@ -644,6 +682,60 @@ function onPieceMove(san: San) {
|
|
|
644
682
|
resetState()
|
|
645
683
|
}
|
|
646
684
|
|
|
685
|
+
/** pointerdown on position */
|
|
686
|
+
function onPointerdownPosition(index: number, evt: PointerEvent) {
|
|
687
|
+
evt.preventDefault()
|
|
688
|
+
|
|
689
|
+
// Don't start new interactions during promotion
|
|
690
|
+
if (staging.value.hexchess) {
|
|
691
|
+
return
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// If clicking on a valid target for the selected piece, don't re-select
|
|
695
|
+
// (the move will be handled in onPointerupPosition/onClickPosition)
|
|
696
|
+
if (selected.value !== null && targets.value.includes(index)) {
|
|
697
|
+
return
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const piece = props.hexchess?.board[index]
|
|
701
|
+
|
|
702
|
+
if (!piece) {
|
|
703
|
+
return
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (props.autoselect) {
|
|
707
|
+
selected.value = index
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (!isPlayingPosition(index)) {
|
|
711
|
+
return
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Only allow dragging if it's the piece's turn (or ignoreTurn is true)
|
|
715
|
+
const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w'
|
|
716
|
+
const isCurrentTurn = props.hexchess?.turn === pieceColor
|
|
717
|
+
|
|
718
|
+
if (!props.ignoreTurn && !isCurrentTurn) {
|
|
719
|
+
return
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
pointerdownPosition.value = index
|
|
723
|
+
pointerCoords.value = { x: evt.clientX, y: evt.clientY }
|
|
724
|
+
|
|
725
|
+
if (svgEl.value instanceof Element) {
|
|
726
|
+
svgRect.value = svgEl.value.getBoundingClientRect()
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/** pointermove window */
|
|
731
|
+
function onPointermoveWindow(evt: MouseEvent) {
|
|
732
|
+
if (!props.active) {
|
|
733
|
+
return
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
pointerCoords.value = { x: evt.clientX, y: evt.clientY }
|
|
737
|
+
}
|
|
738
|
+
|
|
647
739
|
/** pointerup position */
|
|
648
740
|
function onPointerupPosition(index: number, evt: PointerEvent) {
|
|
649
741
|
evt.stopPropagation()
|
|
@@ -707,94 +799,6 @@ function onPointerupPosition(index: number, evt: PointerEvent) {
|
|
|
707
799
|
resetState()
|
|
708
800
|
}
|
|
709
801
|
|
|
710
|
-
/** cancel promotion and restore original selection */
|
|
711
|
-
function cancelPromotion() {
|
|
712
|
-
const from = staging.value.promotionFrom
|
|
713
|
-
|
|
714
|
-
staging.value = {
|
|
715
|
-
hexchess: null,
|
|
716
|
-
promotionEl: null,
|
|
717
|
-
promotionFrom: null,
|
|
718
|
-
promotionTo: null,
|
|
719
|
-
selected: null,
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// Keep the original piece selected
|
|
723
|
-
if (typeof from === 'number') {
|
|
724
|
-
selected.value = from
|
|
725
|
-
targets.value = props.hexchess.movesFrom(from).map(san => san.to) ?? []
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
pointerdownPosition.value = null
|
|
729
|
-
skipNextClick = true
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
/** pointerdown on position */
|
|
733
|
-
function onPointerdownPosition(index: number, evt: PointerEvent) {
|
|
734
|
-
evt.preventDefault()
|
|
735
|
-
|
|
736
|
-
// Don't start new interactions during promotion
|
|
737
|
-
if (staging.value.hexchess) {
|
|
738
|
-
return
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
const piece = props.hexchess?.board[index]
|
|
742
|
-
|
|
743
|
-
if (!piece) {
|
|
744
|
-
return
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
if (props.autoselect) {
|
|
748
|
-
selected.value = index
|
|
749
|
-
targets.value = props.hexchess?.movesFrom(index).map(san => san.to) ?? []
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
if (!isPlayingPosition(index)) {
|
|
753
|
-
return
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// Only allow dragging if it's the piece's turn (or ignoreTurn is true)
|
|
757
|
-
const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w'
|
|
758
|
-
const isCurrentTurn = props.hexchess?.turn === pieceColor
|
|
759
|
-
|
|
760
|
-
if (!props.ignoreTurn && !isCurrentTurn) {
|
|
761
|
-
return
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
pointerdownPosition.value = index
|
|
765
|
-
pointerCoords.value = { x: evt.clientX, y: evt.clientY }
|
|
766
|
-
|
|
767
|
-
if (svgEl.value instanceof Element) {
|
|
768
|
-
svgRect.value = svgEl.value.getBoundingClientRect()
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/** mouseenter position */
|
|
773
|
-
function onMouseenter(index: number) {
|
|
774
|
-
mouseoverPosition.value = index
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/** mouseleave position */
|
|
778
|
-
function onMouseleave() {
|
|
779
|
-
mouseoverPosition.value = null
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
/** pointermove window */
|
|
783
|
-
function onPointermoveWindow(evt: MouseEvent) {
|
|
784
|
-
if (!props.active) {
|
|
785
|
-
return
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
pointerCoords.value = { x: evt.clientX, y: evt.clientY }
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/** touchmove window - prevent scrolling while dragging */
|
|
792
|
-
function onTouchmoveWindow(evt: TouchEvent) {
|
|
793
|
-
if (pointerdownPosition.value !== null) {
|
|
794
|
-
evt.preventDefault()
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
802
|
/** pointerup window */
|
|
799
803
|
function onPointerupWindow() {
|
|
800
804
|
// If staging a promotion, cancel it but keep the original piece selected
|
|
@@ -813,6 +817,13 @@ function onPointerupWindow() {
|
|
|
813
817
|
resetState()
|
|
814
818
|
}
|
|
815
819
|
|
|
820
|
+
/** touchmove window - prevent scrolling while dragging */
|
|
821
|
+
function onTouchmoveWindow(evt: TouchEvent) {
|
|
822
|
+
if (pointerdownPosition.value !== null) {
|
|
823
|
+
evt.preventDefault()
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
816
827
|
/** promote piece */
|
|
817
828
|
function promote(promotion: 'n' | 'b' | 'r' | 'q') {
|
|
818
829
|
if (
|
|
@@ -859,6 +870,16 @@ function resetState() {
|
|
|
859
870
|
targets.value = []
|
|
860
871
|
}
|
|
861
872
|
|
|
873
|
+
/** select current targets */
|
|
874
|
+
function selectCurrentTargets() {
|
|
875
|
+
if (typeof selected.value === 'number') {
|
|
876
|
+
targets.value = props.hexchess?.movesFrom(selected.value).map(san => san.to) ?? []
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
targets.value = []
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
862
883
|
/** stop listening for events */
|
|
863
884
|
function unlisten() {
|
|
864
885
|
resetState()
|
|
@@ -3,7 +3,7 @@ import { expect, test, vi } from 'vitest'
|
|
|
3
3
|
import { Hexboard } from '../lib'
|
|
4
4
|
import { Hexchess } from '@bedard/hexchess'
|
|
5
5
|
import { index, San } from '@bedard/hexchess'
|
|
6
|
-
import {
|
|
6
|
+
import { dragMove, clickMove, setup } from './utils'
|
|
7
7
|
import { page } from 'vitest/browser'
|
|
8
8
|
import { ref, nextTick } from 'vue'
|
|
9
9
|
import { userEvent } from 'vitest/browser'
|
|
@@ -520,23 +520,7 @@ test('drag and drop piece emits move event', async () => {
|
|
|
520
520
|
)
|
|
521
521
|
})
|
|
522
522
|
|
|
523
|
-
|
|
524
|
-
const toPosition = page.getByTestId('position-f6')
|
|
525
|
-
|
|
526
|
-
// Start dragging the piece (pointerdown)
|
|
527
|
-
await fromPosition
|
|
528
|
-
.element()
|
|
529
|
-
.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
530
|
-
await nextTick()
|
|
531
|
-
|
|
532
|
-
// Verify dragging started
|
|
533
|
-
await expect.element(page.getByTestId('drag-piece')).toBeVisible()
|
|
534
|
-
|
|
535
|
-
// Move pointer to target position and release (pointerup)
|
|
536
|
-
await toPosition
|
|
537
|
-
.element()
|
|
538
|
-
.dispatchEvent(new PointerEvent('pointerup', { bubbles: true }))
|
|
539
|
-
await nextTick()
|
|
523
|
+
await dragMove(page, 'f5f6')
|
|
540
524
|
|
|
541
525
|
// Verify move event was emitted with correct San object
|
|
542
526
|
await expect(onMove).toHaveBeenCalledOnce()
|
|
@@ -644,7 +628,7 @@ test('promotion', async () => {
|
|
|
644
628
|
)
|
|
645
629
|
})
|
|
646
630
|
|
|
647
|
-
await
|
|
631
|
+
await clickMove(page, 'f10f11')
|
|
648
632
|
await page.getByTestId('promote').click()
|
|
649
633
|
await expect(page.getByTestId('piece-f11')).toHaveAttribute(
|
|
650
634
|
'data-piece-type',
|
|
@@ -682,7 +666,7 @@ test('canceled promotion', async () => {
|
|
|
682
666
|
})
|
|
683
667
|
|
|
684
668
|
// Move pawn to promotion square
|
|
685
|
-
await
|
|
669
|
+
await clickMove(page, 'f10f11')
|
|
686
670
|
|
|
687
671
|
// Promotion UI should be visible
|
|
688
672
|
await expect.element(page.getByTestId('cancel')).toBeVisible()
|
|
@@ -730,7 +714,7 @@ test('clicking position during promotion cancels it', async () => {
|
|
|
730
714
|
})
|
|
731
715
|
|
|
732
716
|
// Move pawn to promotion square
|
|
733
|
-
await
|
|
717
|
+
await clickMove(page, 'f10f11')
|
|
734
718
|
|
|
735
719
|
// Promotion UI should be visible
|
|
736
720
|
await expect.element(page.getByTestId('promote')).toBeVisible()
|
|
@@ -771,14 +755,14 @@ test('ignoreTurn allows moving pieces out of turn', async () => {
|
|
|
771
755
|
})
|
|
772
756
|
|
|
773
757
|
// Initial position is white's turn, try to move black's pawn without ignoreTurn
|
|
774
|
-
await
|
|
758
|
+
await clickMove(page, 'f7f6')
|
|
775
759
|
await expect(onMove).not.toHaveBeenCalled()
|
|
776
760
|
|
|
777
761
|
// Enable ignoreTurn and try again
|
|
778
762
|
ignoreTurn.value = true
|
|
779
763
|
await nextTick()
|
|
780
764
|
|
|
781
|
-
await
|
|
765
|
+
await clickMove(page, 'f7f6')
|
|
782
766
|
await expect(onMove).toHaveBeenCalledOnce()
|
|
783
767
|
await expect(onMove).toHaveBeenCalledWith(
|
|
784
768
|
expect.objectContaining({
|
|
@@ -791,6 +775,7 @@ test('ignoreTurn allows moving pieces out of turn', async () => {
|
|
|
791
775
|
test('dragging piece to non-target position keeps selection', async () => {
|
|
792
776
|
const selected = ref<number | null>(null)
|
|
793
777
|
const targets = ref<number[]>([])
|
|
778
|
+
const onMove = vi.fn()
|
|
794
779
|
|
|
795
780
|
setup(() => {
|
|
796
781
|
return () => (
|
|
@@ -801,32 +786,86 @@ test('dragging piece to non-target position keeps selection', async () => {
|
|
|
801
786
|
playing="w"
|
|
802
787
|
v-model:selected={selected.value}
|
|
803
788
|
v-model:targets={targets.value}
|
|
789
|
+
onMove={onMove}
|
|
804
790
|
/>
|
|
805
791
|
<div data-testid="selected-value" v-text={selected.value} />
|
|
806
792
|
</>
|
|
807
793
|
)
|
|
808
794
|
})
|
|
809
795
|
|
|
810
|
-
|
|
811
|
-
const nonTargetPosition = page.getByTestId('position-a1')
|
|
796
|
+
await dragMove(page, 'f5a1')
|
|
812
797
|
|
|
813
|
-
//
|
|
814
|
-
await piecePosition
|
|
815
|
-
.element()
|
|
816
|
-
.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
817
|
-
await nextTick()
|
|
818
|
-
|
|
819
|
-
// Verify piece is selected
|
|
798
|
+
// Piece should still be selected
|
|
820
799
|
await expect.element(page.getByTestId('selected-f5')).toBeVisible()
|
|
821
800
|
await expect(selected.value).toBe(index('f5'))
|
|
822
801
|
|
|
823
|
-
//
|
|
824
|
-
await
|
|
825
|
-
|
|
826
|
-
|
|
802
|
+
// No move should have been emitted
|
|
803
|
+
await expect(onMove).not.toHaveBeenCalled()
|
|
804
|
+
})
|
|
805
|
+
|
|
806
|
+
test('click capture', async () => {
|
|
807
|
+
const hexchess = ref(
|
|
808
|
+
Hexchess.parse('b/qbk/n1b1n/r5r/ppp1ppppp/11/4pP5/4P1P4/3P1B1P3/2P2B2P2/1PRNQBKNRP1 w e6 0 2'),
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
setup(() => {
|
|
812
|
+
return () => (
|
|
813
|
+
<Hexboard
|
|
814
|
+
active
|
|
815
|
+
autoselect
|
|
816
|
+
playing={true}
|
|
817
|
+
hexchess={hexchess.value}
|
|
818
|
+
onMove={san => hexchess.value.applyMoveUnsafe(san)}
|
|
819
|
+
/>
|
|
820
|
+
)
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
await clickMove(page, 'f5e5')
|
|
824
|
+
|
|
825
|
+
await expect(hexchess.value.toString()).toBe('b/qbk/n1b1n/r5r/ppp1ppppp/11/4P6/4P1P4/3P1B1P3/2P2B2P2/1PRNQBKNRP1 b - 0 2')
|
|
826
|
+
})
|
|
827
|
+
|
|
828
|
+
test('drag capture', async () => {
|
|
829
|
+
const hexchess = ref(
|
|
830
|
+
Hexchess.parse('b/qbk/n1b1n/r5r/ppp1ppppp/11/4pP5/4P1P4/3P1B1P3/2P2B2P2/1PRNQBKNRP1 w e6 0 2'),
|
|
831
|
+
)
|
|
832
|
+
|
|
833
|
+
setup(() => {
|
|
834
|
+
return () => (
|
|
835
|
+
<Hexboard
|
|
836
|
+
active
|
|
837
|
+
autoselect
|
|
838
|
+
playing={true}
|
|
839
|
+
hexchess={hexchess.value}
|
|
840
|
+
onMove={san => hexchess.value.applyMoveUnsafe(san)}
|
|
841
|
+
/>
|
|
842
|
+
)
|
|
843
|
+
})
|
|
844
|
+
|
|
845
|
+
await dragMove(page, 'f5e5')
|
|
846
|
+
|
|
847
|
+
await expect(hexchess.value.toString()).toBe('b/qbk/n1b1n/r5r/ppp1ppppp/11/4P6/4P1P4/3P1B1P3/2P2B2P2/1PRNQBKNRP1 b - 0 2')
|
|
848
|
+
})
|
|
849
|
+
|
|
850
|
+
test('updates targets when autoselect is true', async () => {
|
|
851
|
+
const selected = ref<number | null>(index('f5'))
|
|
852
|
+
const targets = ref<number[]>([])
|
|
853
|
+
|
|
854
|
+
setup(() => {
|
|
855
|
+
return () => (
|
|
856
|
+
<Hexboard
|
|
857
|
+
v-model:selected={selected.value}
|
|
858
|
+
v-model:targets={targets.value}
|
|
859
|
+
autoselect
|
|
860
|
+
/>
|
|
861
|
+
)
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
await expect(targets.value).toEqual([index('f6')])
|
|
865
|
+
|
|
866
|
+
selected.value = index('e4')
|
|
867
|
+
|
|
827
868
|
await nextTick()
|
|
828
869
|
|
|
829
|
-
|
|
830
|
-
await expect.element(page.getByTestId('selected-f5')).toBeVisible()
|
|
831
|
-
await expect(selected.value).toBe(index('f5'))
|
|
870
|
+
await expect(targets.value).toEqual([index('e5'), index('e6')])
|
|
832
871
|
})
|
package/src/tests/utils.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { position } from '@bedard/hexchess'
|
|
2
2
|
import { San } from '@bedard/hexchess'
|
|
3
|
+
import { expect } from 'vitest'
|
|
3
4
|
import type { BrowserPage } from 'vitest/browser'
|
|
4
5
|
import { render } from 'vitest-browser-vue'
|
|
5
6
|
import { nextTick } from 'vue'
|
|
6
7
|
|
|
7
|
-
/** make a move on the hexboard */
|
|
8
|
-
export async function
|
|
8
|
+
/** make a move on the hexboard by clicking */
|
|
9
|
+
export async function clickMove(
|
|
9
10
|
page: BrowserPage,
|
|
10
11
|
...sans: string[]
|
|
11
12
|
): Promise<void> {
|
|
@@ -20,6 +21,31 @@ export async function makeMove(
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
/** drag and drop a piece on the hexboard */
|
|
25
|
+
export async function dragMove(
|
|
26
|
+
page: BrowserPage,
|
|
27
|
+
...sans: string[]
|
|
28
|
+
): Promise<void> {
|
|
29
|
+
for (const str of sans) {
|
|
30
|
+
const san = San.from(str)
|
|
31
|
+
|
|
32
|
+
const fromPosition = page.getByTestId(`position-${position(san.from)}`)
|
|
33
|
+
const toPosition = page.getByTestId(`position-${position(san.to)}`)
|
|
34
|
+
|
|
35
|
+
await fromPosition
|
|
36
|
+
.element()
|
|
37
|
+
.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
38
|
+
await nextTick()
|
|
39
|
+
|
|
40
|
+
await expect.element(page.getByTestId('drag-piece')).toBeVisible()
|
|
41
|
+
|
|
42
|
+
await toPosition
|
|
43
|
+
.element()
|
|
44
|
+
.dispatchEvent(new PointerEvent('pointerup', { bubbles: true }))
|
|
45
|
+
await nextTick()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
/** setup a component */
|
|
24
50
|
export function setup(setup: () => any): ReturnType<typeof render> {
|
|
25
51
|
return render({ setup })
|
package/vite.config.ts
CHANGED
|
@@ -27,7 +27,24 @@ export default defineConfig({
|
|
|
27
27
|
rollupTypes: true,
|
|
28
28
|
}),
|
|
29
29
|
tailwindcss(),
|
|
30
|
-
vue(
|
|
30
|
+
vue({
|
|
31
|
+
template: {
|
|
32
|
+
compilerOptions: {
|
|
33
|
+
nodeTransforms: [
|
|
34
|
+
(node) => {
|
|
35
|
+
// remove data-testid attributes from production build
|
|
36
|
+
if (node.type === 1 && node.props) {
|
|
37
|
+
node.props = node.props
|
|
38
|
+
.filter(prop =>
|
|
39
|
+
!(prop.type === 6 && prop.name === 'data-testid')
|
|
40
|
+
&& !(prop.type === 7 && prop.name === 'bind' && prop.arg?.type === 4 && prop.arg.content === 'data-testid'),
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
31
48
|
],
|
|
32
49
|
resolve: {
|
|
33
50
|
alias: {
|