11import { breakpoints } from '@guardian/source/foundations' ;
22import type { Meta , StoryObj } from '@storybook/react' ;
3+ import { expect , userEvent , within } from '@storybook/test' ;
34import { centreColumnDecorator } from '../../.storybook/decorators/gridDecorators' ;
45import { LoopVideo } from './LoopVideo.importable' ;
56
6- export default {
7+ const meta = {
78 component : LoopVideo ,
89 title : 'Components/LoopVideo' ,
910 decorators : [ centreColumnDecorator ] ,
@@ -15,7 +16,10 @@ export default {
1516 } ,
1617} satisfies Meta < typeof LoopVideo > ;
1718
18- export const Default = {
19+ export default meta ;
20+ type Story = StoryObj < typeof LoopVideo > ;
21+
22+ export const Default : Story = {
1923 name : 'Default' ,
2024 args : {
2125 src : 'https://uploads.guim.co.uk/2025%2F06%2F20%2Ftesting+only%2C+please+ignore--3cb22b60-2c3f-48d6-8bce-38c956907cce-3.mp4' ,
@@ -26,14 +30,84 @@ export const Default = {
2630 image : 'https://media.guim.co.uk/9bdb802e6da5d3fd249b5060f367b3a817965f0c/0_0_1800_1080/master/1800.jpg' ,
2731 fallbackImage : '' ,
2832 } ,
29- } satisfies StoryObj < typeof LoopVideo > ;
33+ } ;
3034
31- export const Without5to4Ratio = {
35+ export const Without5to4Ratio : Story = {
3236 name : 'Without 5:4 aspect ratio' ,
3337 args : {
3438 ...Default . args ,
3539 src : 'https://uploads.guim.co.uk/2024/10/01/241001HeleneLoop_2.mp4' ,
3640 height : 1080 ,
3741 width : 1920 ,
3842 } ,
39- } satisfies StoryObj < typeof LoopVideo > ;
43+ } ;
44+
45+ export const PausePlay : Story = {
46+ ...Default ,
47+ play : async ( { canvasElement } ) => {
48+ const canvas = within ( canvasElement ) ;
49+ const videoEl = canvas . getByTestId ( 'loop-video' ) ;
50+
51+ await userEvent . click ( videoEl , {
52+ delay : 300 , // Allow video to start playing.
53+ } ) ;
54+ await canvas . findByTestId ( 'play-icon' ) ;
55+
56+ const progressBar = await canvas . findByRole ( 'progressbar' ) ;
57+ await expect ( Number ( progressBar . ariaValueNow ) ) . toBeGreaterThan ( 0 ) ;
58+
59+ // Play Video
60+ await userEvent . click ( videoEl ) ;
61+ await expect ( canvas . queryByTestId ( 'play-icon' ) ) . not . toBeInTheDocument ( ) ;
62+ } ,
63+ } ;
64+
65+ export const UnmuteMute : Story = {
66+ ...Default ,
67+ parameters : {
68+ test : {
69+ // The following error is received without this flag: "TypeError: ophan.trackClickComponentEvent is not a function"
70+ dangerouslyIgnoreUnhandledErrors : true ,
71+ } ,
72+ } ,
73+ play : async ( { canvasElement } ) => {
74+ const canvas = within ( canvasElement ) ;
75+
76+ await canvas . findByTestId ( 'unmute-icon' ) ;
77+
78+ await userEvent . click ( canvas . getByTestId ( 'unmute-icon' ) ) ;
79+ await canvas . findByTestId ( 'mute-icon' ) ;
80+
81+ await userEvent . click ( canvas . getByTestId ( 'mute-icon' ) ) ;
82+ await canvas . findByTestId ( 'unmute-icon' ) ;
83+ } ,
84+ } ;
85+
86+ // Function to emulate pausing between interactions
87+ function sleep ( ms : number ) {
88+ return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
89+ }
90+
91+ export const InteractionObserver : Story = {
92+ ...Default ,
93+ render : ( args ) => (
94+ < div data-testid = "test-container" >
95+ < LoopVideo { ...args } />
96+ < div style = { { height : '100vh' } } > </ div >
97+ < p data-testid = "page-end" > End of page</ p >
98+ </ div >
99+ ) ,
100+ play : async ( { canvasElement } ) => {
101+ const canvas = within ( canvasElement ) ;
102+
103+ await sleep ( 500 ) ; // Allow enough time for the autoplay video to start.
104+ canvas . getByTestId ( 'page-end' ) . scrollIntoView ( ) ;
105+
106+ const progressBar = await canvas . findByRole ( 'progressbar' ) ;
107+ const progress = Number ( progressBar . ariaValueNow ) ;
108+
109+ await sleep ( 500 ) ; // Allow enough time to be confident that the video is paused.
110+ await expect ( Number ( progressBar . ariaValueNow ) ) . toEqual ( progress ) ;
111+ await expect ( canvas . queryByTestId ( 'play-icon' ) ) . not . toBeInTheDocument ( ) ;
112+ } ,
113+ } ;
0 commit comments