Test Video Play Using Cypress

How to trigger and check if an HTML video element is playing using Cypress test runner.

If you want to play a video using <video> element, how do you confirm that it is playing? By using its properties.

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<head>
<style>
video {
width: 100%;
}
</style>
</head>
<body>
<h1>Video</h1>
<video controls="true">
<source src="test1.mp4" type="video/mp4" />
</video>
</body>

🎁 You can find the source code for this blog post in the repo bahmutov/video-example. 📺 You can watch the explanation in the video Test And Play Video From Cypress.

At the start, the video should be paused. We can confirm it using have.prop assertions.

cypress/integration/spec.js
1
2
3
4
5
6
7
8
9
/// <reference types="cypress" />

it('plays video', () => {
cy.visit('index.html')
// https://html.spec.whatwg.org/multipage/media.html#playing-the-media-resource
cy.get('video')
.should('have.prop', 'paused', true)
.and('have.prop', 'ended', false)
})

Video is paused

Let's play the video. We need to get the video HTML element reference and call play() method.

1
2
3
4
5
6
cy.get('video')
.should('have.prop', 'paused', true)
.and('have.prop', 'ended', false)
.then(($video) => {
$video[0].play()
})

The video is playing - let's confirm it.

1
2
3
4
5
6
7
8
9
10
11
cy.get('video')
.should('have.prop', 'paused', true)
.and('have.prop', 'ended', false)
.then(($video) => {
$video[0].play()
})

// once the video starts playing, check props
cy.get('video')
.should('have.prop', 'paused', false)
.and('have.prop', 'ended', false)

Our video is pretty short (it is about 6 seconds long OBS recording). Thus we can use the built-in assertion retry-ability to wait until the property ended turns to true.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
it('plays video', () => {
cy.visit('index.html')
// https://html.spec.whatwg.org/multipage/media.html#playing-the-media-resource
cy.get('video')
.should('have.prop', 'paused', true)
.and('have.prop', 'ended', false)
.then(($video) => {
$video[0].play()
})

// once the video starts playing, check props
cy.get('video')
.should('have.prop', 'paused', false)
.and('have.prop', 'ended', false)

// wait for the video to finish playing
// by retrying the assertion
// I think our video is about 6 seconds long
cy.get('video', { timeout: 10000 }).and('have.prop', 'ended', true)
})

The test plays the video to the end

Video duration

If we know the expected video duration, we can confirm it

1
2
3
4
it('has known duration', () => {
cy.visit('index.html')
cy.get('video').should('have.prop', 'duration', 6.8)
})

If we do not known the video duration, we can assert that it is greater than zero seconds. At first the duration property is NaN, then it becomes defined as the video information loads.

1
2
3
4
5
6
7
it('has some positive duration', () => {
cy.visit('index.html')
// at first it is NaN, then it becomes a number
cy.get('video').should(($video) => {
expect($video[0].duration).to.be.gt(0)
})
})

Confirming the video has the duration number

Controlling the playing speed

We can change how fast the video is played using the playbackRate property. The test can limit how long it waits for the ended: true assertion.

1
2
3
4
5
6
7
8
9
10
11
12
it('plays video at 4x speed', () => {
cy.visit('index.html')
cy.get('video').then(($video) => {
$video[0].playbackRate = 4
$video[0].play()
})

// wait for the video to finish playing
// because the video is playing at 4x speed
// we don't have to wait as long
cy.get('video', { timeout: 2000 }).and('have.prop', 'ended', true)
})

The test plays the video at 4x speed

That was a fast test.