@nexxtmove/ui 2.5.0 → 2.7.0
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/dist/components/editor/molecules/EditorBorder/EditorBorder.vue.d.ts +12 -0
- package/dist/components/editor/molecules/EditorColor/_ThemeRoleSwatches.vue.d.ts +16 -0
- package/dist/index.js +1094 -1135
- package/dist/nuxt.js +0 -1
- package/package.json +38 -36
- package/src/attribute-contract.test.ts +151 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.stories.ts +36 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.test.ts +21 -0
- package/src/components/atoms/Avatar/Avatar.stories.ts +122 -0
- package/src/components/atoms/Avatar/Avatar.test.ts +134 -0
- package/src/components/atoms/Button/Button.stories.ts +290 -0
- package/src/components/atoms/Button/Button.test.ts +406 -0
- package/src/components/atoms/Card/Card.stories.ts +45 -0
- package/src/components/atoms/Card/Card.test.ts +46 -0
- package/src/components/atoms/Checkbox/Checkbox.stories.ts +35 -0
- package/src/components/atoms/Checkbox/Checkbox.test.ts +55 -0
- package/src/components/atoms/Chip/Chip.stories.ts +274 -0
- package/src/components/atoms/Chip/Chip.test.ts +230 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.stories.ts +189 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.test.ts +100 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.stories.ts +677 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.test.ts +864 -0
- package/src/components/atoms/Icon/Icon.stories.ts +63 -0
- package/src/components/atoms/Icon/Icon.test.ts +89 -0
- package/src/components/atoms/Loader/Loader.stories.ts +36 -0
- package/src/components/atoms/Loader/Loader.test.ts +40 -0
- package/src/components/atoms/PickerItem/PickerItem.stories.ts +172 -0
- package/src/components/atoms/PickerItem/PickerItem.test.ts +142 -0
- package/src/components/atoms/PickerPreview/PickerPreview.stories.ts +183 -0
- package/src/components/atoms/PickerPreview/PickerPreview.test.ts +137 -0
- package/src/components/atoms/PresenceIndicator/PresenceIndicator.stories.ts +21 -0
- package/src/components/atoms/PropertyListing/PropertyListing.stories.ts +38 -0
- package/src/components/atoms/PropertyListing/PropertyListing.test.ts +46 -0
- package/src/components/atoms/SelectableOption/SelectableOption.stories.ts +131 -0
- package/src/components/atoms/SelectableOption/SelectableOption.test.ts +96 -0
- package/src/components/atoms/Skeleton/Skeleton.stories.ts +27 -0
- package/src/components/atoms/Skeleton/Skeleton.test.ts +28 -0
- package/src/components/atoms/Slider/Slider.stories.ts +151 -0
- package/src/components/atoms/Slider/Slider.test.ts +191 -0
- package/src/components/atoms/Squircle/Squircle.stories.ts +114 -0
- package/src/components/atoms/Squircle/Squircle.test.ts +70 -0
- package/src/components/atoms/Switch/Switch.stories.ts +91 -0
- package/src/components/atoms/Switch/Switch.test.ts +183 -0
- package/src/components/atoms/Tooltip/Tooltip.stories.ts +158 -0
- package/src/components/atoms/Tooltip/Tooltip.test.ts +134 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.stories.ts +124 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.test.ts +464 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.stories.ts +266 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.test.ts +421 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.stories.ts +365 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.test.ts +484 -0
- package/src/components/editor/composables/useRadioGrid.test.ts +261 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.stories.ts +73 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.test.ts +189 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.stories.ts +309 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.test.ts +359 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.stories.ts +138 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.test.ts +344 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.stories.ts +87 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.test.ts +262 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.stories.ts +115 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.test.ts +243 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.stories.ts +187 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.test.ts +456 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.vue +58 -2
- package/src/components/editor/molecules/EditorColor/EditorColor.stories.ts +563 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +689 -0
- package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +793 -0
- package/src/components/editor/molecules/EditorColor/_ColorField.vue +7 -29
- package/src/components/editor/molecules/EditorColor/_ThemeRoleSwatches.vue +57 -0
- package/src/components/editor/molecules/EditorColor/gradientClass.test.ts +303 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.stories.ts +72 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.test.ts +124 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.stories.ts +46 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.test.ts +97 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.stories.ts +248 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.test.ts +288 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.stories.ts +129 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.test.ts +253 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.stories.ts +68 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.test.ts +150 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.stories.ts +71 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.test.ts +161 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.stories.ts +204 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.test.ts +238 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.stories.ts +257 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.test.ts +286 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.stories.ts +135 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.test.ts +230 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.stories.ts +154 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.test.ts +112 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.stories.ts +206 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.test.ts +332 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.stories.ts +374 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.test.ts +712 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.stories.ts +64 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.test.ts +137 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.stories.ts +103 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.test.ts +168 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.stories.ts +84 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.test.ts +253 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.stories.ts +49 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.test.ts +43 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.stories.ts +69 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.test.ts +155 -0
- package/src/components/molecules/Announcement/Announcement.stories.ts +224 -0
- package/src/components/molecules/Announcement/Announcement.test.ts +161 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.stories.ts +99 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.test.ts +70 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.stories.ts +148 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.test.ts +271 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.stories.ts +45 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.test.ts +205 -0
- package/src/components/molecules/InfoBlock/InfoBlock.stories.ts +84 -0
- package/src/components/molecules/InfoBlock/InfoBlock.test.ts +101 -0
- package/src/components/molecules/InputField/InputField.stories.ts +117 -0
- package/src/components/molecules/InputField/InputField.test.ts +138 -0
- package/src/components/molecules/InputSelect/InputSelect.stories.ts +116 -0
- package/src/components/molecules/InputSelect/InputSelect.test.ts +127 -0
- package/src/components/molecules/ModalFooter/ModalFooter.stories.ts +204 -0
- package/src/components/molecules/ModalFooter/ModalFooter.test.ts +276 -0
- package/src/components/molecules/PageCard/PageCard.stories.ts +100 -0
- package/src/components/molecules/PageCard/PageCard.test.ts +91 -0
- package/src/components/molecules/PageContainer/PageContainer.stories.ts +90 -0
- package/src/components/molecules/PageContainer/PageContainer.test.ts +50 -0
- package/src/components/molecules/PageMockup/PageMockup.stories.ts +38 -0
- package/src/components/molecules/PageMockup/PageMockup.test.ts +20 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.stories.ts +63 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.test.ts +41 -0
- package/src/components/molecules/Pagination/Pagination.stories.ts +72 -0
- package/src/components/molecules/Pagination/Pagination.test.ts +130 -0
- package/src/components/molecules/PickerGroup/PickerGroup.stories.ts +90 -0
- package/src/components/molecules/PickerGroup/PickerGroup.test.ts +145 -0
- package/src/components/molecules/PickerList/PickerList.stories.ts +436 -0
- package/src/components/molecules/PickerList/PickerList.test.ts +870 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.stories.ts +48 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.test.ts +107 -0
- package/src/components/molecules/PropertyCard/PropertyCard.stories.ts +46 -0
- package/src/components/molecules/PropertyCard/PropertyCard.test.ts +59 -0
- package/src/components/molecules/Select/Select.stories.ts +264 -0
- package/src/components/molecules/Select/Select.test.ts +315 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.stories.ts +84 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.test.ts +103 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.stories.ts +11 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.test.ts +38 -0
- package/src/components/molecules/SocialIcons/SocialIcons.stories.ts +28 -0
- package/src/components/molecules/SocialIcons/SocialIcons.test.ts +58 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +11 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +116 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.stories.ts +37 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.test.ts +126 -0
- package/src/components/molecules/Source/Source.stories.ts +35 -0
- package/src/components/molecules/Source/Source.test.ts +45 -0
- package/src/components/molecules/TabbedContent/TabbedContent.stories.ts +96 -0
- package/src/components/molecules/TabbedContent/TabbedContent.test.ts +109 -0
- package/src/components/molecules/Tabs/Tabs.stories.ts +230 -0
- package/src/components/molecules/Tabs/Tabs.test.ts +280 -0
- package/src/components/molecules/TemplateCard/TemplateCard.stories.ts +327 -0
- package/src/components/molecules/TemplateCard/TemplateCard.test.ts +339 -0
- package/src/components/molecules/Textarea/Textarea.stories.ts +216 -0
- package/src/components/molecules/Textarea/Textarea.test.ts +191 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.stories.ts +319 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.test.ts +224 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.stories.ts +29 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.test.ts +30 -0
- package/src/components/molecules/Toast/Toast.stories.ts +110 -0
- package/src/components/molecules/Toast/Toast.test.ts +103 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.stories.ts +297 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.test.ts +578 -0
- package/src/components/molecules/Tree/Tree.stories.ts +884 -0
- package/src/components/molecules/Tree/Tree.test.ts +1247 -0
- package/src/components/molecules/Tree/tree-drop.test.ts +223 -0
- package/src/components/organisms/Calendar/Calendar.stories.ts +108 -0
- package/src/components/organisms/Calendar/Calendar.test.ts +279 -0
- package/src/components/organisms/Calendar/_CalendarDayView.test.ts +104 -0
- package/src/components/organisms/Calendar/_CalendarHeader.test.ts +86 -0
- package/src/components/organisms/Calendar/_CalendarMonthView.test.ts +52 -0
- package/src/components/organisms/Calendar/_CalendarTimePicker.test.ts +89 -0
- package/src/components/organisms/Calendar/_CalendarYearView.test.ts +56 -0
- package/src/components/organisms/ColorPicker/ColorPicker.stories.ts +229 -0
- package/src/components/organisms/ColorPicker/ColorPicker.test.ts +638 -0
- package/src/components/organisms/ColorPicker/gradient.test.ts +67 -0
- package/src/components/organisms/ColorPicker/gradientGeometry.test.ts +206 -0
- package/src/components/organisms/Combobox/Combobox.stories.ts +234 -0
- package/src/components/organisms/Combobox/Combobox.test.ts +512 -0
- package/src/components/organisms/Combobox/Combobox.vue +1 -1
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.stories.ts +158 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.test.ts +524 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.vue +1 -1
- package/src/components/organisms/CountrySelect/CountrySelect.stories.ts +139 -0
- package/src/components/organisms/CountrySelect/CountrySelect.test.ts +149 -0
- package/src/components/organisms/DatePicker/DatePicker.stories.ts +163 -0
- package/src/components/organisms/DatePicker/DatePicker.test.ts +214 -0
- package/src/components/organisms/DropdownButton/DropdownButton.stories.ts +123 -0
- package/src/components/organisms/DropdownButton/DropdownButton.test.ts +379 -0
- package/src/components/organisms/FontPicker/FontPicker.stories.ts +476 -0
- package/src/components/organisms/FontPicker/FontPicker.test.ts +873 -0
- package/src/components/organisms/Header/Header.stories.ts +75 -0
- package/src/components/organisms/Header/Header.test.ts +233 -0
- package/src/components/organisms/IconPicker/IconPicker.stories.ts +734 -0
- package/src/components/organisms/IconPicker/IconPicker.test.ts +669 -0
- package/src/components/organisms/IconPicker/_IconPickerGrid.test.ts +184 -0
- package/src/components/organisms/IconPicker/_IconPickerRail.test.ts +102 -0
- package/src/components/organisms/IconPicker/useIconPickerNavigation.test.ts +215 -0
- package/src/components/organisms/Modal/Modal.stories.ts +350 -0
- package/src/components/organisms/Modal/Modal.test.ts +275 -0
- package/src/components/organisms/PickerPanel/PickerPanel.stories.ts +452 -0
- package/src/components/organisms/PickerPanel/PickerPanel.test.ts +358 -0
- package/src/components/organisms/ProgressBar/ProgressBar.stories.ts +100 -0
- package/src/components/organisms/ProgressBar/ProgressBar.test.ts +417 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.stories.ts +191 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.test.ts +165 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.stories.ts +65 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.test.ts +466 -0
- package/src/components/organisms/StepperHeader/StepperHeader.stories.ts +91 -0
- package/src/components/organisms/StepperHeader/StepperHeader.test.ts +279 -0
- package/src/components/organisms/Table/Table.stories.ts +332 -0
- package/src/components/organisms/Table/Table.test.ts +320 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.stories.ts +198 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.test.ts +283 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.stories.ts +120 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.test.ts +91 -0
- package/src/components/organisms/Toaster/Toaster.stories.ts +51 -0
- package/src/components/organisms/Toaster/Toaster.test.ts +75 -0
- package/src/components/organisms/VideoControls/VideoControls.stories.ts +792 -0
- package/src/components/organisms/VideoControls/VideoControls.test.ts +1056 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.stories.ts +327 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.test.ts +439 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { flushPromises, mount, type VueWrapper } from '@vue/test-utils'
|
|
3
|
+
import { defineComponent, h, nextTick, ref } from 'vue'
|
|
4
|
+
import VideoPlayer from './VideoPlayer.vue'
|
|
5
|
+
import VideoControls from '../VideoControls/VideoControls.vue'
|
|
6
|
+
import type { NexxtVideoPlayerProps } from './VideoPlayer.vue'
|
|
7
|
+
|
|
8
|
+
const SRC = 'https://example.com/media/rondleiding.mp4'
|
|
9
|
+
|
|
10
|
+
let wrapper: VueWrapper | undefined
|
|
11
|
+
|
|
12
|
+
const mountPlayer = (props: Partial<NexxtVideoPlayerProps> = {}, attrs = {}) => {
|
|
13
|
+
wrapper = mount(VideoPlayer, {
|
|
14
|
+
props: { src: SRC, ...props },
|
|
15
|
+
attrs,
|
|
16
|
+
attachTo: document.body,
|
|
17
|
+
})
|
|
18
|
+
return wrapper
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** play() as a browser does it: the play event, then a resolved promise (jsdom plays nothing). */
|
|
22
|
+
const playsForReal = () =>
|
|
23
|
+
vi.spyOn(HTMLMediaElement.prototype, 'play').mockImplementation(function (
|
|
24
|
+
this: HTMLMediaElement,
|
|
25
|
+
) {
|
|
26
|
+
this.dispatchEvent(new Event('play'))
|
|
27
|
+
return Promise.resolve()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
vi.useRealTimers()
|
|
32
|
+
wrapper?.unmount()
|
|
33
|
+
wrapper = undefined
|
|
34
|
+
vi.restoreAllMocks()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('VideoPlayer', () => {
|
|
38
|
+
describe('video element', () => {
|
|
39
|
+
it('renders the src, loop and an accessible name', () => {
|
|
40
|
+
const w = mountPlayer({ loop: true })
|
|
41
|
+
const video = w.get('video')
|
|
42
|
+
|
|
43
|
+
expect(video.attributes('src')).toBe(SRC)
|
|
44
|
+
expect(video.attributes('loop')).toBeDefined()
|
|
45
|
+
expect(video.attributes('aria-label')).toBe('Video')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('takes its accessible name from `name`, or from messages.video', () => {
|
|
49
|
+
expect(mountPlayer({ name: 'Rondleiding' }).get('video').attributes('aria-label')).toBe(
|
|
50
|
+
'Rondleiding',
|
|
51
|
+
)
|
|
52
|
+
wrapper?.unmount()
|
|
53
|
+
expect(
|
|
54
|
+
mountPlayer({ messages: { video: 'Clip' } })
|
|
55
|
+
.get('video')
|
|
56
|
+
.attributes('aria-label'),
|
|
57
|
+
).toBe('Clip')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('preloads nothing with a poster, and only the metadata without one', () => {
|
|
61
|
+
const withPoster = mountPlayer({ poster: '/poster.jpg' }).get('video')
|
|
62
|
+
expect(withPoster.attributes('poster')).toBe('/poster.jpg')
|
|
63
|
+
expect(withPoster.attributes('preload')).toBe('none')
|
|
64
|
+
wrapper?.unmount()
|
|
65
|
+
|
|
66
|
+
const without = mountPlayer().get('video')
|
|
67
|
+
expect(without.attributes('poster')).toBeUndefined()
|
|
68
|
+
expect(without.attributes('preload')).toBe('metadata')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('lets a consumer class land on the root', () => {
|
|
72
|
+
const w = mountPlayer({}, { class: 'rounded-none' })
|
|
73
|
+
expect(w.classes()).toContain('rounded-none')
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('controls', () => {
|
|
78
|
+
it('shows the full bar by default', () => {
|
|
79
|
+
const w = mountPlayer({ name: 'Rondleiding' })
|
|
80
|
+
expect(w.get('[role="group"]').attributes('aria-label')).toBe('Videobediening: Rondleiding')
|
|
81
|
+
expect(w.find('[role="slider"][aria-label="Tijdlijn"]').exists()).toBe(true)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('shows only the play/pause and mute buttons with `toggle`', () => {
|
|
85
|
+
const w = mountPlayer({ controls: 'toggle' })
|
|
86
|
+
const corner = w.findAll('[data-video-controls] button')
|
|
87
|
+
expect(corner.map((b) => b.attributes('aria-label'))).toEqual(['Geluid uit', 'Afspelen'])
|
|
88
|
+
expect(w.find('[role="slider"]').exists()).toBe(false)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('shows nothing with `none`', () => {
|
|
92
|
+
const w = mountPlayer({ controls: 'none' })
|
|
93
|
+
expect(w.find('button').exists()).toBe(false)
|
|
94
|
+
expect(w.find('[data-video-controls]').exists()).toBe(false)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('passes the messages on to the controls', () => {
|
|
98
|
+
const w = mountPlayer({ messages: { play: 'Play', timeline: 'Timeline' } })
|
|
99
|
+
expect(w.find('button[aria-label="Play"]').exists()).toBe(true)
|
|
100
|
+
expect(w.find('[role="slider"][aria-label="Timeline"]').exists()).toBe(true)
|
|
101
|
+
expect(w.find('button[aria-label="Geluid uit"]').exists()).toBe(true)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
describe('big play button', () => {
|
|
106
|
+
/** Lets the button's fade-out (a real Transition in jsdom) finish. */
|
|
107
|
+
const settle = () => new Promise((resolve) => setTimeout(resolve, 50))
|
|
108
|
+
const startButton = (w: VueWrapper) => w.find('[data-video-start]')
|
|
109
|
+
const controlsRoot = (w: VueWrapper) => w.get('[data-video-controls]').element.parentElement!
|
|
110
|
+
|
|
111
|
+
it.each(['full', 'toggle'] as const)('stands before the first play with `%s`', (controls) => {
|
|
112
|
+
const w = mountPlayer({ controls, name: 'Rondleiding' })
|
|
113
|
+
|
|
114
|
+
expect(startButton(w).attributes('aria-label')).toBe('Afspelen: Rondleiding')
|
|
115
|
+
// The controls wait behind it: hidden and out of reach of Tab
|
|
116
|
+
expect(controlsRoot(w).hasAttribute('inert')).toBe(true)
|
|
117
|
+
expect(controlsRoot(w).classList).toContain('invisible')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('is not there with `none` or with autoplay', () => {
|
|
121
|
+
expect(startButton(mountPlayer({ controls: 'none' })).exists()).toBe(false)
|
|
122
|
+
wrapper?.unmount()
|
|
123
|
+
expect(startButton(mountPlayer({ autoplay: true, muted: true })).exists()).toBe(false)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// Enter and Space are the button's native activation, a click; the real keys are in the story
|
|
127
|
+
it('plays on a click and hands the focus to the controls', async () => {
|
|
128
|
+
const play = playsForReal()
|
|
129
|
+
const w = mountPlayer()
|
|
130
|
+
// The media listeners attach after mount, as they would before any real event
|
|
131
|
+
await flushPromises()
|
|
132
|
+
const button = startButton(w).element as HTMLButtonElement
|
|
133
|
+
button.focus()
|
|
134
|
+
|
|
135
|
+
// From the keyboard: a click without a pointer (detail 0)
|
|
136
|
+
button.click()
|
|
137
|
+
await flushPromises()
|
|
138
|
+
await settle()
|
|
139
|
+
|
|
140
|
+
expect(play).toHaveBeenCalledOnce()
|
|
141
|
+
expect(startButton(w).exists()).toBe(false)
|
|
142
|
+
expect(controlsRoot(w).hasAttribute('inert')).toBe(false)
|
|
143
|
+
// The controls' own play button (jsdom does not play, so it still says Afspelen)
|
|
144
|
+
const focused = document.activeElement!
|
|
145
|
+
expect(focused.closest('[data-video-controls]')).not.toBeNull()
|
|
146
|
+
expect(focused.getAttribute('aria-label')).toBe('Afspelen')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it.each([
|
|
150
|
+
['a mouse click', 1, false],
|
|
151
|
+
['the keyboard', 0, true],
|
|
152
|
+
])(
|
|
153
|
+
'after %s, the bar hides as usual unless the keyboard started it',
|
|
154
|
+
async (_, detail, stays) => {
|
|
155
|
+
vi.useFakeTimers()
|
|
156
|
+
const w = mountPlayer()
|
|
157
|
+
await flushPromises()
|
|
158
|
+
const video = w.get('video').element
|
|
159
|
+
// jsdom does not play: the video reports playing once started
|
|
160
|
+
let paused = true
|
|
161
|
+
Object.defineProperty(video, 'paused', { get: () => paused })
|
|
162
|
+
vi.spyOn(video, 'play').mockImplementation(() => {
|
|
163
|
+
paused = false
|
|
164
|
+
video.dispatchEvent(new Event('play'))
|
|
165
|
+
return Promise.resolve()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const button = startButton(w).element as HTMLButtonElement
|
|
169
|
+
button.focus()
|
|
170
|
+
button.dispatchEvent(new MouseEvent('click', { bubbles: true, detail }))
|
|
171
|
+
await flushPromises()
|
|
172
|
+
vi.advanceTimersByTime(3000)
|
|
173
|
+
await nextTick()
|
|
174
|
+
|
|
175
|
+
expect(document.activeElement?.closest('[data-video-controls]')).not.toBeNull()
|
|
176
|
+
expect(w.get('[data-video-controls]').classes('opacity-100')).toBe(stays)
|
|
177
|
+
vi.useRealTimers()
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
it('hands the focus over when the video starts another way while the button has it', async () => {
|
|
182
|
+
playsForReal()
|
|
183
|
+
const player = ref<InstanceType<typeof VideoPlayer> | null>(null)
|
|
184
|
+
wrapper = mount(
|
|
185
|
+
defineComponent({ setup: () => () => h(VideoPlayer, { ref: player, src: SRC }) }),
|
|
186
|
+
{ attachTo: document.body },
|
|
187
|
+
)
|
|
188
|
+
await flushPromises()
|
|
189
|
+
;(wrapper.get('[data-video-start]').element as HTMLButtonElement).focus()
|
|
190
|
+
|
|
191
|
+
player.value!.playing = true
|
|
192
|
+
await flushPromises()
|
|
193
|
+
|
|
194
|
+
expect(document.activeElement?.closest('[data-video-controls]')).not.toBeNull()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('stays when play() is refused: nothing plays, so nothing has started', async () => {
|
|
198
|
+
vi.spyOn(HTMLMediaElement.prototype, 'play').mockRejectedValue(
|
|
199
|
+
new DOMException('Not allowed', 'NotAllowedError'),
|
|
200
|
+
)
|
|
201
|
+
const w = mountPlayer()
|
|
202
|
+
await flushPromises()
|
|
203
|
+
|
|
204
|
+
;(startButton(w).element as HTMLButtonElement).click()
|
|
205
|
+
await flushPromises()
|
|
206
|
+
await settle()
|
|
207
|
+
|
|
208
|
+
expect(startButton(w).exists()).toBe(true)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('does not come back after a pause or the end', async () => {
|
|
212
|
+
const w = mountPlayer()
|
|
213
|
+
// The media listeners attach after mount, as they would before any real event
|
|
214
|
+
await flushPromises()
|
|
215
|
+
const video = w.get('video').element
|
|
216
|
+
video.dispatchEvent(new Event('play'))
|
|
217
|
+
await settle()
|
|
218
|
+
expect(startButton(w).exists()).toBe(false)
|
|
219
|
+
|
|
220
|
+
video.dispatchEvent(new Event('pause'))
|
|
221
|
+
video.dispatchEvent(new Event('ended'))
|
|
222
|
+
await settle()
|
|
223
|
+
expect(startButton(w).exists()).toBe(false)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('goes when the parent plays the video', async () => {
|
|
227
|
+
playsForReal()
|
|
228
|
+
const player = ref<InstanceType<typeof VideoPlayer> | null>(null)
|
|
229
|
+
const Parent = defineComponent({
|
|
230
|
+
setup: () => () => h(VideoPlayer, { ref: player, src: SRC }),
|
|
231
|
+
})
|
|
232
|
+
wrapper = mount(Parent, { attachTo: document.body })
|
|
233
|
+
await flushPromises()
|
|
234
|
+
expect(wrapper.find('[data-video-start]').exists()).toBe(true)
|
|
235
|
+
|
|
236
|
+
player.value!.playing = true
|
|
237
|
+
await settle()
|
|
238
|
+
expect(wrapper.find('[data-video-start]').exists()).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('clicks on the video', () => {
|
|
243
|
+
const mountInParent = (controls: NexxtVideoPlayerProps['controls']) => {
|
|
244
|
+
const onParentClick = vi.fn()
|
|
245
|
+
const Parent = defineComponent({
|
|
246
|
+
setup: () => () =>
|
|
247
|
+
h('div', { onClick: onParentClick }, [h(VideoPlayer, { src: SRC, controls })]),
|
|
248
|
+
})
|
|
249
|
+
wrapper = mount(Parent, { attachTo: document.body })
|
|
250
|
+
return onParentClick
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
it('with `none`, leaves the click to the parent and does not play', async () => {
|
|
254
|
+
const play = vi.spyOn(HTMLMediaElement.prototype, 'play')
|
|
255
|
+
const onParentClick = mountInParent('none')
|
|
256
|
+
await nextTick()
|
|
257
|
+
|
|
258
|
+
wrapper!
|
|
259
|
+
.get('video')
|
|
260
|
+
.element.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
261
|
+
wrapper!.get('video').element.click()
|
|
262
|
+
|
|
263
|
+
expect(onParentClick).toHaveBeenCalledOnce()
|
|
264
|
+
expect(play).not.toHaveBeenCalled()
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('with `toggle`, leaves the click on the video alone', async () => {
|
|
268
|
+
const play = vi.spyOn(HTMLMediaElement.prototype, 'play')
|
|
269
|
+
mountInParent('toggle')
|
|
270
|
+
await nextTick()
|
|
271
|
+
|
|
272
|
+
wrapper!.get('video').element.click()
|
|
273
|
+
expect(play).not.toHaveBeenCalled()
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('with `full`, a mouse click plays', async () => {
|
|
277
|
+
const play = vi.spyOn(HTMLMediaElement.prototype, 'play')
|
|
278
|
+
mountInParent('full')
|
|
279
|
+
await nextTick()
|
|
280
|
+
|
|
281
|
+
wrapper!.get('video').element.click()
|
|
282
|
+
expect(play).toHaveBeenCalledOnce()
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
describe('autoplay', () => {
|
|
287
|
+
it('mutes before it plays', async () => {
|
|
288
|
+
const mutedAtPlay: boolean[] = []
|
|
289
|
+
vi.spyOn(HTMLMediaElement.prototype, 'play').mockImplementation(function (
|
|
290
|
+
this: HTMLMediaElement,
|
|
291
|
+
) {
|
|
292
|
+
mutedAtPlay.push(this.muted)
|
|
293
|
+
return Promise.resolve()
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
mountPlayer({ autoplay: true, muted: true })
|
|
297
|
+
await nextTick()
|
|
298
|
+
|
|
299
|
+
expect(mutedAtPlay).toEqual([true])
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('does not play without autoplay', async () => {
|
|
303
|
+
const play = vi.spyOn(HTMLMediaElement.prototype, 'play')
|
|
304
|
+
mountPlayer({ muted: true })
|
|
305
|
+
await nextTick()
|
|
306
|
+
|
|
307
|
+
expect(play).not.toHaveBeenCalled()
|
|
308
|
+
expect(wrapper!.get('video').element.muted).toBe(true)
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it('swallows a refused play() and reports it is not playing', async () => {
|
|
312
|
+
vi.spyOn(HTMLMediaElement.prototype, 'play').mockRejectedValue(
|
|
313
|
+
new DOMException('Autoplay is not allowed', 'NotAllowedError'),
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
const player = ref<InstanceType<typeof VideoPlayer> | null>(null)
|
|
317
|
+
const Parent = defineComponent({
|
|
318
|
+
setup: () => () => h(VideoPlayer, { ref: player, src: SRC, autoplay: true }),
|
|
319
|
+
})
|
|
320
|
+
wrapper = mount(Parent, { attachTo: document.body })
|
|
321
|
+
await flushPromises()
|
|
322
|
+
|
|
323
|
+
expect(player.value?.playing).toBe(false)
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
describe('removed props', () => {
|
|
328
|
+
it.each([
|
|
329
|
+
[{ showControls: false }, 'controls="none"'],
|
|
330
|
+
[{ 'show-controls': 'false' }, 'controls="none"'],
|
|
331
|
+
[{ labels: { play: 'Play' } }, '`messages`'],
|
|
332
|
+
])('warns once in development about %o, and keeps it off the root', (attrs, replacement) => {
|
|
333
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
334
|
+
const w = mountPlayer({}, attrs)
|
|
335
|
+
|
|
336
|
+
expect(warn).toHaveBeenCalledOnce()
|
|
337
|
+
expect(warn.mock.calls[0][0]).toContain(replacement)
|
|
338
|
+
for (const key of Object.keys(attrs)) expect(w.attributes(key)).toBeUndefined()
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('does not warn in production', () => {
|
|
342
|
+
vi.stubEnv('NODE_ENV', 'production')
|
|
343
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
344
|
+
mountPlayer({}, { showControls: false, labels: {} })
|
|
345
|
+
|
|
346
|
+
expect(warn).not.toHaveBeenCalled()
|
|
347
|
+
vi.unstubAllEnvs()
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('still passes other attrs to the root', () => {
|
|
351
|
+
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
352
|
+
const w = mountPlayer({}, { id: 'intro', 'data-test': 'player', showControls: false })
|
|
353
|
+
|
|
354
|
+
expect(w.attributes('id')).toBe('intro')
|
|
355
|
+
expect(w.attributes('data-test')).toBe('player')
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
describe('error state', () => {
|
|
360
|
+
it('shows the message, and never the URL', async () => {
|
|
361
|
+
const w = mountPlayer()
|
|
362
|
+
await w.get('video').trigger('error')
|
|
363
|
+
|
|
364
|
+
const alert = w.get('[role="alert"]')
|
|
365
|
+
expect(alert.text()).toBe('De video kan niet worden geladen')
|
|
366
|
+
expect(alert.html()).not.toContain('example.com')
|
|
367
|
+
expect(alert.html()).not.toContain('rondleiding.mp4')
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('takes its copy from messages.error', async () => {
|
|
371
|
+
const w = mountPlayer({ messages: { error: 'Video unavailable' } })
|
|
372
|
+
await w.get('video').trigger('error')
|
|
373
|
+
expect(w.get('[role="alert"]').text()).toBe('Video unavailable')
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('hides the controls and the video', async () => {
|
|
377
|
+
const w = mountPlayer()
|
|
378
|
+
await w.get('video').trigger('error')
|
|
379
|
+
|
|
380
|
+
expect(w.find('[data-video-controls]').exists()).toBe(false)
|
|
381
|
+
expect(w.get('video').classes()).toContain('invisible')
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('brings the cursor back when the error comes while the bar is hidden', async () => {
|
|
385
|
+
const w = mountPlayer()
|
|
386
|
+
w.findComponent(VideoControls).vm.$emit('visibility-change', false)
|
|
387
|
+
await nextTick()
|
|
388
|
+
expect(w.classes()).toContain('cursor-none')
|
|
389
|
+
|
|
390
|
+
await w.get('video').trigger('error')
|
|
391
|
+
expect(w.classes()).not.toContain('cursor-none')
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
it('clears when the src changes', async () => {
|
|
395
|
+
const w = mountPlayer()
|
|
396
|
+
await w.get('video').trigger('error')
|
|
397
|
+
await w.setProps({ src: 'https://example.com/other.mp4' })
|
|
398
|
+
|
|
399
|
+
expect(w.find('[role="alert"]').exists()).toBe(false)
|
|
400
|
+
})
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
describe('expose', () => {
|
|
404
|
+
const mountWithRef = () => {
|
|
405
|
+
const player = ref<InstanceType<typeof VideoPlayer> | null>(null)
|
|
406
|
+
const Parent = defineComponent({
|
|
407
|
+
setup: () => () => h(VideoPlayer, { ref: player, src: SRC, controls: 'none' }),
|
|
408
|
+
})
|
|
409
|
+
wrapper = mount(Parent, { attachTo: document.body })
|
|
410
|
+
return player
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
it('writes `playing` through to the video', async () => {
|
|
414
|
+
const play = vi.spyOn(HTMLMediaElement.prototype, 'play')
|
|
415
|
+
const pause = vi.spyOn(HTMLMediaElement.prototype, 'pause')
|
|
416
|
+
const player = mountWithRef()
|
|
417
|
+
await nextTick()
|
|
418
|
+
|
|
419
|
+
player.value!.playing = !player.value!.playing
|
|
420
|
+
expect(play).toHaveBeenCalledOnce()
|
|
421
|
+
expect(player.value!.playing).toBe(true)
|
|
422
|
+
|
|
423
|
+
player.value!.playing = false
|
|
424
|
+
expect(pause).toHaveBeenCalledOnce()
|
|
425
|
+
// At once, not only after the pause event
|
|
426
|
+
expect(player.value!.playing).toBe(false)
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
it('reads and writes `muted`', async () => {
|
|
430
|
+
const player = mountWithRef()
|
|
431
|
+
await nextTick()
|
|
432
|
+
|
|
433
|
+
expect(player.value!.muted).toBe(false)
|
|
434
|
+
player.value!.muted = true
|
|
435
|
+
expect(wrapper!.get('video').element.muted).toBe(true)
|
|
436
|
+
expect(player.value!.muted).toBe(true)
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
})
|