├── .eslintignore
├── .eslintrc.js
├── .github
├── CODEOWNERS
└── workflows
│ ├── deprecate.yml
│ ├── e2e_pr.yml
│ ├── release-edge.yml
│ ├── release.yml
│ └── validate.yml
├── .gitignore
├── .husky
└── commit-msg
├── .npmrc
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.js
├── commitlint.config.js
├── docs
├── 360.html
├── _template.html
├── adaptive-streaming.html
├── analytics.html
├── api.html
├── audio.html
├── autoplay-on-scroll.html
├── chapters.html
├── cloudinary-analytics.html
├── codec-formats.html
├── colors.html
├── components.html
├── custom-cld-errors.html
├── debug.html
├── embedded-iframe.html
├── es-modules
│ ├── 360.html
│ ├── _template.html
│ ├── adaptive-streaming.html
│ ├── all.html
│ ├── analytics.html
│ ├── api.html
│ ├── audio.html
│ ├── autoplay-on-scroll.html
│ ├── chapters.html
│ ├── cloudinary-analytics.html
│ ├── codec-formats.html
│ ├── colors.html
│ ├── components.html
│ ├── custom-cld-errors.html
│ ├── debug.html
│ ├── floating-player.html
│ ├── fluid.html
│ ├── force-hls-subtitles.html
│ ├── highlights-graph.html
│ ├── index.html
│ ├── interaction-area.html
│ ├── multiple-players.html
│ ├── netlify.toml
│ ├── package-lock.json
│ ├── package.json
│ ├── playlist-by-tag.html
│ ├── playlist.html
│ ├── pnpm-lock.yaml
│ ├── poster.html
│ ├── profiles.html
│ ├── raw-url.html
│ ├── recommendations.html
│ ├── seek-thumbs.html
│ ├── shoppable.html
│ ├── subtitles-and-captions.html
│ ├── transformations.html
│ ├── ui-config.html
│ ├── vast-vpaid.html
│ ├── visual-search.html
│ └── vite.config.js
├── floating-player.html
├── fluid.html
├── force-hls-subtitles-ios.html
├── highlights-graph.html
├── index.html
├── interaction-area.html
├── live-streaming.html
├── multiple-players.html
├── playlist-by-tag-captions.html
├── playlist.html
├── poster.html
├── profiles.html
├── raw-url.html
├── recommendations.html
├── scripts.js
├── seek-thumbs.html
├── shoppable.html
├── subtitles-and-captions.html
├── transformations.html
├── ui-config.html
├── vast-vpaid.html
└── visual-search.html
├── env.example.js
├── index.html
├── jest-puppeteer.config.js
├── jest.config.js
├── package-lock.json
├── package.json
├── src
├── assets
│ ├── icon-font
│ │ ├── README.md
│ │ ├── VideoJS.svg
│ │ ├── VideoJS.ttf
│ │ ├── VideoJS.woff
│ │ ├── custom-icons
│ │ │ └── cld
│ │ │ │ ├── check.svg
│ │ │ │ ├── forward-10.svg
│ │ │ │ ├── fullscreen-exit.svg
│ │ │ │ ├── fullscreen.svg
│ │ │ │ ├── pause.svg
│ │ │ │ ├── picture-in-picture-off.svg
│ │ │ │ ├── picture-in-picture-on.svg
│ │ │ │ ├── play.svg
│ │ │ │ ├── replay-10.svg
│ │ │ │ ├── settings.svg
│ │ │ │ ├── subtitles-off.svg
│ │ │ │ ├── subtitles-on.svg
│ │ │ │ ├── volume-down.svg
│ │ │ │ ├── volume-off.svg
│ │ │ │ └── volume-up.svg
│ │ └── icons.json
│ ├── icons
│ │ ├── cloudinary_icon_for_black_bg.svg
│ │ ├── cloudinary_icon_for_white_bg.svg
│ │ └── info-circle.svg
│ └── styles
│ │ ├── _icons.scss
│ │ ├── components
│ │ ├── loading-button.scss
│ │ ├── text-tracks.scss
│ │ └── title-bar.scss
│ │ ├── main.scss
│ │ ├── mixins
│ │ ├── aspect-ratio.scss
│ │ ├── disable-transition.scss
│ │ └── mixins.scss
│ │ └── variables.scss
├── components
│ ├── component-utils.js
│ ├── index.js
│ ├── jumpButtons
│ │ ├── jump-10-minus.js
│ │ └── jump-10-plus.js
│ ├── logoButton
│ │ ├── logo-button.js
│ │ └── logo-button.scss
│ ├── progress-control-events-blocker
│ │ └── progress-control-events-blocker.js
│ ├── recommendations-overlay
│ │ ├── index.js
│ │ ├── recommendations-overlay-content.js
│ │ ├── recommendations-overlay-hide-button.js
│ │ ├── recommendations-overlay-item.js
│ │ ├── recommendations-overlay-primary-item.js
│ │ ├── recommendations-overlay-secondary-item.js
│ │ ├── recommendations-overlay-secondary-items-container.js
│ │ ├── recommendations-overlay.js
│ │ └── recommendations-overlay.scss
│ ├── shoppable-bar
│ │ ├── layout
│ │ │ ├── bar-layout.js
│ │ │ ├── shoppable-panel-toggle.js
│ │ │ └── shoppable-products-overlay.js
│ │ ├── panel
│ │ │ ├── shoppable-panel-item.js
│ │ │ └── shoppable-panel.js
│ │ ├── shoppable-post-widget.js
│ │ ├── shoppable-widget.const.js
│ │ ├── shoppable-widget.js
│ │ └── shoppable-widget.scss
│ ├── themeButton
│ │ ├── themedButton.const.js
│ │ └── themedButton.js
│ └── title-bar
│ │ └── title-bar.js
├── config
│ └── defaults.js
├── extended-events.js
├── index.all.js
├── index.es.js
├── index.js
├── index.player.js
├── index.videoPlayer.js
├── mixins
│ └── eventable.js
├── player.js
├── plugins
│ ├── adaptive-streaming
│ │ ├── abr-strategies.js
│ │ ├── adaptive-streaming.js
│ │ ├── index.js
│ │ ├── quality-levels.js
│ │ └── videojs-contrib-hlsjs.js
│ ├── ai-highlights-graph
│ │ ├── ai-highlights-graph.scss
│ │ └── index.js
│ ├── analytics
│ │ └── index.js
│ ├── autoplay-on-scroll
│ │ └── index.js
│ ├── chapters
│ │ ├── chapters.js
│ │ ├── chapters.scss
│ │ └── index.js
│ ├── cloudinary-analytics
│ │ └── index.js
│ ├── cloudinary
│ │ ├── common.js
│ │ ├── event-handler-registry.js
│ │ ├── index.js
│ │ └── models
│ │ │ ├── audio-source
│ │ │ ├── audio-source.const.js
│ │ │ └── audio-source.js
│ │ │ ├── base-source.js
│ │ │ ├── image-source.js
│ │ │ └── video-source
│ │ │ ├── video-source.const.js
│ │ │ ├── video-source.js
│ │ │ └── video-source.utils.js
│ ├── colors
│ │ ├── colors.js
│ │ ├── colors.scss
│ │ └── index.js
│ ├── context-menu
│ │ ├── components
│ │ │ ├── context-menu-item.js
│ │ │ └── context-menu.js
│ │ ├── context-menu.scss
│ │ ├── contextMenuContent.js
│ │ ├── index.js
│ │ └── videojs-contextmenu.js
│ ├── floating-player
│ │ ├── floating-player.scss
│ │ └── index.js
│ ├── ima
│ │ ├── ima.js
│ │ └── index.js
│ ├── index.js
│ ├── interaction-areas
│ │ ├── index.js
│ │ ├── interaction-areas.const.js
│ │ ├── interaction-areas.scss
│ │ ├── interaction-areas.service.js
│ │ └── interaction-areas.utils.js
│ ├── paced-transcript
│ │ └── index.js
│ ├── playlist
│ │ ├── index.js
│ │ ├── playlist.js
│ │ ├── ui
│ │ │ ├── components
│ │ │ │ ├── playlist-button.js
│ │ │ │ ├── playlist-buttons.js
│ │ │ │ ├── playlist-buttons.scss
│ │ │ │ ├── playlist-next-button.js
│ │ │ │ ├── playlist-previous-button.js
│ │ │ │ ├── upcoming-video-overlay.js
│ │ │ │ └── upcoming-video-overlay.scss
│ │ │ ├── layout
│ │ │ │ ├── playlist-layout-custom.js
│ │ │ │ ├── playlist-layout-horizontal.js
│ │ │ │ ├── playlist-layout-vertical.js
│ │ │ │ └── playlist-layout.js
│ │ │ ├── panel
│ │ │ │ ├── playlist-panel-item.js
│ │ │ │ └── playlist-panel.js
│ │ │ ├── playlist-widget.js
│ │ │ ├── playlist.const.js
│ │ │ ├── playlist.js
│ │ │ ├── playlist.scss
│ │ │ └── thumbnail
│ │ │ │ ├── thumbnail.js
│ │ │ │ └── thumbnail.scss
│ │ └── utils
│ │ │ ├── api.js
│ │ │ ├── dom.js
│ │ │ └── time.js
│ ├── shoppable-plugin
│ │ └── index.js
│ ├── srt-text-tracks
│ │ ├── index.js
│ │ └── srt-text-tracks.js
│ ├── styled-text-tracks
│ │ ├── index.js
│ │ ├── styled-text-tracks.js
│ │ └── styled-text-tracks.scss
│ ├── visual-search
│ │ ├── components
│ │ │ ├── SearchButton.js
│ │ │ ├── SearchInput.js
│ │ │ └── SearchResults.js
│ │ ├── index.js
│ │ ├── visual-search.js
│ │ └── visual-search.scss
│ └── vtt-thumbnails
│ │ ├── index.js
│ │ └── vtt-thumbnails.scss
├── utils
│ ├── apply-with-props.js
│ ├── attributes-normalizer.js
│ ├── cloudinary.js
│ ├── consts.js
│ ├── css-prefix.js
│ ├── dom.js
│ ├── find.js
│ ├── fontFace.js
│ ├── get-analytics-player-options.js
│ ├── index.js
│ ├── mixin.js
│ ├── object.js
│ ├── positioning.js
│ ├── querystring.js
│ ├── slicing.js
│ ├── time.js
│ └── video-retry.js
├── validators
│ ├── validators-functions.js
│ ├── validators-types.js
│ └── validators.js
├── video-player.const.js
├── video-player.js
└── video-player.utils.js
├── test
├── adaptive-streaming.test.js
├── ads.test.js
├── analytics.test.js
├── api.test.js
├── autoplay.scroll.test.js
├── basic-ui.test.js
├── colors.test.js
├── components.test.js
├── custom-error.test.js
├── e2e
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── components
│ │ ├── BaseComponent.ts
│ │ └── videoComponent.ts
│ ├── fixtures
│ │ └── vpTest.ts
│ ├── playwright.config.ts
│ ├── specs
│ │ ├── ESM
│ │ │ ├── esmAnalyticsPage.spec.ts
│ │ │ ├── esmApiAndEventsPage.spec.ts
│ │ │ ├── esmAudioPlayerPage.spec.ts
│ │ │ ├── esmAutoplayOnScroll.spec.ts
│ │ │ ├── esmChaptersPage.spec.ts
│ │ │ ├── esmCldAnalyticsPage.spec.ts
│ │ │ ├── esmCodecsAndFormatsPage.spec.ts
│ │ │ ├── esmColorsApiPage.spec.ts
│ │ │ ├── esmComponentsPage.spec.ts
│ │ │ ├── esmDisplayConfigurationPage.spec.ts
│ │ │ ├── esmFloatingPlayer.spec.ts
│ │ │ ├── esmFluidLayoutsPage.spec.ts
│ │ │ ├── esmForceHlsSubtitlesPage.spec.ts
│ │ │ ├── esmHighlightsGraphPage.spec.ts
│ │ │ ├── esmMainPageVideoIsPlaying.spec.ts
│ │ │ ├── esmMultiplePlayersPage.spec.ts
│ │ │ ├── esmPlaylistByTag.spec.ts
│ │ │ ├── esmPlaylistPage.spec.ts
│ │ │ ├── esmPosterOptionsPage.spec.ts
│ │ │ ├── esmProfilesPage.spec.ts
│ │ │ ├── esmRawUrlPage.spec.ts
│ │ │ ├── esmRecommendationsPage.spec.ts
│ │ │ ├── esmSeekThumbnailsPage.spec.ts
│ │ │ ├── esmShoppableVideosPage.spec.ts
│ │ │ ├── esmSubtitlesAndCaptionsPage.spec.ts
│ │ │ ├── esmVastAndVpaidPage.spec.ts
│ │ │ ├── esmVideoTransformationsPage.spec.ts
│ │ │ ├── esmVr360Page.spec.ts
│ │ │ └── linksConsoleErrorsEsmPage.spec.ts
│ │ ├── NonESM
│ │ │ ├── analyticsPage.spec.ts
│ │ │ ├── apiAndEventsPage.spec.ts
│ │ │ ├── audioPlayerPage.spec.ts
│ │ │ ├── autoplayOnScrollPage.spec.ts
│ │ │ ├── chaptersPage.spec.ts
│ │ │ ├── cldAnalyticsPage.spec.ts
│ │ │ ├── codecsAndFormats.spec.ts
│ │ │ ├── colorsApiPage.spec.ts
│ │ │ ├── componentsPage.spec.ts
│ │ │ ├── displayConfigurationsPage.spec.ts
│ │ │ ├── floatingPlayerPgae.spec.ts
│ │ │ ├── fluidLayoutsPage.spec.ts
│ │ │ ├── forceHlsSubtitlesPage.spec.ts
│ │ │ ├── highlightsGraphPageVideoIsPlaying.spec.ts
│ │ │ ├── linksConsolErros.spec.ts
│ │ │ ├── mainPageVideoIsPlaying.spec.ts
│ │ │ ├── multiplePlayersPage.spec.ts
│ │ │ ├── playlistByTagPage.spec.ts
│ │ │ ├── playlistPage.spec.ts
│ │ │ ├── posterOptionsPage.spec.ts
│ │ │ ├── profilesPage.spec.ts
│ │ │ ├── rawUrlPage.spec.ts
│ │ │ ├── recommendationsPage.spec.ts
│ │ │ ├── seekThumbnailsPage.spec.ts
│ │ │ ├── shoppableVideosPage.spec.ts
│ │ │ ├── subtitlesAndCaptionsPage.spec.ts
│ │ │ ├── vastAndVpaidPage.spec.ts
│ │ │ ├── videoTransformationsPage.spec.ts
│ │ │ ├── visualSearchPage.spec.ts
│ │ │ └── vr360VideosPage.spec.ts
│ │ ├── Setup
│ │ │ └── global.setup.ts
│ │ └── commonSpecs
│ │ │ ├── apiAndEventsPageVideoPlaying.ts
│ │ │ ├── audioPlayerPageVideoPlaying.ts
│ │ │ ├── autoplayOnScrollVideoPlaying.ts
│ │ │ ├── chaptersPage.ts
│ │ │ ├── cldAnalyticsPageVideoPlaying.ts
│ │ │ ├── codecsAndFormatsVideoPlaying.ts
│ │ │ ├── colorsApiPageVideoPlaying.ts
│ │ │ ├── componentsPageVideoPlaying.ts
│ │ │ ├── displayConfigurationPageVideoPlaying.ts
│ │ │ ├── floatingPlayerPageVideoPlaying.ts
│ │ │ ├── fluidLayoutsPageVideoPlaying.ts
│ │ │ ├── forceHlsSubtitlesPageVideoPlaying.ts
│ │ │ ├── mainPageVideoPlaying.ts
│ │ │ ├── multiplePlayersPageVideoPlaying.ts
│ │ │ ├── playlistByTagPageVideoPlaying.ts
│ │ │ ├── playlistPageVideoPlaying.ts
│ │ │ ├── posterOptionsPage.ts
│ │ │ ├── profilesPageVideoPlaying.ts
│ │ │ ├── rawUrlPageVideoPlaying.ts
│ │ │ ├── recommendationsPageVideoPlaying.ts
│ │ │ ├── seekThumbnailsPageVideoPlaying.ts
│ │ │ ├── shoppableVideosPageVideoPlaying.ts
│ │ │ ├── subtitlesAndCaptionsPgaeVideoPlaying.ts
│ │ │ ├── vastAndVpaidPage.ts
│ │ │ ├── videoTransformationsPage.ts
│ │ │ └── vr360VideosPageVideoPlaying.ts
│ ├── src
│ │ ├── helpers
│ │ │ ├── validatePageErrors.ts
│ │ │ └── waitForPageToLoadWithTimeout.ts
│ │ └── pom
│ │ │ ├── BasePage.ts
│ │ │ ├── PageManager.ts
│ │ │ ├── analyticsPage.ts
│ │ │ ├── apiAndEventsPage.ts
│ │ │ ├── audioPlayerPage.ts
│ │ │ ├── autoplayOnScrollPage.ts
│ │ │ ├── chaptersPage.ts
│ │ │ ├── cldAnalyticsPage.ts
│ │ │ ├── codecsAndFormats.ts
│ │ │ ├── colorsApiPage.ts
│ │ │ ├── componentsPage.ts
│ │ │ ├── displayConfigurationsPage.ts
│ │ │ ├── floatingPlayerPgae.ts
│ │ │ ├── fluidLayoutsPage.ts
│ │ │ ├── forceHlsSubtitlesPage.ts
│ │ │ ├── highlightsGraphPage.ts
│ │ │ ├── mainPage.ts
│ │ │ ├── multiplePlayersPage.ts
│ │ │ ├── playlistByTagPage.ts
│ │ │ ├── playlistPage.ts
│ │ │ ├── posterOptionsPage.ts
│ │ │ ├── profilesPage.ts
│ │ │ ├── rawUrlPage.ts
│ │ │ ├── recommendationsPage.ts
│ │ │ ├── seekThumbnailsPage.ts
│ │ │ ├── shoppableVideosPage.ts
│ │ │ ├── subtitlesAndCaptionsPage.ts
│ │ │ ├── vastAndVpaidPage.ts
│ │ │ ├── videoTransformationsPage.ts
│ │ │ ├── visualSearchPage.ts
│ │ │ └── vr360VideosPage.ts
│ ├── testData
│ │ ├── ExampleLinkNames.ts
│ │ ├── esmPageLinksData.ts
│ │ ├── esmUrl.ts
│ │ └── pageLinksData.ts
│ └── types
│ │ └── exampleLinkType.ts
├── fluid.test.js
├── isValidConfig.test.js
├── mocks
│ ├── cloudinary-core-mock.js
│ └── styleMock.js
├── multiplayer.test.js
├── playlist.test.js
├── puppeteer
│ └── vp-env.js
├── recommendations.test.js
├── title-bar.test.js
├── ui-conf.test.js
└── unit
│ ├── cloudinaryConfig.test.js
│ ├── cloudinaryUtils.test.js
│ └── videoSource.test.js
├── tsconfig.json
├── types
├── cld-video-player-tests.ts
└── cld-video-player.d.ts
└── webpack
├── analyzer.config.js
├── build-utils.js
├── build.config.js
├── common.config.js
├── copy-light-bundle.js
├── dev.config.js
├── es6.config.js
└── puppeteer.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 |
3 | /types/
4 | /test/types/api.test.ts
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | node: true,
5 | commonjs: true,
6 | es6: true,
7 | jest: true
8 | },
9 | extends: ['eslint:recommended'],
10 | parserOptions: {
11 | ecmaVersion: 'latest',
12 | sourceType: 'module'
13 | },
14 |
15 | globals: {
16 | VERSION: true,
17 | page: true,
18 | browser: true
19 | },
20 | rules: {
21 | indent: ['error', 2, { SwitchCase: 1 }],
22 | 'linebreak-style': ['error', 'unix'],
23 | quotes: ['error', 'single'],
24 | semi: ['warn', 'always'],
25 | 'no-console': 'off',
26 | 'no-unused-vars': 'warn',
27 | 'no-useless-escape': 'off'
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @cloudinary/video
2 |
--------------------------------------------------------------------------------
/.github/workflows/deprecate.yml:
--------------------------------------------------------------------------------
1 | name: ⛔ Deprecate
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | tag:
7 | description: 'Tag to update'
8 | required: true
9 | type: choice
10 | options:
11 | - edge
12 | - latest
13 | version:
14 | description: 'Version to deprecate'
15 | required: true
16 | tag_version:
17 | description: 'Version to use instead'
18 | required: true
19 |
20 | jobs:
21 | deprecate:
22 | runs-on: ubuntu-latest
23 |
24 | steps:
25 | - name: Setup Node.js
26 | uses: actions/setup-node@v4
27 | with:
28 | node-version: '20.x'
29 | registry-url: 'https://registry.npmjs.org'
30 |
31 | - name: Deprecate version
32 | run: npm deprecate cloudinary-video-player@${{ github.event.inputs.version }} "This version of cloudinary-video-player has been deprecated."
33 | env:
34 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
35 |
36 | - name: Set tag version
37 | run: npm dist-tag add cloudinary-video-player@${{ github.event.inputs.tag_version }} ${{ github.event.inputs.tag }}
38 | env:
39 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
40 |
--------------------------------------------------------------------------------
/.github/workflows/validate.yml:
--------------------------------------------------------------------------------
1 | name: 🚧 Validate
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | validate:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - name: Checkout code
12 | uses: actions/checkout@v4
13 |
14 | - name: Setup Node.js
15 | uses: actions/setup-node@v3
16 | with:
17 | node-version: '20.x'
18 | registry-url: 'https://registry.npmjs.org'
19 |
20 | - name: Install dependencies
21 | run: npm ci
22 |
23 | - name: Build
24 | run: npm run build-all
25 |
26 | - name: Unit tests
27 | run: npm run test:unit
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE
2 | .idea/
3 | .tern-port
4 | tags
5 |
6 | # nodejs
7 | node_modules/
8 | npm-debug.log
9 | yarn-error.log
10 |
11 | # project
12 | env.js
13 | dist/
14 | lib/
15 | .DS_Store
16 |
17 | \.history/
18 |
19 | # playwright e2e tests
20 |
21 | /test-results/
22 | /playwright-report/
23 | /blob-report/
24 | /playwright/.cache/
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npm run commitlint ${1}
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | save_exact=true
2 | registry=https://registry.npmjs.org
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "singleQuote": true,
4 | "tabWidth": 2,
5 | "useTabs": false,
6 | "trailingComma": "none",
7 | "arrowParens": "avoid"
8 | }
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Cloudinary
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | browsers: ['>0.25%', 'not ie 11', 'not op_mini all']
8 | }
9 | }
10 | ]
11 | ],
12 | env: {
13 | test: {
14 | plugins: ['@babel/plugin-transform-runtime']
15 | }
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ['@commitlint/config-conventional'] };
2 |
--------------------------------------------------------------------------------
/docs/es-modules/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | ignore = "false"
3 |
--------------------------------------------------------------------------------
/docs/es-modules/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cld-vp-es-examples",
3 | "private": true,
4 | "version": "0.0.1",
5 | "scripts": {
6 | "prepare-player": "cd ../../ && npm i && npm run build-all",
7 | "install-player": "npm i ../../ --no-save",
8 | "prepare": "npm run prepare-player && npm run install-player",
9 | "update-edge": "npm i",
10 | "start": "vite",
11 | "build": "vite build",
12 | "preview": "vite preview"
13 | },
14 | "devDependencies": {
15 | "vite": "^6.0.2"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docs/es-modules/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import { resolve } from 'path';
3 | import fs from 'fs';
4 |
5 |
6 | export default defineConfig({
7 | build: {
8 | rollupOptions: {
9 | input: fs.readdirSync(resolve(__dirname)).filter(file => file.endsWith('.html'))
10 | }
11 | }
12 | });
13 |
--------------------------------------------------------------------------------
/env.example.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | devServer: {
3 | host: 'localhost',
4 | port: 3000
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/jest-puppeteer.config.js:
--------------------------------------------------------------------------------
1 | // workaround for find-process sudo
2 | global.process.getuid = () => 0;
3 | module.exports = {
4 | server: {
5 | command: 'node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack/puppeteer.config.js',
6 | port: 3000,
7 | launchTimeout: 200000
8 | },
9 | launch: {
10 | headless: true
11 | },
12 | browserContext: 'default'
13 |
14 | };
15 |
--------------------------------------------------------------------------------
/src/assets/icon-font/README.md:
--------------------------------------------------------------------------------
1 | # Cloudinary Video Player Icons
2 |
3 | ## How to generate an updated icon-font
4 |
5 | Use the utility from https://github.com/videojs/font with the custom icons in the `cld` folder and the configuration in the `icons.json` provided here.
6 |
7 | MUI3 icons can be found [here](https://github.com/google/material-design-icons/blob/3.0.2/sprites/css-sprite/sprite-action-black.png).
8 |
9 | Copy the generated `videojs-icons.scss` file to the `styles` folder.
10 |
--------------------------------------------------------------------------------
/src/assets/icon-font/VideoJS.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudinary/cloudinary-video-player/847f64997b6c8527858e16c45f718fcf9da2ff46/src/assets/icon-font/VideoJS.ttf
--------------------------------------------------------------------------------
/src/assets/icon-font/VideoJS.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudinary/cloudinary-video-player/847f64997b6c8527858e16c45f718fcf9da2ff46/src/assets/icon-font/VideoJS.woff
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/check.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/forward-10.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/fullscreen-exit.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/fullscreen.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/pause.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/picture-in-picture-off.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/picture-in-picture-on.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/play.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/replay-10.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/settings.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/subtitles-off.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/subtitles-on.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/volume-down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/volume-off.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-font/custom-icons/cld/volume-up.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/cloudinary_icon_for_black_bg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/cloudinary_icon_for_white_bg.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/icons/info-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
--------------------------------------------------------------------------------
/src/assets/styles/components/loading-button.scss:
--------------------------------------------------------------------------------
1 | $border-radius : 4px;
2 |
3 | .cld-video-player {
4 |
5 | .vp-theme-button {
6 | position: relative;
7 | height: 40px;
8 | font-size: 16px;
9 | border-radius: $border-radius;
10 | padding: 0 20px;
11 | overflow: hidden;
12 |
13 | &.theme-transparent-white {
14 | background-color: rgba(255, 255, 254, 0.18);
15 |
16 | &:hover {
17 | background-color: rgba(255, 255, 254, 0.28);
18 | }
19 |
20 | .vp-loading-bar {
21 | background-color: rgba(255, 255, 254, 0.22);
22 | }
23 |
24 | }
25 |
26 | .vp-loading-bar {
27 | position: absolute;
28 | top:0;
29 | left: 0;
30 | height: 100%;
31 | width: 100%;
32 | transition: width;
33 | animation-name: loading;
34 | }
35 |
36 | &:hover{
37 | cursor: pointer;
38 | }
39 |
40 | }
41 |
42 | }
43 |
44 |
45 | @keyframes loading {
46 | from { width: 0; }
47 | to { width: 100%; }
48 | }
49 |
--------------------------------------------------------------------------------
/src/assets/styles/components/text-tracks.scss:
--------------------------------------------------------------------------------
1 | .cld-video-player {
2 | // Default captions styles
3 | // See styled-text-tracks plugin for more options
4 | .vjs-text-track-display {
5 | // When control-bar is shown
6 | bottom: 5em;
7 | z-index: 2;
8 | // Word highlight
9 | &.cld-paced-text-tracks b {
10 | color: var(--color-accent);
11 | }
12 | }
13 | &.vjs-controls-disabled .vjs-text-track-display,
14 | &.vjs-user-inactive.vjs-playing .vjs-text-track-display {
15 | // When control-bar is hidden/disabled
16 | bottom: 1em;
17 | }
18 |
19 | .vjs-text-track-cue {
20 | max-width: 100%;
21 | > div {
22 | display: inline-block !important;
23 | padding: 0.1em 0.3em;
24 | background-color: rgba(0, 0, 0, 0.5) !important;
25 | }
26 | }
27 |
28 | // Word highlight - default style
29 | .cld-paced-text-tracks .vjs-text-track-cue b {
30 | color: var(--color-accent);
31 | }
32 |
33 | // Default caption styles, when not configured to `videojs-default` theme
34 | .vjs-text-track-display:not(.cld-styled-text-tracks-theme-videojs-default) {
35 | .vjs-text-track-cue {
36 | font-family: inherit !important;
37 | > div {
38 | font-weight: 700;
39 | background-color: transparent !important;
40 | text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.8);
41 | border-radius: 0.2em;
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/assets/styles/mixins/aspect-ratio.scss:
--------------------------------------------------------------------------------
1 | @use "sass:math";
2 |
3 | @mixin aspect-ratio($width, $height) {
4 | &:before {
5 | display: block;
6 | content: "";
7 | width: 100%;
8 | padding-top: math.div($height, $width) * 100%;
9 | }
10 |
11 | > .aspect-ratio-content {
12 | position: absolute;
13 | top: 0;
14 | left: 0;
15 | right: 0;
16 | bottom: 0;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/assets/styles/mixins/disable-transition.scss:
--------------------------------------------------------------------------------
1 | &.disable-transition {
2 | transition: visibility 0s;
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/styles/mixins/mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin ellipsis {
2 | white-space: nowrap;
3 | overflow: hidden;
4 | text-overflow: ellipsis;
5 | }
--------------------------------------------------------------------------------
/src/assets/styles/variables.scss:
--------------------------------------------------------------------------------
1 | $text-font-family: Arial, Helvetica, sans-serif;
2 | $center-big-play-button: true;
3 |
--------------------------------------------------------------------------------
/src/components/component-utils.js:
--------------------------------------------------------------------------------
1 | const hide = (el) => {
2 | el.style.display = 'none';
3 | };
4 |
5 | const show = (el) => {
6 | el.style.display = '';
7 | };
8 |
9 | const setText = (el, text) => {
10 | if (!text || text.length <= 0) {
11 | el.innerText = '';
12 | hide(el);
13 | return;
14 | }
15 |
16 | el.innerText = text;
17 | show(el);
18 | };
19 |
20 | module.exports = { hide, show, setText };
21 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import JumpForwardButton from './jumpButtons/jump-10-plus';
2 | import JumpBackButton from './jumpButtons/jump-10-minus';
3 | import LogoButton from './logoButton/logo-button';
4 | import ProgressControlEventsBlocker from './progress-control-events-blocker/progress-control-events-blocker';
5 | import TitleBar from './title-bar/title-bar';
6 |
7 | export {
8 | JumpForwardButton,
9 | JumpBackButton,
10 | LogoButton,
11 | ProgressControlEventsBlocker,
12 | TitleBar
13 | };
14 |
--------------------------------------------------------------------------------
/src/components/jumpButtons/jump-10-minus.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 |
3 | const ClickableComponent = videojs.getComponent('ClickableComponent');
4 |
5 | class JumpBackButton extends ClickableComponent {
6 |
7 | handleClick(event) {
8 | super.handleClick(event);
9 | this.player().currentTime(this.player().currentTime() - 10);
10 | }
11 |
12 | createEl() {
13 | return videojs.dom.createEl('button', {
14 | className: 'vjs-control vjs-icon-skip-10-min vjs-icon-replay-10 vjs-button',
15 | ariaLabel: 'Jump back 10 seconds'
16 | });
17 | }
18 | }
19 |
20 | videojs.registerComponent('JumpBackButton', JumpBackButton);
21 |
22 | export default JumpBackButton;
23 |
--------------------------------------------------------------------------------
/src/components/jumpButtons/jump-10-plus.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 |
3 | const ClickableComponent = videojs.getComponent('ClickableComponent');
4 |
5 | class JumpForwardButton extends ClickableComponent {
6 | handleClick(event) {
7 | super.handleClick(event);
8 | this.player().currentTime(this.player().currentTime() + 10);
9 | }
10 |
11 | createEl() {
12 | return videojs.dom.createEl('button', {
13 | className: 'vjs-control vjs-icon-skip-10-plus vjs-icon-forward-10 vjs-button',
14 | ariaLabel: 'Jump forward 10 seconds'
15 | });
16 | }
17 | }
18 |
19 | videojs.registerComponent('JumpForwardButton', JumpForwardButton);
20 |
21 | export default JumpForwardButton;
22 |
--------------------------------------------------------------------------------
/src/components/logoButton/logo-button.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 | import './logo-button.scss';
3 |
4 | // support VJS5 & VJS6 at the same time
5 | const ClickableComponent = videojs.getComponent('ClickableComponent');
6 |
7 | class LogoButton extends ClickableComponent {
8 |
9 | createEl() {
10 | const opts = this.options_.playerOptions;
11 | const display = opts.showLogo ? 'block' : 'none';
12 |
13 | const bgImage = opts.logoImageUrl ? `background-image: url(${opts.logoImageUrl})` : '';
14 |
15 | return videojs.dom.createEl('a', {}, {
16 | class: 'vjs-control vjs-cloudinary-button vjs-button',
17 | href: opts.logoOnclickUrl,
18 | target: '_blank',
19 | style: `display: ${display}; ${bgImage}`,
20 | 'aria-label': 'Logo link'
21 | });
22 | }
23 | }
24 |
25 | videojs.registerComponent('logoButton', LogoButton);
26 |
27 | export default LogoButton;
28 |
--------------------------------------------------------------------------------
/src/components/logoButton/logo-button.scss:
--------------------------------------------------------------------------------
1 | // Player Cloudinary button
2 | .vjs-control-bar a.vjs-control.vjs-cloudinary-button {
3 | background-image: url("../../assets/icons/cloudinary_icon_for_black_bg.svg");
4 | background-size: 25px;
5 | background-position: center;
6 | background-repeat: no-repeat;
7 | color: inherit;
8 |
9 | .cld-video-player-skin-light & {
10 | background-image: url("../../assets/icons/cloudinary_icon_for_white_bg.svg");
11 | }
12 |
13 | &:hover {
14 | cursor: pointer;
15 | }
16 |
17 | &:last-child {
18 | margin-right: 0.4em;
19 | margin-left: 0.8em;
20 | &::before {
21 | content: '';
22 | position: absolute;
23 | left: -0.25em;
24 | top: 0.3em;
25 | bottom: 0.3em;
26 | border-left: 1px solid currentColor;
27 | opacity: 0.25;
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/progress-control-events-blocker/progress-control-events-blocker.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 | const Component = videojs.getComponent('Component');
3 |
4 | class ProgressControlEventsBlocker extends Component {
5 |
6 | constructor(player, options = {}) {
7 | super(player, options);
8 | }
9 |
10 | createEl() {
11 | return super.createEl('div', {
12 | className: 'vjs-progress-control-events-blocker'
13 | });
14 | }
15 | }
16 |
17 | videojs.registerComponent('progressControlEventsBlocker', ProgressControlEventsBlocker);
18 |
19 | export default ProgressControlEventsBlocker;
20 |
--------------------------------------------------------------------------------
/src/components/recommendations-overlay/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyRecommendationsOverlayComponent(player) {
2 | try {
3 | if (!player.getChild('recommendationsOverlay')) {
4 | await import(/* webpackChunkName: "recommendations-overlay" */ './recommendations-overlay');
5 | player.addChild('recommendationsOverlay');
6 | }
7 | return player;
8 | } catch (error) {
9 | console.error('Failed to load plugin:', error);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/recommendations-overlay/recommendations-overlay-hide-button.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 |
3 | const ClickableComponent = videojs.getComponent('ClickableComponent');
4 |
5 | class RecommendationOverlayHideButton extends ClickableComponent {
6 |
7 | createEl() {
8 | return super.createEl('span', {
9 | className: 'vjs-recommendations-overlay-hide vjs-icon-close'
10 | });
11 | }
12 |
13 | handleClick() {
14 | this.options_.clickHandler();
15 | }
16 | }
17 |
18 | export default RecommendationOverlayHideButton;
19 |
--------------------------------------------------------------------------------
/src/components/recommendations-overlay/recommendations-overlay-item.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 |
3 | const ClickableComponent = videojs.getComponent('ClickableComponent');
4 |
5 | class RecommendationsOverlayItem extends ClickableComponent {
6 |
7 | setItem(item) {
8 | const { action, source } = item;
9 | this.source = source;
10 |
11 | const info = source.info();
12 |
13 | this.setTitle(info.title || source.publicId());
14 |
15 | this.setPoster(this.source.poster().url({ transformation: { aspect_ratio: '16:9', crop: 'pad', background: 'black' } }));
16 |
17 | this.setAction(action);
18 | }
19 |
20 | setTitle(text) {
21 | this.title.innerText = text;
22 | }
23 |
24 | setAction(action) {
25 | this.action = action;
26 | }
27 |
28 | handleClick() {
29 | super.handleClick();
30 | this.player().trigger('recommendationshide');
31 | this.action();
32 | }
33 | }
34 |
35 | export default RecommendationsOverlayItem;
36 |
--------------------------------------------------------------------------------
/src/components/recommendations-overlay/recommendations-overlay-secondary-item.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 | import RecommendationsOverlayItem from './recommendations-overlay-item';
3 |
4 | // support VJS5 & VJS6 at the same time
5 | const dom = videojs.dom || videojs;
6 |
7 | class RecommendationsOverlaySecondaryItem extends RecommendationsOverlayItem {
8 |
9 | setItem(item) {
10 | super.setItem(item);
11 | this.setDuration('');
12 | }
13 |
14 | setPoster(url) {
15 | this.el().style.backgroundImage = `url('${url}')`;
16 | }
17 |
18 | setDuration(text) {
19 | this.duration.innerText = text;
20 | }
21 |
22 | createEl() {
23 | const el = super.createEl('div', {
24 | className: 'vjs-recommendations-overlay-item vjs-recommendations-overlay-item-secondary'
25 | });
26 |
27 | this.title = dom.createEl('span', { className: 'vjs-recommendations-overlay-item-secondary-title' });
28 | this.title.innerHTML = '';
29 |
30 | this.duration = dom.createEl('span', { className: 'vjs-recommendations-overlay-item-secondary-duration' });
31 | this.duration.innerHTML = '';
32 |
33 | const caption = dom.createEl('div', { className: 'vjs-recommendations-overlay-item-info' });
34 | caption.appendChild(this.title);
35 | caption.appendChild(this.duration);
36 |
37 | el.appendChild(caption);
38 |
39 | return el;
40 | }
41 |
42 | handleClick() {
43 | super.handleClick();
44 | this.action();
45 | }
46 | }
47 |
48 | export default RecommendationsOverlaySecondaryItem;
49 |
--------------------------------------------------------------------------------
/src/components/recommendations-overlay/recommendations-overlay-secondary-items-container.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 | import RecommendationsOverlaySecondaryItem from './recommendations-overlay-secondary-item';
3 |
4 | const Component = videojs.getComponent('Component');
5 |
6 | class RecommendationsOverlaySecondaryItemsContainer extends Component {
7 |
8 | setItems(...items) {
9 | this.clearItems();
10 |
11 | if (!items) {
12 | return;
13 | }
14 |
15 | items.forEach((item) => {
16 | const component = new RecommendationsOverlaySecondaryItem(this.player());
17 | component.setItem(item);
18 | this.addChild(component);
19 | });
20 | }
21 |
22 | clearItems() {
23 | this.children().forEach(() => {
24 | this.removeChild(this.children()[0]);
25 | });
26 | }
27 |
28 | createEl() {
29 | return super.createEl('div', {
30 | className: 'vjs-recommendations-overlay-item-secondary-container'
31 | });
32 | }
33 | }
34 |
35 | export default RecommendationsOverlaySecondaryItemsContainer;
36 |
--------------------------------------------------------------------------------
/src/components/shoppable-bar/shoppable-widget.const.js:
--------------------------------------------------------------------------------
1 | export const SHOPPABLE_WIDGET_OPTIONS_DEFAULTS = {
2 | location: 'right',
3 | toggleIcon: '',
4 | width: '20%',
5 | startState: 'openOnPlay',
6 | autoClose: 2,
7 | transformation: {
8 | quality: 'auto',
9 | width: 'auto',
10 | fetch_format: 'auto',
11 | crop: 'scale'
12 | },
13 | products: [],
14 | showPostPlayOverlay: false
15 | };
16 |
17 | export const SHOPPABLE_CLICK_ACTIONS = {
18 | GO_TO: 'goto',
19 | SEEk: 'seek'
20 | };
21 |
22 | export const SHOPPABLE_HOVER_ACTIONS = {
23 | OVERLAY: 'overlay'
24 | };
25 |
26 | export const SHOPPABLE_PANEL_VISIBLE_CLASS = 'shoppable-panel-visible';
27 |
28 | export const SHOPPABLE_PANEL_HIDDEN_CLASS = 'shoppable-panel-hidden';
29 |
30 | export const SHOPPABLE_PRODUCTS_OVERLAY_CLASS = 'shoppable-products-overlay';
31 |
32 | export const CLD_SPBL_PANEL_CLASS = 'cld-spbl-panel';
33 |
34 | export const CLD_SPBL_TOGGLE_CLASS = 'cld-spbl-toggle';
35 |
36 | export const CLD_SPBL_TOGGLE_ICON_CLASS = 'cld-spbl-toggle-icon';
37 |
38 | export const CLD_SPBL_INNER_BAR = 'cld-spbl-bar-inner';
39 |
40 | export const CLD_SPBL_TOGGLE_CUSTOM_ICON_CLASS = 'cld-spbl-toggle-custom-icon';
41 |
42 | export const ICON_CART_CLASS = 'vjs-icon-cart';
43 |
44 | export const CLOSE_ICON_CLASS = 'vjs-icon-close';
45 |
46 | export const SHOPPABLE_ANIMATION_CLASS = 'animate';
47 |
48 | export const CLD_SPBL_ITEM = 'cld-spbl-item';
49 |
50 | export const CLD_SPBL_IMAGE = 'cld-spbl-img';
51 |
--------------------------------------------------------------------------------
/src/components/themeButton/themedButton.const.js:
--------------------------------------------------------------------------------
1 | export const BUTTON_THEME = {
2 | TRANSPARENT_WHITE: 'transparent-white'
3 | };
4 |
--------------------------------------------------------------------------------
/src/components/themeButton/themedButton.js:
--------------------------------------------------------------------------------
1 | import { elementsCreator } from '../../utils/dom';
2 |
3 |
4 | export const themedButton = ({ text, onClick, theme = '', loadingDelay = 0 }) => {
5 | return elementsCreator({
6 | tag: 'button',
7 | attr: { class: `vp-theme-button theme-${theme}` },
8 | onClick,
9 | children: [
10 | {
11 | tag: 'div',
12 | attr: { class: 'vp-loading-bar' },
13 | style: {
14 | 'animation-duration': `${loadingDelay}ms`
15 | }
16 | },
17 | {
18 | tag: 'div',
19 | attr: { class: 'content' },
20 | children: text
21 | }
22 | ]
23 | });
24 | };
25 |
26 |
--------------------------------------------------------------------------------
/src/config/defaults.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 | import contextMenuContent from '../plugins/context-menu/contextMenuContent';
3 | import { FLOATING_TO, PRELOAD } from '../video-player.const';
4 |
5 | export default {
6 | logoOnclickUrl: 'https://cloudinary.com/',
7 | showLogo: true,
8 | showJumpControls: false,
9 | playsinline: videojs.browser.IS_IOS,
10 | skin: 'dark',
11 | controls: false,
12 | chaptersButton: false,
13 | pictureInPictureToggle: false,
14 | seekThumbnails: true,
15 | aiHighlightsGraph: false,
16 | visualSearch: false,
17 | preload: PRELOAD.AUTO,
18 | textTrackSettings: false,
19 | loop: false,
20 | muted: false,
21 | posterOptions: {},
22 | sourceTypes: ['auto'],
23 | contextMenu: {
24 | content: contextMenuContent
25 | },
26 | floatingWhenNotVisible: FLOATING_TO.NONE,
27 | hideContextMenu: false,
28 | analytics: false,
29 | cloudinaryAnalytics: true,
30 | allowUsageReport: true,
31 | playedEventPercents: [25, 50, 75, 100],
32 | adaptiveStreaming: {
33 | strategy: 'balanced',
34 | },
35 | html5: {
36 | handlePartialData: false,
37 | nativeTextTracks: false
38 | },
39 | disableSeekWhileScrubbingOnMobile: true
40 | };
41 |
--------------------------------------------------------------------------------
/src/index.all.js:
--------------------------------------------------------------------------------
1 | // This file is bundled as `all.js` to be imported as a single module that includes all plugins.
2 |
3 | // Usage:
4 | // import { videoPlayer, videoPlayers } from 'cloudinary-video-player/all';
5 | // Or:
6 | // import cloudinary from 'cloudinary-video-player/all';
7 |
8 | import cloudinary from './index.js';
9 |
10 | export * from './index.js';
11 | export * from './plugins/adaptive-streaming/adaptive-streaming.js';
12 | export * from './plugins/chapters/chapters.js';
13 | export * from './plugins/colors/colors.js';
14 | export * from './plugins/ima/ima.js';
15 | export * from './plugins/playlist/playlist.js';
16 | export * from './plugins/interaction-areas/interaction-areas.service.js';
17 | export * from './plugins/visual-search/visual-search.js';
18 | export * from './plugins/srt-text-tracks/srt-text-tracks.js';
19 | export * from './components/shoppable-bar/shoppable-widget.js';
20 | export * from './components/recommendations-overlay/recommendations-overlay.js';
21 |
22 | export default cloudinary;
23 |
--------------------------------------------------------------------------------
/src/index.es.js:
--------------------------------------------------------------------------------
1 | // This file is bundled as `cld-video-player.js` to be imported as a tree-shaken module.
2 | // It is the default export of the Cloudinary Video Player.
3 |
4 | // Usage:
5 | // import cloudinary from 'cloudinary-video-player';
6 | // Or:
7 | // import { videoPlayer } from "cloudinary-video-player";
8 |
9 | // Other modules can be imported like that:
10 | // import dash from 'cloudinary-video-player/dash';
11 |
12 | import cloudinary from './index.js';
13 |
14 | export const videoPlayer = cloudinary.videoPlayer;
15 | export const videoPlayers = cloudinary.videoPlayers;
16 |
17 | export const player = cloudinary.player;
18 |
19 | export default cloudinary;
20 |
--------------------------------------------------------------------------------
/src/index.player.js:
--------------------------------------------------------------------------------
1 | // This file is bundled as `player.js` to be imported as a tree-shaken module.
2 |
3 | // Usage:
4 | // import player from 'cloudinary-video-player/player';
5 |
6 | export { player as default } from './index.js';
7 |
--------------------------------------------------------------------------------
/src/index.videoPlayer.js:
--------------------------------------------------------------------------------
1 | // This file is bundled as `videoPlayer.js` to be imported as a tree-shaken module.
2 |
3 | // Usage:
4 | // import videoPlayer from 'cloudinary-video-player/videoPlayer';
5 |
6 | export { videoPlayer as default } from './index.js';
7 |
--------------------------------------------------------------------------------
/src/mixins/eventable.js:
--------------------------------------------------------------------------------
1 | const Eventable = (superclass) => class extends superclass {
2 | constructor() {
3 | super();
4 |
5 | const eventable = { data: {}, handlers: {} };
6 |
7 | this.on = (...args) => {
8 | const lastIndex = args.length - 1;
9 | const func = args[lastIndex];
10 |
11 | eventable.handlers[func] = (event, ..._args) => {
12 | event.Player = this;
13 | func(event, ..._args);
14 | };
15 |
16 | args[lastIndex] = eventable.handlers[func];
17 |
18 | return this.videojs.on(...args);
19 | };
20 |
21 | this.one = (...args) => {
22 | const lastIndex = args.length - 1;
23 | const func = args[lastIndex];
24 |
25 | eventable.handlers[func] = (event, ..._args) => {
26 | event.Player = this;
27 | func(event, ..._args);
28 | delete eventable.handlers[func];
29 | };
30 |
31 | args[lastIndex] = eventable.handlers[func];
32 |
33 | return this.videojs.one(...args);
34 | };
35 |
36 | this.off = (...args) => {
37 | const lastIndex = args.length - 1;
38 | const func = args[lastIndex];
39 |
40 | args[lastIndex] = eventable.handlers[func];
41 |
42 | const res = this.videojs.off(...args);
43 | delete eventable.handlers[func];
44 |
45 | return res;
46 | };
47 |
48 | this.trigger = (...args) => {
49 | this.videojs.trigger(...args);
50 | };
51 | }
52 | };
53 |
54 | export default Eventable;
55 |
--------------------------------------------------------------------------------
/src/plugins/adaptive-streaming/abr-strategies.js:
--------------------------------------------------------------------------------
1 | export const abrStrategies = {
2 | fastStart: {
3 | capLevelToPlayerSize: true,
4 | ignoreDevicePixelRatio: true,
5 | maxDevicePixelRatio: 2,
6 | abrEwmaDefaultEstimate: 4194304,
7 | abrEwmaDefaultEstimateMax: 4194304,
8 | enableWorker: false,
9 | startLevel: 0
10 | },
11 | balanced: {
12 | capLevelToPlayerSize: true,
13 | ignoreDevicePixelRatio: true,
14 | maxDevicePixelRatio: 2,
15 | abrEwmaDefaultEstimate: 4194304,
16 | abrEwmaDefaultEstimateMax: 4194304,
17 | enableWorker: false
18 | },
19 | highQuality: {
20 | capLevelToPlayerSize: true,
21 | ignoreDevicePixelRatio: false,
22 | maxDevicePixelRatio: 2,
23 | abrEwmaDefaultEstimate: 4194304,
24 | abrEwmaDefaultEstimateMax: 4194304,
25 | enableWorker: false
26 | }
27 | };
28 |
29 | export const ADAPTIVE_STREAMING_STRATEGY = Object.keys(abrStrategies);
30 |
31 | export const hdrSupported = window.matchMedia && window.matchMedia('(dynamic-range: high)').matches;
32 |
--------------------------------------------------------------------------------
/src/plugins/adaptive-streaming/adaptive-streaming.js:
--------------------------------------------------------------------------------
1 | import 'hls.js';
2 | import 'videojs-contrib-quality-levels';
3 | import 'videojs-contrib-quality-menu';
4 | import './videojs-contrib-hlsjs';
5 | import { qualityLevels } from './quality-levels';
6 | import { abrStrategies, hdrSupported } from './abr-strategies';
7 |
8 | export default async function adaptiveStreamingPlugin(player, options) {
9 | const config = {
10 | ...abrStrategies[options.strategy],
11 | videoPreference: hdrSupported ? { preferHDR: true } : undefined
12 | };
13 | player.tech_.options_.hlsjsConfig = config;
14 | player.on('loadstart', () => qualityLevels(player, options).init());
15 | player.qualityMenu();
16 | player.adaptiveStreamingLoaded = true;
17 | }
18 |
--------------------------------------------------------------------------------
/src/plugins/adaptive-streaming/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyAdaptiveStreamingPlugin(options) {
2 | const player = this;
3 |
4 | try {
5 | if (options.isDash) {
6 | await import(/* webpackChunkName: "dash" */ 'videojs-contrib-dash');
7 | }
8 | await import(/* webpackChunkName: "adaptive-streaming" */ './adaptive-streaming');
9 | const { default: abrPlugin } = await import(/* webpackChunkName: "adaptive-streaming" */ './adaptive-streaming');
10 | abrPlugin(player, options);
11 | } catch (error) {
12 | console.error('Failed to load plugin:', error);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/plugins/ai-highlights-graph/ai-highlights-graph.scss:
--------------------------------------------------------------------------------
1 | .cld-video-player {
2 | .vjs-highlights-graph-display {
3 | position: absolute;
4 | left: 0;
5 | right: 0;
6 | bottom: 50%;
7 | z-index: 0;
8 | pointer-events: none;
9 | opacity: 0;
10 | transition: opacity 0.2s;
11 | svg {
12 | width: 100%;
13 | height: 100%;
14 | }
15 | path {
16 | fill: currentColor;
17 | }
18 | }
19 |
20 | .vjs-progress-control:hover .vjs-highlights-graph-display {
21 | opacity: 0.8;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/plugins/chapters/chapters.scss:
--------------------------------------------------------------------------------
1 | .cld-video-player {
2 | .vjs-control-bar-chapter-wrapper {
3 | display: flex;
4 | align-items: center;
5 | container-type: inline-size;
6 | }
7 | .vjs-control-bar-chapter-display {
8 | line-height: 1.5;
9 | font-size: 90%;
10 | white-space: nowrap;
11 | overflow: hidden;
12 | text-overflow: ellipsis;
13 | padding: 0 0.5em;
14 | &:not(:empty)::before {
15 | content: '•';
16 | padding-right: 0.5em;
17 | }
18 | @container (max-width: 150px) {
19 | display: none;
20 | }
21 | }
22 |
23 | .vjs-chapter-marker {
24 | pointer-events: none;
25 | position: absolute;
26 | background: var(--color-base);
27 | width: 4px;
28 | top: 0;
29 | bottom: 0;
30 | opacity: 0.5;
31 | z-index: 1;
32 | }
33 |
34 | .vjs-chapter-display {
35 | pointer-events: none;
36 | line-height: 1.5;
37 | font-size: 90%;
38 | white-space: nowrap;
39 | overflow: hidden;
40 | text-overflow: ellipsis;
41 | transform: translateX(-50%);
42 | bottom: 2.7em;
43 | position: absolute;
44 | padding: 0 0.3em;
45 | font-weight: bold;
46 | text-shadow: 0 0 8px var(--color-base), 0 0 1px var(--color-base), 0 0 1px var(--color-base);
47 | &:not(:empty) ~ .vjs-vtt-thumbnail-display {
48 | bottom: 4em;
49 | }
50 | }
51 |
52 | .vjs-time-tooltip {
53 | right: auto !important;
54 | translate: -50%;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/plugins/chapters/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyChaptersPlugin(options) {
2 | const player = this;
3 | try {
4 | const { default: initPlugin } = await import(/* webpackChunkName: "chapters" */ './chapters');
5 | player.ready(() => initPlugin(options, player));
6 | } catch (error) {
7 | console.error('Failed to load plugin:', error);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugins/cloudinary/event-handler-registry.js:
--------------------------------------------------------------------------------
1 | class EventHandlerRegistry {
2 | constructor(emitter) {
3 | this._emitter = emitter;
4 | this._eventHandlers = [];
5 | }
6 |
7 | on(type, handler) {
8 | this._eventHandlers.push({ type, handler });
9 | this._emitter.on(type, handler);
10 | }
11 |
12 | one(type, handler) {
13 | const wrapper = (...args) => {
14 | handler(...args);
15 | this.off(type, handler);
16 | };
17 |
18 | this._eventHandlers.push({ type, handler, wrapper });
19 | this._emitter.one(type, handler);
20 | }
21 |
22 | off(type, handler) {
23 | const index = this._eventHandlers?.findIndex((event) =>
24 | event.type === type && event.handler === handler);
25 |
26 | if (index === -1) {
27 | return;
28 | }
29 |
30 | const event = this._eventHandlers[index];
31 |
32 | this._emitter.off(type, event.wrapper || event.handler);
33 |
34 | this._eventHandlers.splice(index, 1);
35 | }
36 |
37 | removeAllListeners() {
38 | this._eventHandlers.forEach((event) => {
39 | this.off(event);
40 | });
41 | }
42 | }
43 |
44 | export default EventHandlerRegistry;
45 |
--------------------------------------------------------------------------------
/src/plugins/cloudinary/models/audio-source/audio-source.const.js:
--------------------------------------------------------------------------------
1 | export const DEFAULT_POSTER_PARAMS = { format: 'jpg', resource_type: 'video', transformation: { flags: 'waveform' } };
2 |
3 | export const COMMON_AUDIO_FORMATS = ['mp3', 'ogg', 'wav', 'mp4'];
4 |
5 | export const AUDIO_SUFFIX_REMOVAL_PATTERN = RegExp(`\\.(${COMMON_AUDIO_FORMATS.join('|')})$$`);
6 |
7 | export const DEFAULT_AUDIO_PARAMS = {
8 | resource_type: 'video',
9 | type: 'upload',
10 | transformation: []
11 | };
12 |
--------------------------------------------------------------------------------
/src/plugins/cloudinary/models/image-source.js:
--------------------------------------------------------------------------------
1 | import BaseSource from './base-source';
2 | import { normalizeOptions } from '../common';
3 |
4 | const COMMON_IMAGE_FORMATS = ['jpg', 'png', 'gif', 'webp'];
5 | const IMAGE_SUFFIX_REMOVAL_PATTERN = RegExp(`\\.(${COMMON_IMAGE_FORMATS.join('|')})$$`);
6 | const DEFAULT_IMAGE_PARAMS = {
7 | resource_type: 'image',
8 | type: 'upload',
9 | transformation: []
10 | };
11 |
12 | class ImageSource extends BaseSource {
13 | constructor(publicId, options = {}) {
14 | ({ publicId, options } = normalizeOptions(publicId, options));
15 |
16 | publicId = publicId.replace(IMAGE_SUFFIX_REMOVAL_PATTERN, '');
17 |
18 | options = Object.assign({}, DEFAULT_IMAGE_PARAMS, options);
19 |
20 | super(publicId, options);
21 | this._type = 'ImageSource';
22 | }
23 | }
24 |
25 | export default ImageSource;
26 |
--------------------------------------------------------------------------------
/src/plugins/cloudinary/models/video-source/video-source.utils.js:
--------------------------------------------------------------------------------
1 | import { CONTAINER_MIME_TYPES, FORMAT_MAPPINGS } from './video-source.const';
2 | import { VIDEO_CODEC } from '../../common';
3 | import isString from 'lodash/isString';
4 | import { isKeyInTransformation } from 'utils/cloudinary';
5 |
6 | export function formatToMimeTypeAndTransformation(format) {
7 | const [container, codec] = format.toLowerCase().split('/');
8 | const mimetype = CONTAINER_MIME_TYPES[container] || `video/${container}`;
9 | let result = [mimetype];
10 |
11 | if (codec) {
12 | const transformation = { video_codec: codec };
13 | result = [mimetype, transformation];
14 | }
15 |
16 | return result;
17 | }
18 |
19 | export function normalizeFormat(format) {
20 | format = format.toLowerCase().split('/').shift();
21 |
22 | let res = FORMAT_MAPPINGS[format];
23 | if (!res) {
24 | res = format.split('/').shift();
25 | }
26 |
27 | return res;
28 | }
29 |
30 | const strIncludesCodec = value =>
31 | value && Object.values(VIDEO_CODEC).some(codec => value.includes(codec));
32 |
33 | const hasCodecTrans = transformations =>
34 | ['video_codec', 'streaming_profile'].some(key => isKeyInTransformation(transformations, key));
35 |
36 | export const hasCodec = (transformations) => {
37 | if (!transformations) {
38 | return false;
39 | }
40 |
41 | if (isString(transformations)) {
42 | return strIncludesCodec(transformations);
43 | }
44 |
45 | if (hasCodecTrans(transformations)) {
46 | return true;
47 | }
48 |
49 | return !!transformations.some?.(transformation => {
50 | return hasCodec(transformation);
51 | });
52 | };
53 |
--------------------------------------------------------------------------------
/src/plugins/colors/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyColorsPlugin(options) {
2 | const player = this;
3 | try {
4 | const { default: colorsPlugin } = await import(/* webpackChunkName: "colors" */ './colors');
5 | const colors = colorsPlugin(player, options);
6 | player.cloudinary.colors = colors;
7 | return colors;
8 | } catch (error) {
9 | console.error('Failed to load colors plugin:', error);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/plugins/context-menu/components/context-menu-item.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 | import { createElement } from 'utils/dom';
3 |
4 | const MenuItem = videojs.getComponent('MenuItem');
5 |
6 | class ContextMenuItem extends MenuItem {
7 | handleClick() {
8 | super.handleClick();
9 | this.options_.listener();
10 | }
11 |
12 | createEl() {
13 | const label = createElement('span', {
14 | class: 'vjs-menu-item-text' + (this.options_.class ? ` ${this.options_.class}` : '')
15 | });
16 | label.appendChild(document.createTextNode(this.localize(this.options_.label)));
17 |
18 | const el = createElement('li', {
19 | class: 'vjs-menu-item',
20 | tabIndex: -1
21 | }, label);
22 |
23 | return el;
24 | }
25 | }
26 |
27 | export default ContextMenuItem;
28 |
--------------------------------------------------------------------------------
/src/plugins/context-menu/components/context-menu.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 | import isFunction from 'lodash/isFunction';
3 | import ContextMenuItem from './context-menu-item';
4 | import { setPosition } from 'utils/positioning';
5 |
6 | const Menu = videojs.getComponent('Menu');
7 |
8 | class ContextMenu extends Menu {
9 |
10 | constructor(player, options) {
11 | super(player, options);
12 |
13 | options.content.forEach(c => {
14 | let fn = null;
15 |
16 | if (isFunction(c.listener)) {
17 | fn = c.listener;
18 | } else if (typeof c.href === 'string') {
19 | fn = () => window.open(c.href);
20 | } else {
21 | fn = () => true;
22 | }
23 |
24 | this.addItem(new ContextMenuItem(player, {
25 | label: c.label,
26 | class: c.class,
27 | listener: (...args) => {
28 | fn(...args);
29 | this.dispose();
30 | }
31 | }));
32 | });
33 | }
34 |
35 | setPosition(left, top) {
36 | setPosition(this.el(), left, top);
37 | }
38 |
39 | createEl() {
40 | const el = super.createEl();
41 |
42 | videojs.dom.addClass(el, 'vjs-context-menu-ui');
43 |
44 | if (this.options_.position) {
45 | const { left, top } = this.options_.position;
46 | this.setPosition(left, top);
47 | }
48 |
49 | return el;
50 | }
51 | }
52 |
53 | export default ContextMenu;
54 |
--------------------------------------------------------------------------------
/src/plugins/context-menu/context-menu.scss:
--------------------------------------------------------------------------------
1 | .vjs-context-menu-ui {
2 | position: absolute;
3 | z-index: 2;
4 |
5 | .vjs-menu-content {
6 | background: rgba(0,0,0,.6);
7 | border-radius: 0.2em;
8 | padding: 0;
9 | }
10 |
11 | .vjs-menu-item {
12 | // Override video.js styles for menus
13 | font-size: 1em;
14 | line-height: 1em;
15 | text-transform: none;
16 |
17 | cursor: pointer;
18 | margin: 0;
19 | padding: 0.8em 1.4em;
20 |
21 | &:active, &:hover {
22 | background-color: rgba(0, 0, 0, 0.5);
23 | }
24 | }
25 |
26 | .player-version {
27 | font-size: 80%;
28 | opacity: .7;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/plugins/context-menu/contextMenuContent.js:
--------------------------------------------------------------------------------
1 | const contextMenuContent = (player) => {
2 |
3 | const isLooping = player.loop();
4 | const isPaused = player.paused();
5 | const isMuted = player.muted();
6 | const isFullscreen = player.isFullscreen();
7 |
8 | const aboutMenuItem = {
9 | class: 'player-version',
10 | label: 'Cloudinary Player v' + VERSION
11 | };
12 |
13 | if (!player.controls()) {
14 | return [aboutMenuItem];
15 | }
16 |
17 | return [
18 | {
19 | label: isLooping ? 'Unloop' : 'Loop',
20 | listener: () => {
21 | player.loop(!isLooping);
22 | }
23 | },
24 | {
25 | label: isPaused ? 'Play' : 'Pause',
26 | listener: () => {
27 | if (isPaused) {
28 | player.play();
29 | } else {
30 | player.pause();
31 | }
32 | }
33 | },
34 | {
35 | label: isMuted ? 'Unmute' : 'Mute',
36 | listener: () => {
37 | player.muted(!isMuted);
38 | }
39 | },
40 | {
41 | label: isFullscreen ? 'Exit Fullscreen' : 'Fullscreen',
42 | listener: () => {
43 | if (isFullscreen) {
44 | player.exitFullscreen();
45 | } else {
46 | player.requestFullscreen();
47 | }
48 | }
49 | },
50 | aboutMenuItem
51 | ];
52 | };
53 |
54 | export default contextMenuContent;
55 |
--------------------------------------------------------------------------------
/src/plugins/ima/ima.js:
--------------------------------------------------------------------------------
1 | import 'videojs-contrib-ads';
2 | import 'videojs-ima';
3 | import 'videojs-ima/dist/videojs.ima.scss';
4 |
--------------------------------------------------------------------------------
/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | import 'videojs-per-source-behaviors';
2 |
3 | import aiHighlightsGraph from './ai-highlights-graph';
4 | import analytics from './analytics';
5 | import autoplayOnScroll from './autoplay-on-scroll';
6 | import cloudinary from './cloudinary';
7 | import cloudinaryAnalytics from './cloudinary-analytics';
8 | import contextMenu from './context-menu';
9 | import floatingPlayer from './floating-player';
10 | import pacedTranscript from './paced-transcript';
11 | import styledTextTracks from './styled-text-tracks';
12 | import vttThumbnails from './vtt-thumbnails';
13 |
14 | // Lazy loaded plugins
15 | import chapters from './chapters';
16 | import colors from './colors';
17 | import imaPlugin from './ima';
18 | import interactionAreas from './interaction-areas';
19 | import playlist from './playlist';
20 | import shoppable from './shoppable-plugin';
21 | import srtTextTracks from './srt-text-tracks';
22 | import visualSearch from './visual-search';
23 | import adaptiveStreaming from './adaptive-streaming';
24 |
25 | const plugins = {
26 | aiHighlightsGraph,
27 | analytics,
28 | autoplayOnScroll,
29 | cloudinary,
30 | cloudinaryAnalytics,
31 | contextMenu,
32 | floatingPlayer,
33 | pacedTranscript,
34 | styledTextTracks,
35 | vttThumbnails,
36 |
37 | // Lazy loaded plugins
38 | chapters,
39 | colors,
40 | imaPlugin,
41 | playlist,
42 | shoppable,
43 | srtTextTracks,
44 | interactionAreas,
45 | visualSearch,
46 | adaptiveStreaming
47 | };
48 |
49 | export default plugins;
50 |
--------------------------------------------------------------------------------
/src/plugins/interaction-areas/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyInteractionAreasPlugin(player, playerOptions, videojsOptions) {
2 | try {
3 | const { interactionAreasService } = await import(/* webpackChunkName: "interaction-areas" */ './interaction-areas.service');
4 | interactionAreasService(player, playerOptions, videojsOptions);
5 | } catch (error) {
6 | console.error('Failed to load plugin:', error);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/plugins/interaction-areas/interaction-areas.const.js:
--------------------------------------------------------------------------------
1 | export const INTERACTION_AREA_LAYOUT_LOCAL_STORAGE_NAME = 'cld-ia-layout-state';
2 |
3 | export const INTERACTION_AREAS_PREFIX = 'vp-ia';
4 |
5 | export const INTERACTION_AREAS_CONTAINER_CLASS_NAME = 'interaction-areas-container';
6 |
7 | export const INTERACTION_AREAS_TEMPLATE = {
8 | PORTRAIT: 'portrait',
9 | LANDSCAPE: 'landscape',
10 | All: 'all',
11 | CENTER: 'center'
12 | };
13 |
14 | export const INTERACTION_AREAS_THEME = {
15 | PULSING: 'pulsing',
16 | SHADOWED: 'shadowed'
17 | };
18 |
19 | export const TEMPLATE_INTERACTION_AREAS_VTT = {
20 | [INTERACTION_AREAS_TEMPLATE.PORTRAIT]: 'https://res.cloudinary.com/prod/raw/upload/v1623772481/video-player/vtts/portrait.vtt',
21 | [INTERACTION_AREAS_TEMPLATE.LANDSCAPE]: 'https://res.cloudinary.com/prod/raw/upload/v1623772303/video-player/vtts/landscape.vtt',
22 | [INTERACTION_AREAS_TEMPLATE.All]: 'https://res.cloudinary.com/prod/raw/upload/v1623250266/video-player/vtts/all.vtt',
23 | [INTERACTION_AREAS_TEMPLATE.CENTER]: 'https://res.cloudinary.com/prod/raw/upload/v1623250265/video-player/vtts/center.vtt'
24 | };
25 |
26 | export const INTERACTION_AREA_HAND_ICON = 'https://res.cloudinary.com/prod/image/upload/v1626764643/video-player/interaction-area-hand.svg';
27 |
28 | export const CLOSE_INTERACTION_AREA_LAYOUT_DELAY = 4500;
29 |
30 | export const DEFAULT_INTERACTION_ARE_TRANSITION = 250;
31 |
--------------------------------------------------------------------------------
/src/plugins/playlist/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyPlugin(options) {
2 | const player = this;
3 | try {
4 | const { default: playlistPlugin } = await import(/* webpackChunkName: "playlist" */ './playlist');
5 | const playlist = playlistPlugin(player, options);
6 | player.cloudinary.playlist = playlist;
7 | return playlist;
8 | } catch (error) {
9 | console.error('Failed to load plugin:', error);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/components/playlist-button.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 |
3 | // Get the ClickableComponent base class from Video.js
4 | const ClickableComponent = videojs.getComponent('ClickableComponent');
5 |
6 | // Create a common class for playlist buttons
7 | class PlaylistButton extends ClickableComponent {
8 |
9 | constructor(player, options) {
10 | // It is important to invoke the superclass before anything else,
11 | // to get all the features of components out of the box!
12 | super(player, options);
13 |
14 | const type = options.type;
15 |
16 | if (!type && type !== 'previous' && type !== 'next') {
17 | throw new Error('Type must be either \'previous\' or \'next\'');
18 | }
19 | }
20 |
21 | // The `createEl` function of a component creates its DOM element.
22 | createEl() {
23 | const type = this.options_.type;
24 | const typeCssClass = `vjs-icon-${type}-item`;
25 |
26 | return videojs.dom.createEl('button', {
27 | // Prefixing classes of elements within a player with "vjs-"
28 | // is a convention used in Video.js.
29 | className: `vjs-control vjs-playlist-button vjs-button ${typeCssClass}`,
30 | ariaLabel: `Playlist ${type} item`
31 | });
32 | }
33 | }
34 |
35 | export default PlaylistButton;
36 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/components/playlist-buttons.js:
--------------------------------------------------------------------------------
1 | import PlaylistNextButton from './playlist-next-button';
2 | import PlaylistPreviousButton from './playlist-previous-button';
3 | import './playlist-buttons.scss';
4 |
5 | export { PlaylistNextButton, PlaylistPreviousButton };
6 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/components/playlist-buttons.scss:
--------------------------------------------------------------------------------
1 | .cld-video-player {
2 |
3 | .vjs-playlist-button {
4 | display: none;
5 | }
6 |
7 | &.vjs-playlist {
8 |
9 | .vjs-playlist-button {
10 | display: block;
11 | }
12 | }
13 |
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/components/playlist-next-button.js:
--------------------------------------------------------------------------------
1 | import PlaylistButton from './playlist-button';
2 | import videojs from 'video.js';
3 |
4 | class PlaylistNextButton extends PlaylistButton {
5 |
6 | constructor(player) {
7 | super(player, { type: 'next' });
8 | }
9 |
10 | handleClick(event) {
11 | event.stopPropagation();
12 | super.handleClick(event);
13 | this.player().cloudinary.playlist().playNext();
14 | }
15 | }
16 |
17 | videojs.registerComponent('PlaylistNextButton', PlaylistNextButton);
18 |
19 | export default PlaylistNextButton;
20 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/components/playlist-previous-button.js:
--------------------------------------------------------------------------------
1 | import PlaylistButton from './playlist-button';
2 | import videojs from 'video.js';
3 |
4 | class PlaylistPreviousButton extends PlaylistButton {
5 |
6 | constructor(player) {
7 | super(player, { type: 'previous' });
8 | }
9 |
10 | handleClick(event) {
11 | super.handleClick(event);
12 | this.player().cloudinary.playlist().playPrevious();
13 | }
14 | }
15 |
16 | videojs.registerComponent('PlaylistPreviousButton', PlaylistPreviousButton);
17 |
18 | export default PlaylistPreviousButton;
19 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/layout/playlist-layout-custom.js:
--------------------------------------------------------------------------------
1 | import PlaylistLayout from './playlist-layout';
2 |
3 | class PlaylistLayoutCustom extends PlaylistLayout {
4 |
5 | getCls() {
6 | let cls = super.getCls();
7 | cls.push('cld-plw-custom');
8 |
9 | return cls;
10 | }
11 |
12 | createEl() {
13 | const el = super.createEl();
14 | this.options_.renderTo.appendChild(el);
15 |
16 | return el;
17 | }
18 | }
19 |
20 |
21 | export default PlaylistLayoutCustom;
22 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/layout/playlist-layout-horizontal.js:
--------------------------------------------------------------------------------
1 | import PlaylistLayout from './playlist-layout';
2 |
3 | class PlaylistLayoutHorizontal extends PlaylistLayout {
4 | constructor (player, options) {
5 | options.wrap = true;
6 | super(player, options);
7 | }
8 |
9 | getCls() {
10 | const cls = super.getCls();
11 | cls.push('cld-plw-horizontal');
12 | return cls;
13 | }
14 | }
15 |
16 | export default PlaylistLayoutHorizontal;
17 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/layout/playlist-layout-vertical.js:
--------------------------------------------------------------------------------
1 | import PlaylistLayout from './playlist-layout';
2 |
3 | class PlaylistLayoutVertical extends PlaylistLayout {
4 |
5 | constructor (player, options) {
6 | options.wrap = true;
7 | super(player, options);
8 | }
9 |
10 | getCls() {
11 | const cls = super.getCls();
12 | cls.push('cld-plw-vertical');
13 |
14 | return cls;
15 | }
16 | }
17 |
18 |
19 | export default PlaylistLayoutVertical;
20 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/playlist.const.js:
--------------------------------------------------------------------------------
1 | export const DEFAULT_AUTO_ADVANCE = 0;
2 |
3 | export const DEFAULT_PRESENT_UPCOMING = 10;
4 |
5 | export const UPCOMING_VIDEO_TRANSITION = 1;
6 |
7 | export const PLAYLIST_DEFAULTS_OPTIONS = {
8 | fluid: false,
9 | show: true,
10 | direction: 'vertical',
11 | total: 4,
12 | selector: false,
13 | renderTo: []
14 | };
15 |
--------------------------------------------------------------------------------
/src/plugins/playlist/ui/thumbnail/thumbnail.scss:
--------------------------------------------------------------------------------
1 | .cld-thumbnail {
2 | position: relative;
3 | display: block;
4 | width: 100%;
5 | overflow: hidden;
6 | font-size: 1em;
7 | text-align: left;
8 | background-repeat: no-repeat;
9 | background-size: cover;
10 | background-position: center;
11 |
12 | .cld-thumbnail-img {
13 | display: none;
14 | }
15 |
16 | &:before {
17 | content: '';
18 | position: absolute;
19 | top: 40%;
20 | max-height: 60%;
21 | right: 0;
22 | bottom: 0;
23 | left: 0;
24 | background: linear-gradient(to top, var(--color-base), transparent 80%);
25 | opacity: 0.9;
26 | }
27 |
28 | &.cld-plw-panel-item-active {
29 | border: 1px solid var(--color-accent);
30 | box-sizing: border-box;
31 | box-shadow: 0 0 3em -0.5em var(--color-accent) inset;
32 | }
33 | }
34 |
35 | .cld-plw-panel-item:hover:after {
36 | content: '';
37 | position: absolute;
38 | width: 100%;
39 | height: 100%;
40 | top: 0px;
41 | left: 0px;
42 | background-color: var(--color-text);
43 | opacity: 0.2;
44 | }
45 |
46 | @media only screen and (max-width: 768px) {
47 | .cld-thumbnail:before {
48 | background: none;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/plugins/playlist/utils/api.js:
--------------------------------------------------------------------------------
1 | import camelCase from 'lodash/camelCase';
2 | import isPlainObject from 'lodash/isPlainObject';
3 | import { parseISO8601 } from './time';
4 |
5 | const TIME_FIELDS = ['created_at', 'updated_at'];
6 |
7 | const normalizeJsonResponse = (obj) => {
8 | const agg = {};
9 |
10 | if (isPlainObject(obj)) {
11 | Object.keys(obj).reduce((agg, key) => {
12 | const newKey = camelCase(key);
13 |
14 | if (TIME_FIELDS.indexOf(key) !== -1) {
15 | agg[newKey] = new Date(parseISO8601(obj[key]));
16 | } else {
17 | agg[newKey] = normalizeJsonResponse(obj[key]);
18 | }
19 |
20 | return agg;
21 | }, agg);
22 |
23 | return agg;
24 | } else if (Array.isArray(obj)) {
25 | return obj.map((item) => normalizeJsonResponse(item));
26 | } else {
27 | return obj;
28 | }
29 | };
30 |
31 |
32 | export { normalizeJsonResponse };
33 |
--------------------------------------------------------------------------------
/src/plugins/playlist/utils/dom.js:
--------------------------------------------------------------------------------
1 | export const wrap = (el, wrapper) => {
2 | el.parentNode.insertBefore(wrapper, el);
3 | wrapper.appendChild(el);
4 |
5 | return wrapper;
6 | };
7 |
--------------------------------------------------------------------------------
/src/plugins/playlist/utils/time.js:
--------------------------------------------------------------------------------
1 | // https://github.com/csnover/js-iso8601/blob/master/iso8601.js
2 | const numericKeys = [1, 4, 5, 6, 7, 10, 11];
3 |
4 | const parseISO8601 = function (date) {
5 | let timestamp = 0;
6 | let struct = 0;
7 | let minutesOffset = 0;
8 |
9 | // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
10 | // before falling back to any implementation-specific date parsing, so that’s what we do, even if native
11 | // implementations could be faster
12 | // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
13 | if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
14 | // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
15 | for (let i = 0, k; (k = numericKeys[i]); ++i) {
16 | struct[k] = +struct[k] || 0;
17 | }
18 |
19 | // allow undefined days and months
20 | struct[2] = (+struct[2] || 1) - 1;
21 | struct[3] = +struct[3] || 1;
22 |
23 | if (struct[8] !== 'Z' && struct[9] !== undefined) {
24 | minutesOffset = struct[10] * 60 + struct[11];
25 |
26 | if (struct[9] === '+') {
27 | minutesOffset = 0 - minutesOffset;
28 | }
29 | }
30 |
31 | timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
32 | } else {
33 | timestamp = NaN;
34 | }
35 |
36 | return timestamp;
37 | };
38 |
39 | export { parseISO8601 };
40 |
--------------------------------------------------------------------------------
/src/plugins/shoppable-plugin/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyShoppablePlugin(player, options) {
2 |
3 | const { default: ShoppableWidget } = await import(/* webpackChunkName: "shoppable" */ '../../components/shoppable-bar/shoppable-widget');
4 | new ShoppableWidget(player, options.shoppable).init();
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/src/plugins/srt-text-tracks/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazySrtTextTracksPlugin(config) {
2 | const player = this;
3 | try {
4 | const { default: srtTextTracks } = await import(/* webpackChunkName: "srt-text-tracks" */ './srt-text-tracks');
5 | player.ready(() => srtTextTracks(config, player));
6 | } catch (error) {
7 | console.error('Failed to load srt-text-tracks plugin:', error);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugins/styled-text-tracks/index.js:
--------------------------------------------------------------------------------
1 | import styledTextTracks from './styled-text-tracks';
2 |
3 | export default async function styledTextTracksPlugin(config) {
4 | const player = this;
5 | try {
6 | player.ready(() => styledTextTracks(config, player));
7 | } catch (error) {
8 | console.error('Failed to load plugin:', error);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/plugins/visual-search/components/SearchButton.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 |
3 | export const SearchButton = (onClick) => {
4 | const button = videojs.dom.createEl('button', {
5 | className: 'vjs-control vjs-button vjs-visual-search-button',
6 | title: 'Search video content',
7 | ariaLabel: 'Search video content'
8 | });
9 |
10 | const searchIcon = videojs.dom.createEl('span', {
11 | className: 'vjs-icon-search'
12 | });
13 | button.appendChild(searchIcon);
14 |
15 | const spinnerIcon = videojs.dom.createEl('span', {
16 | className: 'vjs-loading-spinner'
17 | });
18 | button.appendChild(spinnerIcon);
19 |
20 | button.addEventListener('click', onClick);
21 |
22 | return button;
23 | };
24 |
--------------------------------------------------------------------------------
/src/plugins/visual-search/components/SearchInput.js:
--------------------------------------------------------------------------------
1 | import videojs from 'video.js';
2 |
3 | export const SearchInput = (onSearch, onClose) => {
4 | const form = videojs.dom.createEl('form', {
5 | className: 'vjs-visual-search-form'
6 | });
7 |
8 | const input = videojs.dom.createEl('input', {
9 | className: 'vjs-visual-search-input',
10 | type: 'text',
11 | ariaLabel: 'Search input',
12 | tabIndex: -1
13 | });
14 |
15 | const closeButton = videojs.dom.createEl('button', {
16 | className: 'vjs-control vjs-button vjs-visual-search-close',
17 | type: 'button',
18 | title: 'Close search',
19 | ariaLabel: 'Close search',
20 | tabIndex: -1
21 | });
22 |
23 | // Add close icon
24 | const closeIcon = videojs.dom.createEl('span', {
25 | className: 'vjs-icon-close'
26 | });
27 | closeButton.appendChild(closeIcon);
28 |
29 | form.appendChild(input);
30 | form.appendChild(closeButton);
31 |
32 | // Handle search submission
33 | form.addEventListener('submit', (e) => {
34 | e.preventDefault();
35 | const query = input.value.trim();
36 | if (query) {
37 | onSearch(query);
38 | }
39 | });
40 |
41 | // Handle close button
42 | closeButton.addEventListener('click', (e) => {
43 | e.preventDefault();
44 | if (onClose) {
45 | onClose();
46 | }
47 | });
48 |
49 | return {
50 | element: form,
51 | input,
52 | closeButton
53 | };
54 | };
55 |
--------------------------------------------------------------------------------
/src/plugins/visual-search/index.js:
--------------------------------------------------------------------------------
1 | export default async function lazyVisualSearchPlugin(options) {
2 | const player = this;
3 | try {
4 | const { default: initPlugin } = await import(/* webpackChunkName: "visual-search" */ './visual-search');
5 | player.ready(() => initPlugin(options, player));
6 | } catch (error) {
7 | console.error('Failed to load plugin:', error);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugins/vtt-thumbnails/vtt-thumbnails.scss:
--------------------------------------------------------------------------------
1 | .cld-video-player {
2 | .vjs-vtt-thumbnail-display {
3 | position: absolute;
4 | left: 0;
5 | z-index: 1;
6 | transition: opacity 0.2s;
7 | bottom: 3em;
8 | pointer-events: none;
9 | border: 1px solid var(--color-base);
10 | border-radius: 2px;
11 | box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
12 | backdrop-filter: blur(12px);
13 | transform: translateX(-50%);
14 | }
15 |
16 | .vjs-vtt-time-display {
17 | font-size: 80%;
18 | line-height: 1.4;
19 | position: absolute;
20 | bottom: -2.8em;
21 | left: 0;
22 | right: 0;
23 | margin: auto;
24 | color: var(--color-text);
25 | background: var(--color-base);
26 | padding: 0.3em 0.6em;
27 | width: fit-content;
28 | border-radius: 4px;
29 | }
30 |
31 | .vjs-time-tooltip {
32 | right: auto !important;
33 | translate: -50%;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/utils/apply-with-props.js:
--------------------------------------------------------------------------------
1 | export function applyWithProps(context, obj) {
2 | Object.entries(obj).forEach(([key, value]) => {
3 | if (context[key] && typeof context[key] === 'function') {
4 | context[key](value);
5 | }
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/consts.js:
--------------------------------------------------------------------------------
1 | export const PLAYER_EVENT = {
2 | READY: 'ready',
3 | PLAY: 'play',
4 | PLAYING: 'playing',
5 | PAUSE: 'pause',
6 | SEEK: 'seek',
7 | SEEKING: 'seeking',
8 | MUTE: 'mute',
9 | UNMUTE: 'unmute',
10 | PAUSE_NO_SEEK: 'pausenoseek',
11 | ERROR: 'error',
12 | TIME_UPDATE: 'timeupdate',
13 | EMPTIED: 'emptied',
14 | RETRY_PLAYLIST: 'retryplaylist',
15 | CAN_PLAY_THROUGH: 'canplaythrough',
16 | CLD_SOURCE_CHANGED: 'cldsourcechanged',
17 | SOURCE_CHANGED: 'sourcechanged',
18 | LOADED_METADATA: 'loadedmetadata',
19 | LOADED_DATA: 'loadeddata',
20 | REFRESH_TEXT_TRACKS: 'refreshTextTracks',
21 | PLAYLIST_CREATED: 'playlistcreated',
22 | UP_COMING_VIDEO_SHOW: 'upcomingvideoshow',
23 | UP_COMING_VIDEO_HIDE: 'upcomingvideohide',
24 | PLAYLIST_ITEM_CHANGED: 'playlistitemchanged',
25 | VOLUME_CHANGE: 'volumechange',
26 | FLUID: 'fluid',
27 | PLAYLIST_PANEL: 'PlaylistPanel',
28 | ENDED: 'ended',
29 | RESIZE: 'resize',
30 | START: 'start',
31 | VIDEO_LOAD: 'videoload',
32 | PRODUCT_BAR_MIN: 'productBarMin',
33 | SHOW_PRODUCTS_OVERLAY: 'showProductsOverlay',
34 | SHOPPABLE_ITEM_CHANGED: 'shoppableitemchanged',
35 | FULL_SCREEN_CHANGE: 'fullscreenchange',
36 | PERCENTS_PLAYED: 'percentsplayed',
37 | TIME_PLAYED: 'timeplayed',
38 | PLAYER_LOAD: 'playerload',
39 | DISPOSE: 'dispose',
40 | QUALITY_CHANGED: 'qualitychanged'
41 | };
42 |
43 | export const SOURCE_TYPE = {
44 | AUDIO: 'AudioSource',
45 | VIDEO: 'VideoSource'
46 | };
47 |
--------------------------------------------------------------------------------
/src/utils/css-prefix.js:
--------------------------------------------------------------------------------
1 | import defaults from '../config/defaults';
2 | import { find } from './find';
3 |
4 | const CLASS_PREFIX = 'cld-video-player';
5 | const SKIN_CLASS_PREFIX = `${CLASS_PREFIX}-skin-`;
6 |
7 | const playerClassPrefix = (componentInstance) => `${CLASS_PREFIX}-${componentInstance.id_}`;
8 |
9 | const skinClass = (skin) => `${SKIN_CLASS_PREFIX}${skin}`;
10 |
11 | const skinClassPrefix = (componentInstance) => find(componentInstance.el().classList, (cls) => cls.startsWith(SKIN_CLASS_PREFIX));
12 |
13 | const setSkinClassPrefix = (componentInstance, name) => {
14 | const currentSkinPrefix = skinClassPrefix(componentInstance);
15 | const skinName = name ? name.replace(SKIN_CLASS_PREFIX, '') : false;
16 |
17 | let newSkinPrefix = '';
18 | if (skinName) {
19 | // From html class
20 | newSkinPrefix = skinClass(skinName);
21 | } else if (componentInstance.options_.skin) {
22 | // From JS config
23 | newSkinPrefix = skinClass(componentInstance.options_.skin);
24 | } else {
25 | // Defult
26 | newSkinPrefix = skinClass(defaults.skin);
27 | }
28 |
29 | if (newSkinPrefix !== currentSkinPrefix) {
30 | if (currentSkinPrefix) {
31 | componentInstance.removeClass(currentSkinPrefix);
32 | }
33 | componentInstance.addClass(newSkinPrefix);
34 | }
35 |
36 | if (skinName && componentInstance.options_.skin !== skinName) {
37 | componentInstance.options_.skin = skinName;
38 | }
39 |
40 | };
41 |
42 | export { CLASS_PREFIX, playerClassPrefix, skinClassPrefix, skinClass, setSkinClassPrefix };
43 |
--------------------------------------------------------------------------------
/src/utils/find.js:
--------------------------------------------------------------------------------
1 | function find(list, callback) {
2 | if (Array.prototype.find && Array.isArray(list)) {
3 | return list.find(callback);
4 | }
5 |
6 | return findElementAndIndex(list, callback)[0];
7 | }
8 |
9 | function findElementAndIndex(list, callback) {
10 | for (let i = 0; i < list.length; i++) {
11 | const element = list[i];
12 | if (callback(element, i, list)) {
13 | return [element, i];
14 | }
15 | }
16 |
17 | return [undefined, -1];
18 | }
19 |
20 | export { find };
21 |
--------------------------------------------------------------------------------
/src/utils/fontFace.js:
--------------------------------------------------------------------------------
1 | import WebFont from 'webfontloader';
2 |
3 | const FONT_FAMILY = 'Inter';
4 |
5 | const fontFace = (elem, fontFace) => {
6 | // Default font-face is "Inter"
7 | if (typeof fontFace === 'undefined') {
8 | fontFace = FONT_FAMILY;
9 | }
10 |
11 | if (fontFace && fontFace !== 'inherit') {
12 | WebFont.load({
13 | google: {
14 | families: [fontFace]
15 | }
16 | });
17 | elem.style.fontFamily = fontFace;
18 | } else if (fontFace === 'inherit') {
19 | elem.style.fontFamily = 'inherit';
20 | }
21 | };
22 |
23 | export { fontFace };
24 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | import * as slicing from './slicing';
2 | import * as positioning from './positioning';
3 | import * as cloudinaryUtils from './cloudinary';
4 | import * as mixin from './mixin';
5 | import * as fontFace from './fontFace';
6 | import * as cssPrefix from './css-prefix';
7 | import * as normalizeAttributes from './attributes-normalizer';
8 |
9 | const Utils = Object.assign({},
10 | slicing,
11 | positioning,
12 | cloudinaryUtils,
13 | fontFace,
14 | mixin,
15 | cssPrefix,
16 | normalizeAttributes
17 | );
18 |
19 | export default Utils;
20 |
--------------------------------------------------------------------------------
/src/utils/mixin.js:
--------------------------------------------------------------------------------
1 | function mixin(...mixins) {
2 | return mixins.reduce((c, mixin) => mixin(c), class Blank {});
3 | }
4 |
5 | export { mixin };
6 |
--------------------------------------------------------------------------------
/src/utils/object.js:
--------------------------------------------------------------------------------
1 | import snakeCase from 'lodash/snakeCase';
2 |
3 | export const convertKeysToSnakeCase = (obj) => {
4 | let snakeCaseObj = {};
5 |
6 | for (const key of Object.keys(obj)) {
7 | const snakeCaseKey = snakeCase(key);
8 | snakeCaseObj[snakeCaseKey] = obj[key];
9 | }
10 |
11 | return snakeCaseObj;
12 | };
13 |
--------------------------------------------------------------------------------
/src/utils/querystring.js:
--------------------------------------------------------------------------------
1 | const objectToQuerystring = (obj) => {
2 | const keys = Object.keys(obj);
3 |
4 | if (!keys.length) {
5 | return '';
6 | }
7 |
8 | const query = keys.map((key) => `${key}=${obj[key]}`).join('&');
9 | return `?${query}`;
10 | };
11 |
12 | export { objectToQuerystring };
13 |
--------------------------------------------------------------------------------
/src/utils/slicing.js:
--------------------------------------------------------------------------------
1 | function _sliceProperties(obj, isUnset, ...props) {
2 | return props.reduce((acc, prop) => {
3 | if (Object.prototype.hasOwnProperty.call(obj, prop)) {
4 | acc[prop] = obj[prop];
5 | if (isUnset) {
6 | delete obj[prop];
7 | }
8 | }
9 | return acc;
10 | }, {});
11 | }
12 |
13 | function sliceProperties(obj, ...props) {
14 | return _sliceProperties(obj, false, ...props);
15 | }
16 |
17 | function sliceAndUnsetProperties(obj, ...props) {
18 | return _sliceProperties(obj, true, ...props);
19 | }
20 |
21 | export { sliceProperties, sliceAndUnsetProperties };
22 |
--------------------------------------------------------------------------------
/src/utils/time.js:
--------------------------------------------------------------------------------
1 | // Convert time string i.e. '2:40' to seconds number (160)
2 | // Also allows h:m:s format and mm:ss, m:s etc.
3 | const parseTime = function (hms) {
4 | const [seconds, minutes, hours] = hms.split(':').reverse();
5 | let sum = null;
6 | if (!isNaN(seconds)) {
7 | sum = (+hours || 0) * 60 * 60 + (+minutes || 0) * 60 + (+seconds);
8 | }
9 | return sum;
10 | };
11 |
12 | export { parseTime };
13 |
--------------------------------------------------------------------------------
/src/utils/video-retry.js:
--------------------------------------------------------------------------------
1 | const checkIfVideoIsAvailable = (videoUrl, videoType = 'default') => {
2 | return new Promise((resolve, reject) => {
3 | const tempVideo = document.createElement('video');
4 | tempVideo.setAttribute('crossorigin', 'anonymous');
5 | const targetEvent = videoType === 'live' ? 'onprogress' : 'canplay';
6 | tempVideo[targetEvent] = () => {
7 | tempVideo.onerror = null;
8 | tempVideo[targetEvent] = null;
9 | resolve();
10 | };
11 | tempVideo.onerror = () => reject();
12 | tempVideo.src = videoUrl;
13 | tempVideo.load();
14 | });
15 | };
16 |
17 | const isVideoInReadyState = (readyState) => {
18 | return readyState >= (/iPad|iPhone|iPod/.test(navigator.userAgent) ? 1 : 4);
19 | };
20 |
21 | export { checkIfVideoIsAvailable, isVideoInReadyState };
22 |
--------------------------------------------------------------------------------
/test/adaptive-streaming.test.js:
--------------------------------------------------------------------------------
1 | describe('Adaptive streaming tests', () => {
2 | beforeAll(async () => {
3 | jest.setTimeout(35000);
4 | await page.setViewport({ width: 1280, height: 800 });
5 | await page.goto('http://localhost:3000/docs/adaptive-streaming.html', { waitUntil: 'load' });
6 | await page.evaluate(() => {
7 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
8 | get: function () {
9 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
10 | this.readyState > 2);
11 | }
12 | });
13 | });
14 | }, 10000);
15 | it('Should not throw an error when setting new hls source', async () => {
16 | jest.setTimeout(35000);
17 | await page.waitFor(1000);
18 |
19 | // async function not working without being put inside a template string,
20 | // See https://github.com/puppeteer/puppeteer/issues/1665
21 | const error = await page.evaluate(`(async () => {
22 | let error = null;
23 |
24 | // Get any error into error variable
25 | playerHls.on('error', (e) => (error = e.Player.videojs.error()));
26 |
27 | // Set new hls source
28 | playerHls.source('snow_horses', {sourceTypes: ['hls'], transformation: { streaming_profile: 'hd' }});
29 |
30 | // wait a second for error event
31 | await new Promise(resolve=>setTimeout(resolve, 1000));
32 |
33 | // Return null or an error object
34 | return JSON.stringify(error);
35 | })()`);
36 | expect(JSON.parse(error)).toEqual(null); // expect no error
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/ads.test.js:
--------------------------------------------------------------------------------
1 | describe('Ads tests', () => {
2 |
3 | beforeEach(async () => {
4 | await page.setViewport({ width: 1280, height: 800 });
5 | await page.goto('http://localhost:3000/vast-vpaid.html', { waitUntil: 'load' });
6 | await page.evaluate(() => {
7 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
8 | get: function () {
9 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
10 | this.readyState > 2);
11 | }
12 | });
13 | });
14 |
15 | await page.evaluate(() => (window.ev = []));
16 | await page.evaluate(() => player.on('readyforpreroll', () => {
17 | window.ev.push('preroll');
18 | }));
19 | await page.evaluate(() => player.on('readyforpostroll', () => {
20 | window.ev.push('postroll');
21 | }));
22 | }, 10000);
23 |
24 | it('event test', async () => {
25 | jest.setTimeout(65000);
26 | await page.waitForSelector('#player_ima-ad-container', {visible: true});
27 | await page.waitFor(1000);
28 | const duration = await page.evaluate(() => player.duration());
29 | await page.waitFor(duration * 1000 + 1000);
30 | const events = await page.evaluate(() => window.ev);
31 | expect(events.includes('preroll')).toEqual(true);
32 | expect(events.includes('postroll')).toEqual(true);
33 | });
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/test/autoplay.scroll.test.js:
--------------------------------------------------------------------------------
1 | describe('Auto-play tests', () => {
2 |
3 | beforeEach(async () => {
4 | await page.setViewport({width: 1280, height: 800});
5 | await page.goto('http://localhost:3000/autoplay-on-scroll.html', {waitUntil: 'load'});
6 | await page.evaluate(() => {
7 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
8 | get: function() {
9 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
10 | this.readyState > 2);
11 | }
12 | });
13 | });
14 | }, 10000);
15 |
16 | it('Test scroll', async () => {
17 | const player = await page.$('#player_html5_api');
18 | expect(await player.isIntersectingViewport()).toEqual(false);
19 | await player.tap();
20 | await page.waitFor(1000);
21 | expect(await page.$eval('#player_html5_api', p => p.playing)).toEqual(true);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/components.test.js:
--------------------------------------------------------------------------------
1 | describe('Components tests', () => {
2 |
3 | beforeEach(async () => {
4 | await page.setViewport({width: 1280, height: 800});
5 | await page.goto('http://localhost:3000/components.html', {waitUntil: 'load'});
6 | await page.evaluate(() => {
7 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
8 | get: function() {
9 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
10 | this.readyState > 2);
11 | }
12 | });
13 | });
14 | }, 10000);
15 |
16 | it('Test components', async () => {
17 | page.waitFor(1000);
18 | expect(await page.$eval('.vjs-playlist-control.vjs-playlist-next-control', (b => b.tagName))).toEqual('BUTTON');
19 | expect(await page.$eval('.vjs-playlist-control.vjs-playlist-previous-control', (b => b.tagName))).toEqual('BUTTON');
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/custom-error.test.js:
--------------------------------------------------------------------------------
1 | describe('custom error tests', () => {
2 |
3 | beforeAll(async () => {
4 | jest.setTimeout(35000);
5 | await page.setViewport({ width: 1280, height: 800 });
6 | await page.goto('http://localhost:3000/docs/custom-cld-errors.html', { waitUntil: 'load' });
7 | await page.evaluate(() => {
8 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
9 | get: function() {
10 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
11 | this.readyState > 2);
12 | }
13 | });
14 | });
15 | }, 10000);
16 |
17 | it('Test error', async () => {
18 | jest.setTimeout(35000);
19 | await page.waitFor(1000);
20 | // eslint-disable-next-line no-undef
21 | const errorMsg = await page.evaluate(() => player.videojs.error_.message);
22 | expect(errorMsg).toBe('My custom error message');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/e2e/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['../../.eslintrc.js', 'plugin:@typescript-eslint/recommended', 'prettier'],
3 | parser: '@typescript-eslint/parser',
4 | plugins: ['@typescript-eslint', 'prettier'],
5 | rules: {
6 | 'prettier/prettier': [
7 | 'error',
8 | {
9 | endOfLine: 'auto',
10 | },
11 | ],
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/test/e2e/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | /test-results/
3 | /playwright-report/
4 | /blob-report/
5 | /playwright/.cache/
6 |
--------------------------------------------------------------------------------
/test/e2e/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 256,
3 | "semi": true,
4 | "singleQuote": true,
5 | "trailingComma": "es5",
6 | "bracketSpacing": true,
7 | "tabWidth": 4,
8 | "arrowParens": "always"
9 | }
10 |
--------------------------------------------------------------------------------
/test/e2e/components/BaseComponent.ts:
--------------------------------------------------------------------------------
1 | import { Locator, Page } from '@playwright/test';
2 |
3 | /**
4 | * Base component constructor interface
5 | *
6 | * selector is optional in order to allow default selector usage.
7 | * For example:
8 | *
9 | * constructor(dataProps: IBaseComponent) {
10 | * const baseComponentProps: IBaseComponent = dataProps;
11 | * baseComponentProps.selector = dataProps?.selector ?? "//*";
12 | * super(baseComponentProps);
13 | * ...
14 | * }
15 | */
16 | export interface IBaseComponent {
17 | page: Page;
18 | selector: string;
19 | parentSelector?: string;
20 | iframeSelector?: string;
21 | }
22 | /**
23 | * Base class for an POM component class
24 | * such as buttons, dropList, etc
25 | */
26 | export class BaseComponent {
27 | get locator(): Locator {
28 | return this._locator;
29 | }
30 |
31 | get props(): IBaseComponent {
32 | return this._props;
33 | }
34 |
35 | private readonly _locator: Locator;
36 | private readonly _props: IBaseComponent;
37 |
38 | constructor(basePageProps: IBaseComponent) {
39 | if (!basePageProps.selector) {
40 | throw Error(`Missing selector in basePageProps`);
41 | }
42 | const elementSelector: string = basePageProps.parentSelector ? `${basePageProps.parentSelector}${basePageProps.selector}` : basePageProps.selector;
43 |
44 | this._props = basePageProps;
45 | this._locator = basePageProps.iframeSelector ? basePageProps.page.frameLocator(basePageProps.iframeSelector).locator(elementSelector) : basePageProps.page.locator(elementSelector);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/e2e/fixtures/vpTest.ts:
--------------------------------------------------------------------------------
1 | import { ConsoleMessage, test } from '@playwright/test';
2 | import { MainPage } from '../src/pom/mainPage';
3 | import PageManager from '../src/pom/PageManager';
4 |
5 | /**
6 | * Fixture parameters.
7 | */
8 | type FixtureParams = {
9 | consoleErrors: ConsoleMessage[];
10 | vpExamples: MainPage;
11 | pomPages: PageManager;
12 | };
13 |
14 | /**
15 | * Extend Playwright test with custom fixtures.
16 | */
17 | export const vpTest = test.extend({
18 | /**
19 | * Page Manager
20 | */
21 | pomPages: [
22 | async ({ page }, use) => {
23 | const pomPages = new PageManager(page);
24 | await pomPages.mainPage.goto();
25 | await use(pomPages);
26 | },
27 | { scope: 'test', auto: true },
28 | ],
29 |
30 | /**
31 | * Fixture for capturing console errors.
32 | */
33 | consoleErrors: [
34 | async ({ page }, use) => {
35 | const consoleLogs = new Array();
36 | page.on('console', (msg) => {
37 | if (msg.type() === 'error') consoleLogs.push(msg);
38 | });
39 | await use(consoleLogs);
40 | },
41 | { scope: 'test' },
42 | ],
43 | });
44 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmAnalyticsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { test } from '@playwright/test';
5 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
6 | import { ESM_URL } from '../../testData/esmUrl';
7 |
8 | const link = getEsmLinkByName(ExampleLinkName.Analytics);
9 |
10 | vpTest(`Test if video on ESM analytics page is playing as expected`, async ({ page, pomPages }) => {
11 | await test.step('Navigate to ESM', async () => {
12 | await page.goto(ESM_URL);
13 | });
14 | await test.step('Navigate to analytics page by clicking on link', async () => {
15 | await pomPages.mainPage.clickLinkByName(link.name);
16 | await waitForPageToLoadWithTimeout(page, 5000);
17 | });
18 | await test.step('Click on play button of video player to play video', async () => {
19 | return pomPages.analyticsPage.analyticsVideoComponent.clickPlay();
20 | });
21 | await test.step('Validating that the video is playing', async () => {
22 | await pomPages.analyticsPage.analyticsVideoComponent.validateVideoIsPlaying(true);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmApiAndEventsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ESM_URL } from '../../testData/esmUrl';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { testApiAndEventsPageVideoIsPlaying } from '../commonSpecs/apiAndEventsPageVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.APIAndEvents);
8 |
9 | vpTest(`Test if video on ESM API and Events page can play as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testApiAndEventsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmAudioPlayerPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ESM_URL } from '../../testData/esmUrl';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { testAudioPlayerPageVideosArePlaying } from '../commonSpecs/audioPlayerPageVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.AudioPlayer);
8 |
9 | vpTest(`Test if 2 videos on ESM audio player page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testAudioPlayerPageVideosArePlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmAutoplayOnScroll.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ESM_URL } from '../../testData/esmUrl';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { testAutoplayOnScrollPageVideoIsPlaying } from '../commonSpecs/autoplayOnScrollVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.AutoplayOnScroll);
8 |
9 | vpTest(`Test if video on ESM autoplay on scroll page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testAutoplayOnScrollPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmChaptersPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ESM_URL } from '../../testData/esmUrl';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { testChaptersPageVideosArePlaying } from '../commonSpecs/chaptersPage';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.Chapters);
8 |
9 | vpTest(`Test if 3 videos on ESM chapters page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testChaptersPageVideosArePlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmCldAnalyticsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testCldAnalyticsPageVideoIsPlaying } from '../commonSpecs/cldAnalyticsPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.CloudinaryAnalytics);
8 |
9 | vpTest(`Test if 4 videos on ESM Cloudinary analytics page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testCldAnalyticsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmCodecsAndFormatsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { ESM_URL } from '../../testData/esmUrl';
5 | import { testCodecsAndFormatsPageVideoIsPlaying } from '../commonSpecs/codecsAndFormatsVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.CodecsAndFormats);
8 |
9 | vpTest(`Test if 3 videos on ESM codecs and formats page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testCodecsAndFormatsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmColorsApiPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { ESM_URL } from '../../testData/esmUrl';
5 | import { testColorsApiPageVideoIsPlaying } from '../commonSpecs/colorsApiPageVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.ColorsAPI);
8 |
9 | vpTest(`Test if 3 videos on ESM colors API page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testColorsApiPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmComponentsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { ESM_URL } from '../../testData/esmUrl';
5 | import { testComponentsPageVideoIsPlaying } from '../commonSpecs/componentsPageVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.Components);
8 |
9 | vpTest(`Test if video on ESM components page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testComponentsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmDisplayConfigurationPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { ESM_URL } from '../../testData/esmUrl';
5 | import { testDisplayConfigurationPageVideoIsPlaying } from '../commonSpecs/displayConfigurationPageVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.DisplayConfigurations);
8 |
9 | vpTest(`Test if video on ESM display configurations page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testDisplayConfigurationPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmFloatingPlayer.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { ESM_URL } from '../../testData/esmUrl';
5 | import { testFloatingPlayerPageVideoIsPlaying } from '../commonSpecs/floatingPlayerPageVideoPlaying';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.FloatingPlayer);
8 |
9 | vpTest(`Test if video on ESM floating player page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testFloatingPlayerPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmFluidLayoutsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testFluidLayoutsPageVideoIsPlaying } from '../commonSpecs/fluidLayoutsPageVideoPlaying';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getLinkByName(ExampleLinkName.FluidLayouts);
8 |
9 | vpTest(`Test if video on ESM fluid layouts page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testFluidLayoutsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmForceHlsSubtitlesPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testForceHlsSubtitlesPageVideoIsPlaying } from '../commonSpecs/forceHlsSubtitlesPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.ForceHLSSubtitles);
8 |
9 | vpTest(`Test if video on ESM force HLS subtitles page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testForceHlsSubtitlesPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmHighlightsGraphPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { test } from '@playwright/test';
5 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
6 | import { ESM_URL } from '../../testData/esmUrl';
7 |
8 | const link = getEsmLinkByName(ExampleLinkName.AIHighlightsGraph);
9 |
10 | vpTest(`Test if video on ESM highlights graph page is playing as expected`, async ({ page, pomPages }) => {
11 | await test.step('Navigate to ESM', async () => {
12 | await page.goto(ESM_URL);
13 | });
14 | await test.step('Navigate to highlights graph page by clicking on link', async () => {
15 | await pomPages.mainPage.clickLinkByName(link.name);
16 | await waitForPageToLoadWithTimeout(page, 5000);
17 | });
18 | await test.step('Click on play button of video player to play video', async () => {
19 | return pomPages.highlightGraphPage.videoHighlightsGraphPage.clickPlay();
20 | });
21 | await test.step('Validating that the video is playing', async () => {
22 | await pomPages.highlightGraphPage.videoHighlightsGraphPage.validateVideoIsPlaying(true);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmMainPageVideoIsPlaying.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { testMainPageVideoIsPlaying } from '../commonSpecs/mainPageVideoPlaying';
3 | import { ESM_URL } from '../../testData/esmUrl';
4 |
5 | /**
6 | * Testing if video on main page is playing by checking that is pause return false.
7 | * The video in the main page is not configured as autoplay so first need to click on play button.
8 | */
9 | vpTest(`Test if video on ESM main page can play as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testMainPageVideoIsPlaying(page, pomPages.mainPage.videoMainPage);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmMultiplePlayersPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testMultiplePlayersPageVideoIsPlaying } from '../commonSpecs/multiplePlayersPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.MultiplePlayers);
8 |
9 | vpTest(`Test if 3 videos on ESM multiple players page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testMultiplePlayersPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmPlaylistByTag.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
4 | import { testPlaylistByTagPageVideoIsPlaying } from '../commonSpecs/playlistByTagPageVideoPlaying';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.PlaylistByTag);
8 |
9 | vpTest(`Test if video on ESM playlist by tag page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testPlaylistByTagPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmPlaylistPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testPlaylistPageVideoIsPlaying } from '../commonSpecs/playlistPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.Playlist);
8 |
9 | vpTest(`Test if 2 videos on ESM playlist page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testPlaylistPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmPosterOptionsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testPosterOptionsPageVideoIsPlaying } from '../commonSpecs/posterOptionsPage';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.PosterOptions);
8 |
9 | vpTest(`Test if 4 videos on ESM poster options page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testPosterOptionsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmProfilesPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testProfilesPageVideoIsPlaying } from '../commonSpecs/profilesPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.Profiles);
8 |
9 | vpTest(`Test if 3 videos on ESM profiles page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testProfilesPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmRawUrlPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testRawUrlPageVideoIsPlaying } from '../commonSpecs/rawUrlPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.RawURL);
8 |
9 | vpTest(`Test if 2 videos on ESM raw URL page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testRawUrlPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmRecommendationsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testRecommendationsPageVideoIsPlaying } from '../commonSpecs/recommendationsPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.Recommendations);
8 |
9 | vpTest(`Test if video on ESM recommendations page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testRecommendationsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmSeekThumbnailsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testSeekThumbnailsPageVideoIsPlaying } from '../commonSpecs/seekThumbnailsPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.SeekThumbnails);
8 |
9 | vpTest(`Test if video on ESM seek thumbnails page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testSeekThumbnailsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmShoppableVideosPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testShoppableVideosPageVideoIsPlaying } from '../commonSpecs/shoppableVideosPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.ShoppableVideos);
8 |
9 | vpTest(`Test if video on ESM shoppable videos page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testShoppableVideosPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmSubtitlesAndCaptionsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testSubtitlesAndCaptionsPageVideoIsPlaying } from '../commonSpecs/subtitlesAndCaptionsPgaeVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.SubtitlesAndCaptions);
8 |
9 | vpTest(`Test if 5 videos on ESM subtitles and captions page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testSubtitlesAndCaptionsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmVastAndVpaidPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testVastAndVpaidPageVideoIsPlaying } from '../commonSpecs/vastAndVpaidPage';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.VASTAndVPAIDSupport);
8 |
9 | vpTest(`Test if 2 videos on ESM vast and vpaid page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testVastAndVpaidPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmVideoTransformationsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testVideoTransformationsPageVideoIsPlaying } from '../commonSpecs/videoTransformationsPage';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.VideoTransformations);
8 |
9 | vpTest(`Test if 3 videos on ESM video transformations page are playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testVideoTransformationsPageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/ESM/esmVr360Page.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
3 | import { testVr360PageVideoIsPlaying } from '../commonSpecs/vr360VideosPageVideoPlaying';
4 | import { getEsmLinkByName } from '../../testData/esmPageLinksData';
5 | import { ESM_URL } from '../../testData/esmUrl';
6 |
7 | const link = getEsmLinkByName(ExampleLinkName.VR360Videos);
8 |
9 | vpTest(`Test if video on ESM VR 360 videos page is playing as expected`, async ({ page, pomPages }) => {
10 | await page.goto(ESM_URL);
11 | await testVr360PageVideoIsPlaying(page, pomPages, link);
12 | });
13 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/analyticsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { test } from '@playwright/test';
3 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
4 | import { getLinkByName } from '../../testData/pageLinksData';
5 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
6 |
7 | // Link to Analytics page
8 | const link = getLinkByName(ExampleLinkName.Analytics);
9 | /**
10 | * Testing if video on analytics page is playing by checking that is pause return false.
11 | */
12 | vpTest(`Test if video on analytics page is playing as expected`, async ({ page, pomPages }) => {
13 | await test.step('Navigate to analytics page by clicking on link', async () => {
14 | await pomPages.mainPage.clickLinkByName(link.name);
15 | await waitForPageToLoadWithTimeout(page, 5000);
16 | });
17 | await test.step('Validating that the video is playing', async () => {
18 | await pomPages.analyticsPage.analyticsVideoComponent.validateVideoIsPlaying(true);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/apiAndEventsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testApiAndEventsPageVideoIsPlaying } from '../commonSpecs/apiAndEventsPageVideoPlaying';
5 |
6 | // Link to API and Events page
7 | const link = getLinkByName(ExampleLinkName.APIAndEvents);
8 |
9 | vpTest(`Test if video on API and Events page is playing as expected`, async ({ page, pomPages }) => {
10 | await testApiAndEventsPageVideoIsPlaying(page, pomPages, link);
11 | });
12 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/audioPlayerPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testAudioPlayerPageVideosArePlaying } from '../commonSpecs/audioPlayerPageVideoPlaying';
5 |
6 | // Link to audio player page
7 | const link = getLinkByName(ExampleLinkName.AudioPlayer);
8 | /**
9 | * Testing if videos on audio player page are playing by checking that is pause return false.
10 | */
11 | vpTest(`Test if 2 videos on audio player page are playing as expected`, async ({ page, pomPages }) => {
12 | await testAudioPlayerPageVideosArePlaying(page, pomPages, link);
13 | });
14 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/autoplayOnScrollPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testAutoplayOnScrollPageVideoIsPlaying } from '../commonSpecs/autoplayOnScrollVideoPlaying';
5 |
6 | // Link to autoplay on scroll page
7 | const link = getLinkByName(ExampleLinkName.AutoplayOnScroll);
8 |
9 | vpTest(`Test if video on autoplay on scroll page is playing as expected`, async ({ page, pomPages }) => {
10 | await testAutoplayOnScrollPageVideoIsPlaying(page, pomPages, link);
11 | });
12 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/chaptersPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testChaptersPageVideosArePlaying } from '../commonSpecs/chaptersPage';
5 |
6 | const link = getLinkByName(ExampleLinkName.Chapters);
7 |
8 | vpTest(`Test if 3 videos on chapters page are playing as expected`, async ({ page, pomPages }) => {
9 | await testChaptersPageVideosArePlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/cldAnalyticsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testCldAnalyticsPageVideoIsPlaying } from '../commonSpecs/cldAnalyticsPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.CloudinaryAnalytics);
7 |
8 | vpTest(`Test if 4 videos on Cloudinary analytics page are playing as expected`, async ({ page, pomPages }) => {
9 | await testCldAnalyticsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/codecsAndFormats.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testCodecsAndFormatsPageVideoIsPlaying } from '../commonSpecs/codecsAndFormatsVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.CodecsAndFormats);
7 |
8 | vpTest(`Test if 3 videos on codecs and formats page are playing as expected`, async ({ page, pomPages }) => {
9 | await testCodecsAndFormatsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/colorsApiPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testColorsApiPageVideoIsPlaying } from '../commonSpecs/colorsApiPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.ColorsAPI);
7 |
8 | vpTest(`Test if 3 videos on colors API page are playing as expected`, async ({ page, pomPages }) => {
9 | await testColorsApiPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/componentsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testComponentsPageVideoIsPlaying } from '../commonSpecs/componentsPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.Components);
7 |
8 | vpTest(`Test if video on components page is playing as expected`, async ({ page, pomPages }) => {
9 | await testComponentsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/displayConfigurationsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testDisplayConfigurationPageVideoIsPlaying } from '../commonSpecs/displayConfigurationPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.DisplayConfigurations);
7 |
8 | vpTest(`Test if video on display configurations page is playing as expected`, async ({ page, pomPages }) => {
9 | await testDisplayConfigurationPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/floatingPlayerPgae.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testFloatingPlayerPageVideoIsPlaying } from '../commonSpecs/floatingPlayerPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.FloatingPlayer);
7 |
8 | vpTest(`Test if video on floating player page is playing as expected`, async ({ page, pomPages }) => {
9 | await testFloatingPlayerPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/fluidLayoutsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testFluidLayoutsPageVideoIsPlaying } from '../commonSpecs/fluidLayoutsPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.FluidLayouts);
7 |
8 | vpTest(`Test if video on fluid layouts page is playing as expected`, async ({ page, pomPages }) => {
9 | await testFluidLayoutsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/forceHlsSubtitlesPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testForceHlsSubtitlesPageVideoIsPlaying } from '../commonSpecs/forceHlsSubtitlesPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.ForceHLSSubtitles);
7 |
8 | vpTest(`Test if video on force HLS subtitles page is playing as expected`, async ({ page, pomPages }) => {
9 | await testForceHlsSubtitlesPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/highlightsGraphPageVideoIsPlaying.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { test } from '@playwright/test';
3 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
4 | import { getLinkByName } from '../../testData/pageLinksData';
5 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
6 |
7 | // Link to AI Highlights Graph page
8 | const link = getLinkByName(ExampleLinkName.AIHighlightsGraph);
9 | /**
10 | * Testing if video on highlights graph page is playing by checking that is pause return false.
11 | */
12 | vpTest(`Test if video on highlights graph page is playing as expected`, async ({ page, pomPages }) => {
13 | await test.step('Navigate to highlights graph page by clicking on link', async () => {
14 | await pomPages.mainPage.clickLinkByName(link.name);
15 | await waitForPageToLoadWithTimeout(page, 5000);
16 | });
17 | await test.step('Validating that the video is playing', async () => {
18 | await pomPages.highlightGraphPage.videoHighlightsGraphPage.validateVideoIsPlaying(true);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/mainPageVideoIsPlaying.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { testMainPageVideoIsPlaying } from '../commonSpecs/mainPageVideoPlaying';
3 |
4 | /**
5 | * Testing if video on main page is playing by checking that is pause return false.
6 | * The video in the main page is not configured as autoplay so first need to click on play button.
7 | */
8 | vpTest(`Test if video on main page can play as expected`, async ({ page, pomPages }) => {
9 | await testMainPageVideoIsPlaying(page, pomPages.mainPage.videoMainPage);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/multiplePlayersPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testMultiplePlayersPageVideoIsPlaying } from '../commonSpecs/multiplePlayersPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.MultiplePlayers);
7 |
8 | vpTest(`Test if 3 videos on multiple players page are playing as expected`, async ({ page, pomPages }) => {
9 | await testMultiplePlayersPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/playlistByTagPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testPlaylistByTagPageVideoIsPlaying } from '../commonSpecs/playlistByTagPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.PlaylistByTag);
7 |
8 | vpTest(`Test if video on playlist by tag page is playing as expected`, async ({ page, pomPages }) => {
9 | await testPlaylistByTagPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/playlistPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testPlaylistPageVideoIsPlaying } from '../commonSpecs/playlistPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.Playlist);
7 |
8 | vpTest(`Test if 2 videos on playlist page are playing as expected`, async ({ page, pomPages }) => {
9 | await testPlaylistPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/posterOptionsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testPosterOptionsPageVideoIsPlaying } from '../commonSpecs/posterOptionsPage';
5 |
6 | const link = getLinkByName(ExampleLinkName.PosterOptions);
7 |
8 | vpTest(`Test if 4 videos on poster options page are playing as expected`, async ({ page, pomPages }) => {
9 | await testPosterOptionsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/profilesPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testProfilesPageVideoIsPlaying } from '../commonSpecs/profilesPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.Profiles);
7 |
8 | vpTest(`Test if 3 videos on profiles page are playing as expected`, async ({ page, pomPages }) => {
9 | await testProfilesPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/rawUrlPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testRawUrlPageVideoIsPlaying } from '../commonSpecs/rawUrlPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.RawURL);
7 |
8 | vpTest(`Test if 2 videos on raw URL page are playing as expected`, async ({ page, pomPages }) => {
9 | await testRawUrlPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/recommendationsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testRecommendationsPageVideoIsPlaying } from '../commonSpecs/recommendationsPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.Recommendations);
7 |
8 | vpTest(`Test if video on recommendations page is playing as expected`, async ({ page, pomPages }) => {
9 | await testRecommendationsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/seekThumbnailsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testSeekThumbnailsPageVideoIsPlaying } from '../commonSpecs/seekThumbnailsPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.SeekThumbnails);
7 |
8 | vpTest(`Test if video on seek thumbnails page is playing as expected`, async ({ page, pomPages }) => {
9 | await testSeekThumbnailsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/shoppableVideosPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testShoppableVideosPageVideoIsPlaying } from '../commonSpecs/shoppableVideosPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.ShoppableVideos);
7 |
8 | vpTest(`Test if video on shoppable videos page is playing as expected`, async ({ page, pomPages }) => {
9 | await testShoppableVideosPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/subtitlesAndCaptionsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testSubtitlesAndCaptionsPageVideoIsPlaying } from '../commonSpecs/subtitlesAndCaptionsPgaeVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.SubtitlesAndCaptions);
7 |
8 | vpTest(`Test if 5 videos on subtitles and captions page are playing as expected`, async ({ page, pomPages }) => {
9 | await testSubtitlesAndCaptionsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/vastAndVpaidPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testVastAndVpaidPageVideoIsPlaying } from '../commonSpecs/vastAndVpaidPage';
5 |
6 | const link = getLinkByName(ExampleLinkName.VASTAndVPAIDSupport);
7 |
8 | vpTest(`Test if 2 videos on vast and vpaid page are playing as expected`, async ({ page, pomPages }) => {
9 | await testVastAndVpaidPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/videoTransformationsPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testVideoTransformationsPageVideoIsPlaying } from '../commonSpecs/videoTransformationsPage';
5 |
6 | const link = getLinkByName(ExampleLinkName.VideoTransformations);
7 |
8 | vpTest(`Test if 3 videos on video transformations page are playing as expected`, async ({ page, pomPages }) => {
9 | await testVideoTransformationsPageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/NonESM/vr360VideosPage.spec.ts:
--------------------------------------------------------------------------------
1 | import { vpTest } from '../../fixtures/vpTest';
2 | import { getLinkByName } from '../../testData/pageLinksData';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { testVr360PageVideoIsPlaying } from '../commonSpecs/vr360VideosPageVideoPlaying';
5 |
6 | const link = getLinkByName(ExampleLinkName.VR360Videos);
7 |
8 | vpTest(`Test if video on VR 360 videos page is playing as expected`, async ({ page, pomPages }) => {
9 | await testVr360PageVideoIsPlaying(page, pomPages, link);
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/specs/Setup/global.setup.ts:
--------------------------------------------------------------------------------
1 | import { ExampleLinkType } from '../../types/exampleLinkType';
2 | import { expect, Page } from '@playwright/test';
3 | import { ExampleLinkName } from '../../testData/ExampleLinkNames';
4 | import { ESM_URL } from '../../testData/esmUrl';
5 |
6 | async function globalSetup(page: Page) {
7 | if (ESM_URL) {
8 | const link: ExampleLinkType = {
9 | name: ExampleLinkName.AdaptiveStreaming,
10 | endpoint: 'adaptive-streaming',
11 | };
12 | await waitForDeployPreviewUrl(link, page);
13 | }
14 | }
15 |
16 | /**
17 | * Waits for a deploy preview URL to become available by making repeated requests and check that link is visible.
18 | */
19 | async function waitForDeployPreviewUrl(link: ExampleLinkType, page: Page): Promise {
20 | await expect(async () => {
21 | await page.goto(ESM_URL);
22 | const linkLocator = page.getByRole('link', { name: link.name, exact: true });
23 | await expect(linkLocator).toBeVisible({ timeout: 10000 });
24 | }).toPass({ intervals: [1_000], timeout: 120000 });
25 | }
26 | export default globalSetup;
27 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/apiAndEventsPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testApiAndEventsPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to api and events page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that the video is playing', async () => {
12 | await pomPages.apiAndEventsPage.apiAndEventsVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/audioPlayerPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testAudioPlayerPageVideosArePlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to audio player page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Click on play button of video player to play video', async () => {
12 | return pomPages.audioPlayerPage.audioPlayerVideoComponent.clickPlay();
13 | });
14 | await test.step('Validating that the first video player is playing', async () => {
15 | await pomPages.audioPlayerPage.audioPlayerVideoComponent.validateVideoIsPlaying(true);
16 | });
17 | await test.step('Click on play button of audio player with transformation to play video', async () => {
18 | return pomPages.audioPlayerPage.audioPlayerWithTransformationVideoComponent.clickPlay();
19 | });
20 | await test.step('Validating that the audio player with transformation is playing', async () => {
21 | await pomPages.audioPlayerPage.audioPlayerWithTransformationVideoComponent.validateVideoIsPlaying(true);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/autoplayOnScrollVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testAutoplayOnScrollPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to autoplay on scroll page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that the video is not playing before scrolling', async () => {
12 | await pomPages.autoplayOnScrollPage.autoplayOnScrollVideoComponent.validateVideoIsPlaying(false);
13 | });
14 | await test.step('Scroll until the video element is visible', async () => {
15 | await pomPages.autoplayOnScrollPage.autoplayOnScrollVideoComponent.locator.scrollIntoViewIfNeeded();
16 | });
17 | await test.step('Validating that the video is auto playing after scrolling', async () => {
18 | await pomPages.autoplayOnScrollPage.autoplayOnScrollVideoComponent.validateVideoIsPlaying(true);
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/chaptersPage.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testChaptersPageVideosArePlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to chapters page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that chapters vtt file video is playing', async () => {
12 | await pomPages.chaptersPage.chaptersVttFIleVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | await test.step('Validating that chapters config object video is playing', async () => {
15 | await pomPages.chaptersPage.chaptersConfigObjectVideoComponent.validateVideoIsPlaying(true);
16 | });
17 | await test.step('Scroll until chapters auto vtt file video element is visible', async () => {
18 | await pomPages.chaptersPage.chapterAutoVttFileVideoComponent.locator.scrollIntoViewIfNeeded();
19 | });
20 | await test.step('Validating that chapters auto vtt file video is playing', async () => {
21 | await pomPages.chaptersPage.chapterAutoVttFileVideoComponent.validateVideoIsPlaying(true);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/codecsAndFormatsVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testCodecsAndFormatsPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to codecs and formats page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that f_auto video is playing', async () => {
12 | await pomPages.codecsAndFormatsPage.codecsAndFormatsFAutoVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | await test.step('Validating that AV1 video is playing', async () => {
15 | await pomPages.codecsAndFormatsPage.codecsAndFormatsAv1VideoComponent.validateVideoIsPlaying(true);
16 | });
17 | await test.step('Scroll until VP9 video element is visible', async () => {
18 | await pomPages.codecsAndFormatsPage.codecsAndFormatsVp9VideoComponent.locator.scrollIntoViewIfNeeded();
19 | });
20 | await test.step('Validating that VP9 video is playing', async () => {
21 | await pomPages.codecsAndFormatsPage.codecsAndFormatsVp9VideoComponent.validateVideoIsPlaying(true);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/colorsApiPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testColorsApiPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to colors API page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that modified color video is playing', async () => {
12 | await pomPages.colorsApiPage.colorsApiColorSkinVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | await test.step('Validating that dark skin video video is playing', async () => {
15 | await pomPages.colorsApiPage.colorsApiDarkSkinVideoComponent.validateVideoIsPlaying(true);
16 | });
17 | await test.step('Scroll until light skin video element is visible', async () => {
18 | await pomPages.colorsApiPage.colorsApiLightSkinVideoComponent.locator.scrollIntoViewIfNeeded();
19 | });
20 | await test.step('Validating that light skin video is playing', async () => {
21 | await pomPages.colorsApiPage.colorsApiLightSkinVideoComponent.validateVideoIsPlaying(true);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/componentsPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testComponentsPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to components page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that components video is playing', async () => {
12 | await pomPages.componentsPage.componentsVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/displayConfigurationPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testDisplayConfigurationPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to display configurations page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that display configuration video is playing', async () => {
12 | await pomPages.displayConfigurationsPage.displayConfigurationsPageVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/floatingPlayerPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testFloatingPlayerPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to floating player page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that floating player video is playing', async () => {
12 | await pomPages.floatingPlayerPage.floatingPlayerVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/fluidLayoutsPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testFluidLayoutsPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to fluid layouts page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that fluid layouts video is playing', async () => {
12 | await pomPages.fluidLayoutsPage.fluidLayoutsVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/forceHlsSubtitlesPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testForceHlsSubtitlesPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to force HLS subtitles page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that force HLS subtitles video is playing', async () => {
12 | await pomPages.forceHlsSubtitlesPage.forceHlsSubtitlesVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/mainPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import { VideoComponent } from '../../components/videoComponent';
4 |
5 | export async function testMainPageVideoIsPlaying(page: Page, videoElement: VideoComponent) {
6 | await test.step('Click on play button to play video', async () => {
7 | await waitForPageToLoadWithTimeout(page, 5000);
8 | return videoElement.clickPlay();
9 | });
10 | await test.step('Validating that the video is playing', async () => {
11 | await videoElement.validateVideoIsPlaying(true);
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/multiplePlayersPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testMultiplePlayersPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to multiple players page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that player 1 video is playing', async () => {
12 | await pomPages.multiplePlayersPage.multiplePlayersPlayer1VideoComponent.validateVideoIsPlaying(true);
13 | });
14 | await test.step('Validating that player 2 video video is playing', async () => {
15 | await pomPages.multiplePlayersPage.multiplePlayersPlayer2VideoComponent.validateVideoIsPlaying(true);
16 | });
17 | await test.step('Scroll until player 3 video element is visible', async () => {
18 | await pomPages.colorsApiPage.colorsApiLightSkinVideoComponent.locator.scrollIntoViewIfNeeded();
19 | });
20 | await test.step('Validating that player 3 video is playing', async () => {
21 | await pomPages.multiplePlayersPage.multiplePlayersPlayer3VideoComponent.validateVideoIsPlaying(true);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/playlistByTagPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testPlaylistByTagPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to playlist by tag page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that playlist by tag video is playing', async () => {
12 | await pomPages.playlistByTagPage.playlistByTagVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/playlistPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testPlaylistPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to playlist page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that horizontal playlist video is playing', async () => {
12 | await pomPages.playlistPage.playlistHorizontalVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | await test.step('Validating that vertical playlist video is playing', async () => {
15 | await pomPages.playlistPage.playlistVerticalVideoComponent.validateVideoIsPlaying(true);
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/rawUrlPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testRawUrlPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to raw URL page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that raw url video is playing', async () => {
12 | await pomPages.rawUrlPage.rawUrlVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | await test.step('Validating that raw url adaptive video is playing', async () => {
15 | await pomPages.rawUrlPage.rawUrlAdaptiveVideoComponent.validateVideoIsPlaying(true);
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/recommendationsPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testRecommendationsPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to recommendations page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that recommendations video is playing', async () => {
12 | await pomPages.recommendationsPage.recommendationsVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/seekThumbnailsPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testSeekThumbnailsPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to seek thumbnails page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that seek thumbnails video is playing', async () => {
12 | await pomPages.seekThumbnailsPage.seekThumbnailsVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/shoppableVideosPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testShoppableVideosPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to shoppable video page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Click on play button of shoppable videos to play video', async () => {
12 | return pomPages.shoppableVideosPage.shoppableVideosVideoComponent.clickPlay();
13 | });
14 | await test.step('Validating that shoppable video is playing', async () => {
15 | await pomPages.shoppableVideosPage.shoppableVideosVideoComponent.validateVideoIsPlaying(true);
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/vastAndVpaidPage.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testVastAndVpaidPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to vast and vpaid page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Click on play button of single video with ads to play video', async () => {
12 | return pomPages.vastAndVpaidPage.singleVideoWithAdsVideoComponent.clickPlay();
13 | });
14 | //Sending timeout of 12 seconds to wait until the ad finishes (10 sec) and the video will start
15 | await test.step('Validating that single video with ads is playing', async () => {
16 | await pomPages.vastAndVpaidPage.singleVideoWithAdsVideoComponent.validateVideoIsPlaying(true, 12000);
17 | });
18 | await test.step('Validating that playlist with ads video is playing', async () => {
19 | await pomPages.vastAndVpaidPage.playlistWithAdsVideoComponent.validateVideoIsPlaying(true, 12000);
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/videoTransformationsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testVideoTransformationsPageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to video transformations page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Validating that via source transformation video is playing', async () => {
12 | await pomPages.videoTransformationsPage.viaSourceVideoComponent.validateVideoIsPlaying(true);
13 | });
14 | await test.step('Validating that via player transformation video is playing', async () => {
15 | await pomPages.videoTransformationsPage.viaPlayerVideoComponent.validateVideoIsPlaying(true);
16 | });
17 | await test.step('Scroll until data cld transformation video element is visible', async () => {
18 | await pomPages.videoTransformationsPage.viaDataCldTransformationsVideoComponent.locator.scrollIntoViewIfNeeded();
19 | });
20 | await test.step('Validating that via data cld transformation video is playing', async () => {
21 | await pomPages.videoTransformationsPage.viaDataCldTransformationsVideoComponent.validateVideoIsPlaying(true);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/test/e2e/specs/commonSpecs/vr360VideosPageVideoPlaying.ts:
--------------------------------------------------------------------------------
1 | import { Page, test } from '@playwright/test';
2 | import { waitForPageToLoadWithTimeout } from '../../src/helpers/waitForPageToLoadWithTimeout';
3 | import PageManager from '../../src/pom/PageManager';
4 | import { ExampleLinkType } from '../../types/exampleLinkType';
5 |
6 | export async function testVr360PageVideoIsPlaying(page: Page, pomPages: PageManager, link: ExampleLinkType) {
7 | await test.step('Navigate to VR 360 videos page by clicking on link', async () => {
8 | await pomPages.mainPage.clickLinkByName(link.name);
9 | await waitForPageToLoadWithTimeout(page, 5000);
10 | });
11 | await test.step('Click on play button of 360 video play video', async () => {
12 | return pomPages.vr360VideosPage.vr360VideoComponent.clickPlay();
13 | });
14 | await test.step('Validating that 360 video is playing', async () => {
15 | await pomPages.vr360VideosPage.vr360VideoComponent.validateVideoIsPlaying(true, 6000);
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/test/e2e/src/helpers/validatePageErrors.ts:
--------------------------------------------------------------------------------
1 | import { ConsoleMessage, expect } from '@playwright/test';
2 |
3 | /**
4 | * Validating that the expected error is part of the console errors
5 | */
6 | export function validatePageErrors(consoleErrors: ConsoleMessage[], expectedErrorMessages: string[], ignoreErrorMessages: string[]): void {
7 | /**
8 | * Filters console messages to exclude ignored messages
9 | */
10 | const relevantMessages = consoleErrors.filter((consoleError) => !ignoreErrorMessages.some((ignoreError) => consoleError.text().includes(ignoreError)));
11 |
12 | /**
13 | * Filters expected error messages that are not found in relevant console messages
14 | */
15 | const missingExpectedErrors = expectedErrorMessages.filter((expectedErrorMessage) => !relevantMessages.some((relevantError) => relevantError.text().includes(expectedErrorMessage)));
16 |
17 | expect(missingExpectedErrors.length, `The following expected console errors were not found: ${JSON.stringify(missingExpectedErrors)}`).toBe(0);
18 |
19 | /**
20 | * Filters relevant console messages that are not part of the expected error messages
21 | */
22 | const unexpectedErrors = relevantMessages.filter((relevantError) => !expectedErrorMessages.some((expectedErrorMessage) => relevantError.text().includes(expectedErrorMessage)));
23 |
24 | expect(unexpectedErrors.length, `The following unexpected console errors were found: ${JSON.stringify(unexpectedErrors)}`).toBe(0);
25 | }
26 |
--------------------------------------------------------------------------------
/test/e2e/src/helpers/waitForPageToLoadWithTimeout.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 |
3 | /**
4 | * Wait until there are no network connections for at least 5000 ms or for given timeout to pass.
5 | * Needed for console logs to appear. and in pages that loading video 'waitForLoadState('networkidle')' entering a long loop.
6 | */
7 | export async function waitForPageToLoadWithTimeout(page: Page, timeout: number): Promise {
8 | return Promise.race([page.waitForLoadState('networkidle'), new Promise((r) => setTimeout(r, timeout))]);
9 | }
10 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/BasePage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 |
3 | /**
4 | * Base Page represent an example page
5 | * Such as main page, highlight graph page etc.
6 | */
7 | export class BasePage {
8 | protected page: Page;
9 |
10 | constructor(page: Page) {
11 | this.page = page;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/analyticsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const ANALYTICS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples analytics page object
8 | */
9 | export class AnalyticsPage extends BasePage {
10 | public analyticsVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.analyticsVideoComponent = new VideoComponent(page, ANALYTICS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/apiAndEventsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const API_AND_EVENTS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples api and events page object
8 | */
9 | export class ApiAndEventsPage extends BasePage {
10 | public apiAndEventsVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.apiAndEventsVideoComponent = new VideoComponent(page, API_AND_EVENTS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/audioPlayerPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const AUDIO_PLAYER_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 | const AUDIO_PLAYER_WITH_TRANSFORMATION_VIDEO_SELECTOR = '//*[@id="player-t_html5_api"]';
6 | /**
7 | * Video player examples audio player page object
8 | */
9 | export class AudioPlayerPage extends BasePage {
10 | public audioPlayerVideoComponent: VideoComponent;
11 | public audioPlayerWithTransformationVideoComponent: VideoComponent;
12 |
13 | constructor(page: Page) {
14 | super(page);
15 | this.audioPlayerVideoComponent = new VideoComponent(page, AUDIO_PLAYER_VIDEO_SELECTOR);
16 | this.audioPlayerWithTransformationVideoComponent = new VideoComponent(page, AUDIO_PLAYER_WITH_TRANSFORMATION_VIDEO_SELECTOR);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/autoplayOnScrollPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const AUTOPLAY_ON_SCROLL_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples autoplay on scroll page object
8 | */
9 | export class AutoplayOnScrollPage extends BasePage {
10 | public autoplayOnScrollVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.autoplayOnScrollVideoComponent = new VideoComponent(page, AUTOPLAY_ON_SCROLL_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/chaptersPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const CHAPTERS_PAGE_VTT_FILE_VIDEO_SELECTOR = '//*[@id="player-vtt_html5_api"]';
5 | const CHAPTERS_PAGE_CONFIG_OBJECT_VIDEO_SELECTOR = '//*[@id="player-config_html5_api"]';
6 | const CHAPTERS_PAGE_AUTO_VTT_FILE_VIDEO_SELECTOR = '//*[@id="player-auto-vtt_html5_api"]';
7 |
8 | /**
9 | * Video player examples chapters page object
10 | */
11 | export class ChaptersPage extends BasePage {
12 | public chaptersVttFIleVideoComponent: VideoComponent;
13 | public chaptersConfigObjectVideoComponent: VideoComponent;
14 | public chapterAutoVttFileVideoComponent: VideoComponent;
15 |
16 | constructor(page: Page) {
17 | super(page);
18 | this.chaptersVttFIleVideoComponent = new VideoComponent(page, CHAPTERS_PAGE_VTT_FILE_VIDEO_SELECTOR);
19 | this.chaptersConfigObjectVideoComponent = new VideoComponent(page, CHAPTERS_PAGE_CONFIG_OBJECT_VIDEO_SELECTOR);
20 | this.chapterAutoVttFileVideoComponent = new VideoComponent(page, CHAPTERS_PAGE_AUTO_VTT_FILE_VIDEO_SELECTOR);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/cldAnalyticsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const CLD_ANALYTICS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 | const CLD_ANALYTICS_PAGE_ADP_VIDEO_SELECTOR = '//*[@id="adpPlayer_html5_api"]';
6 | const CLD_ANALYTICS_PAGE_CUSTOM_DATA_OBJECT_VIDEO_SELECTOR = '//*[@id="player-custom-data-plain-object_html5_api"]';
7 | const CLD_ANALYTICS_PAGE_CUSTOM_DATA_FUNCTION_VIDEO_SELECTOR = '//*[@id="player-custom-data-function_html5_api"]';
8 |
9 | /**
10 | * Video player examples chapters page object
11 | */
12 | export class CldAnalyticsPage extends BasePage {
13 | public cldAnalyticsVideoComponent: VideoComponent;
14 | public cldAnalyticsAdpVideoComponent: VideoComponent;
15 | public cldAnalyticsCustomDataObjectVideoComponent: VideoComponent;
16 | public cldAnalyticsCustomDataFunctionVideoComponent: VideoComponent;
17 |
18 | constructor(page: Page) {
19 | super(page);
20 | this.cldAnalyticsVideoComponent = new VideoComponent(page, CLD_ANALYTICS_PAGE_VIDEO_SELECTOR);
21 | this.cldAnalyticsAdpVideoComponent = new VideoComponent(page, CLD_ANALYTICS_PAGE_ADP_VIDEO_SELECTOR);
22 | this.cldAnalyticsCustomDataObjectVideoComponent = new VideoComponent(page, CLD_ANALYTICS_PAGE_CUSTOM_DATA_OBJECT_VIDEO_SELECTOR);
23 | this.cldAnalyticsCustomDataFunctionVideoComponent = new VideoComponent(page, CLD_ANALYTICS_PAGE_CUSTOM_DATA_FUNCTION_VIDEO_SELECTOR);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/codecsAndFormats.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const CODECS_AND_FORMAT_PAGE_F_AUTO_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 | const CODECS_AND_FORMAT_PAGE_AV1_VIDEO_SELECTOR = '//*[@id="player-av1_html5_api"]';
6 | const CODECS_AND_FORMAT_PAGE_VP9_VIDEO_SELECTOR = '//*[@id="player-vp9_html5_api"]';
7 |
8 | /**
9 | * Video player examples chapters page object
10 | */
11 | export class CodecsAndFormats extends BasePage {
12 | public codecsAndFormatsFAutoVideoComponent: VideoComponent;
13 | public codecsAndFormatsAv1VideoComponent: VideoComponent;
14 | public codecsAndFormatsVp9VideoComponent: VideoComponent;
15 |
16 | constructor(page: Page) {
17 | super(page);
18 | this.codecsAndFormatsFAutoVideoComponent = new VideoComponent(page, CODECS_AND_FORMAT_PAGE_F_AUTO_VIDEO_SELECTOR);
19 | this.codecsAndFormatsAv1VideoComponent = new VideoComponent(page, CODECS_AND_FORMAT_PAGE_AV1_VIDEO_SELECTOR);
20 | this.codecsAndFormatsVp9VideoComponent = new VideoComponent(page, CODECS_AND_FORMAT_PAGE_VP9_VIDEO_SELECTOR);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/colorsApiPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const COLORS_API_PAGE_COLOR_SKIN_VIDEO_SELECTOR = '//*[@id="player-1_html5_api"]';
5 | const COLORS_API_PAGE_DARK_SKIN_VIDEO_SELECTOR = '//*[@id="player-2_html5_api"]';
6 | const COLORS_API_PAGE_LIGHT_SKIN_VIDEO_SELECTOR = '//*[@id="player-3_html5_api"]';
7 |
8 | /**
9 | * Video player examples colors API page object
10 | */
11 | export class ColorsApiPage extends BasePage {
12 | public colorsApiColorSkinVideoComponent: VideoComponent;
13 | public colorsApiDarkSkinVideoComponent: VideoComponent;
14 | public colorsApiLightSkinVideoComponent: VideoComponent;
15 |
16 | constructor(page: Page) {
17 | super(page);
18 | this.colorsApiColorSkinVideoComponent = new VideoComponent(page, COLORS_API_PAGE_COLOR_SKIN_VIDEO_SELECTOR);
19 | this.colorsApiDarkSkinVideoComponent = new VideoComponent(page, COLORS_API_PAGE_DARK_SKIN_VIDEO_SELECTOR);
20 | this.colorsApiLightSkinVideoComponent = new VideoComponent(page, COLORS_API_PAGE_LIGHT_SKIN_VIDEO_SELECTOR);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/componentsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const COMPONENTS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples components page object
8 | */
9 | export class ComponentsPage extends BasePage {
10 | public componentsVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.componentsVideoComponent = new VideoComponent(page, COMPONENTS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/displayConfigurationsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const DISPLAY_CONFIGURATIONS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples components page object
8 | */
9 | export class DisplayConfigurationsPage extends BasePage {
10 | public displayConfigurationsPageVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.displayConfigurationsPageVideoComponent = new VideoComponent(page, DISPLAY_CONFIGURATIONS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/floatingPlayerPgae.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const FLOATING_PLAYER_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples Floating player page object
8 | */
9 | export class FloatingPlayerPage extends BasePage {
10 | public floatingPlayerVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.floatingPlayerVideoComponent = new VideoComponent(page, FLOATING_PLAYER_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/fluidLayoutsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const FLUID_LAYOUTS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples fluid layouts page object
8 | */
9 | export class FluidLayoutsPage extends BasePage {
10 | public fluidLayoutsVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.fluidLayoutsVideoComponent = new VideoComponent(page, FLUID_LAYOUTS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/forceHlsSubtitlesPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const FORCE_HLS_SUBTITLES_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples force HLS subtitles page object
8 | */
9 | export class ForceHlsSubtitlesPage extends BasePage {
10 | public forceHlsSubtitlesVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.forceHlsSubtitlesVideoComponent = new VideoComponent(page, FORCE_HLS_SUBTITLES_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/highlightsGraphPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const HIGHLIGHTS_GRAPH_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples highlight graph page object
8 | */
9 | export class HighlightsGraphPage extends BasePage {
10 | public videoHighlightsGraphPage: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.videoHighlightsGraphPage = new VideoComponent(page, HIGHLIGHTS_GRAPH_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/mainPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const MAIN_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples main page object
8 | */
9 | export class MainPage extends BasePage {
10 | public videoMainPage: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.videoMainPage = new VideoComponent(page, MAIN_PAGE_VIDEO_SELECTOR);
15 | }
16 |
17 | /**
18 | * Click links by given name.
19 | */
20 | public async clickLinkByName(name: string): Promise {
21 | await this.page.getByRole('link', { name, exact: true }).click();
22 | await this.page.waitForLoadState('load');
23 | }
24 |
25 | /**
26 | * Navigate to local examples page.
27 | */
28 | public async goto(): Promise {
29 | await this.page.goto('http://localhost:3000/index.html');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/multiplePlayersPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const MULTIPLE_PLAYERS_PAGE_PLAYER_1_VIDEO_SELECTOR = '//*[@id="player-1_html5_api"]';
5 | const MULTIPLE_PLAYERS_PAGE_PLAYER_2_VIDEO_SELECTOR = '//*[@id="player-2_html5_api"]';
6 | const MULTIPLE_PLAYERS_PAGE_PLAYER_3_VIDEO_SELECTOR = '//*[@id="player-3_html5_api"]';
7 |
8 | /**
9 | * Video player examples colors API page object
10 | */
11 | export class MultiplePlayersPage extends BasePage {
12 | public multiplePlayersPlayer1VideoComponent: VideoComponent;
13 | public multiplePlayersPlayer2VideoComponent: VideoComponent;
14 | public multiplePlayersPlayer3VideoComponent: VideoComponent;
15 |
16 | constructor(page: Page) {
17 | super(page);
18 | this.multiplePlayersPlayer1VideoComponent = new VideoComponent(page, MULTIPLE_PLAYERS_PAGE_PLAYER_1_VIDEO_SELECTOR);
19 | this.multiplePlayersPlayer2VideoComponent = new VideoComponent(page, MULTIPLE_PLAYERS_PAGE_PLAYER_2_VIDEO_SELECTOR);
20 | this.multiplePlayersPlayer3VideoComponent = new VideoComponent(page, MULTIPLE_PLAYERS_PAGE_PLAYER_3_VIDEO_SELECTOR);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/playlistByTagPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const PLAYLIST_BY_TAG_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples playlist by tag page object
8 | */
9 | export class PlaylistByTagPage extends BasePage {
10 | public playlistByTagVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.playlistByTagVideoComponent = new VideoComponent(page, PLAYLIST_BY_TAG_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/playlistPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const PLAYLIST_PAGE_HORIZONTAL_VIDEO_SELECTOR = '//*[@id="player-horizontal_html5_api"]';
5 | const PLAYLIST_PAGE_VERTICAL_VIDEO_SELECTOR = '//*[@id="player-vertical_html5_api"]';
6 |
7 | /**
8 | * Video player examples playlist page object
9 | */
10 | export class PlaylistPage extends BasePage {
11 | public playlistHorizontalVideoComponent: VideoComponent;
12 | public playlistVerticalVideoComponent: VideoComponent;
13 |
14 | constructor(page: Page) {
15 | super(page);
16 | this.playlistHorizontalVideoComponent = new VideoComponent(page, PLAYLIST_PAGE_HORIZONTAL_VIDEO_SELECTOR);
17 | this.playlistVerticalVideoComponent = new VideoComponent(page, PLAYLIST_PAGE_VERTICAL_VIDEO_SELECTOR);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/posterOptionsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const POSTER_OPTIONS_PAGE_CUSTOM_IMAGE_VIDEO_SELECTOR = '//*[@id="player-image-poster_html5_api"]';
5 | const POSTER_OPTIONS_PAGE_SPECIFIC_FRAME_VIDEO_SELECTOR = '//*[@id="player-frame-0_html5_api"]';
6 | const POSTER_OPTIONS_PAGE_TRANSFORMATIONS_ARRAY_VIDEO_SELECTOR = '//*[@id="player-poster-options_html5_api"]';
7 | const POSTER_OPTIONS_PAGE_RAW_URL_NO_POSTER_VIDEO_SELECTOR = '//*[@id="player-raw_html5_api"]';
8 |
9 | /**
10 | * Video player examples poster options page object
11 | */
12 | export class PosterOptionsPage extends BasePage {
13 | public posterOptionsCustomImageVideoComponent: VideoComponent;
14 | public posterOptionsSpecificFrameVideoComponent: VideoComponent;
15 | public posterOptionsTransformationsArrayVideoComponent: VideoComponent;
16 | public posterOptionsRawUrlNoPosterVideoComponent: VideoComponent;
17 |
18 | constructor(page: Page) {
19 | super(page);
20 | this.posterOptionsCustomImageVideoComponent = new VideoComponent(page, POSTER_OPTIONS_PAGE_CUSTOM_IMAGE_VIDEO_SELECTOR);
21 | this.posterOptionsSpecificFrameVideoComponent = new VideoComponent(page, POSTER_OPTIONS_PAGE_SPECIFIC_FRAME_VIDEO_SELECTOR);
22 | this.posterOptionsTransformationsArrayVideoComponent = new VideoComponent(page, POSTER_OPTIONS_PAGE_TRANSFORMATIONS_ARRAY_VIDEO_SELECTOR);
23 | this.posterOptionsRawUrlNoPosterVideoComponent = new VideoComponent(page, POSTER_OPTIONS_PAGE_RAW_URL_NO_POSTER_VIDEO_SELECTOR);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/profilesPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const PROFILES_PAGE_DEFAULT_PROFILE_VIDEO_SELECTOR = '//*[@id="player-default-profile_html5_api"]';
5 | const PROFILES_PAGE_CUSTOM_PROFILE_VIDEO_SELECTOR = '//*[@id="player-custom-profile_html5_api"]';
6 | const PROFILES_PAGE_CUSTOM_PROFILE_OVERRIDES_VIDEO_SELECTOR = '//*[@id="player-custom-profile-overrides_html5_api"]';
7 |
8 | /**
9 | * Video player examples profiles page object
10 | */
11 | export class ProfilesPage extends BasePage {
12 | public profilesDefaultProfileVideoComponent: VideoComponent;
13 | public profilesCustomProfileVideoComponent: VideoComponent;
14 | public profilesCustomProfileOverridesVideoComponent: VideoComponent;
15 |
16 | constructor(page: Page) {
17 | super(page);
18 | this.profilesDefaultProfileVideoComponent = new VideoComponent(page, PROFILES_PAGE_DEFAULT_PROFILE_VIDEO_SELECTOR);
19 | this.profilesCustomProfileVideoComponent = new VideoComponent(page, PROFILES_PAGE_CUSTOM_PROFILE_VIDEO_SELECTOR);
20 | this.profilesCustomProfileOverridesVideoComponent = new VideoComponent(page, PROFILES_PAGE_CUSTOM_PROFILE_OVERRIDES_VIDEO_SELECTOR);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/rawUrlPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const RAW_URL_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 | const RAW_URL_PAGE_ADAPTIVE_VIDEO_SELECTOR = '//*[@id="adpPlayer_html5_api"]';
6 |
7 | /**
8 | * Video player examples raw URL page object
9 | */
10 | export class RawUrlPage extends BasePage {
11 | public rawUrlVideoComponent: VideoComponent;
12 | public rawUrlAdaptiveVideoComponent: VideoComponent;
13 |
14 | constructor(page: Page) {
15 | super(page);
16 | this.rawUrlVideoComponent = new VideoComponent(page, RAW_URL_PAGE_VIDEO_SELECTOR);
17 | this.rawUrlAdaptiveVideoComponent = new VideoComponent(page, RAW_URL_PAGE_ADAPTIVE_VIDEO_SELECTOR);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/recommendationsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const RECOMMENDATIONS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples recommendations page object
8 | */
9 | export class RecommendationsPage extends BasePage {
10 | public recommendationsVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.recommendationsVideoComponent = new VideoComponent(page, RECOMMENDATIONS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/seekThumbnailsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const SEEK_THUMBNAILS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples seek thumbnails page object
8 | */
9 | export class SeekThumbnailsPage extends BasePage {
10 | public seekThumbnailsVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.seekThumbnailsVideoComponent = new VideoComponent(page, SEEK_THUMBNAILS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/shoppableVideosPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const SHOPPABLE_VIDEOS_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples shoppable vidoes page object
8 | */
9 | export class ShoppableVideosPage extends BasePage {
10 | public shoppableVideosVideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.shoppableVideosVideoComponent = new VideoComponent(page, SHOPPABLE_VIDEOS_PAGE_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/subtitlesAndCaptionsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const SRT_AND_VTT_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 | const PLAYLIST_SUBTITLES_VIDEO_SELECTOR = '//*[@id="playlist_html5_api"]';
6 | const PACED_STYLES_CAPTIONS_VIDEO_SELECTOR = '//*[@id="paced_html5_api"]';
7 | const KARAOKE_VIDEO_SELECTOR = '//*[@id="karaoke_html5_api"]';
8 | const TRANSLATED_TRANSCRIPT_VIDEO_SELECTOR = '//*[@id="translated-transcript_html5_api"]';
9 | /**
10 | * Video player examples subtitles and captions page object
11 | */
12 | export class SubtitlesAndCaptionsPage extends BasePage {
13 | public srtAndVttVideoComponent: VideoComponent;
14 | public playlistSubtitlesVideoComponent: VideoComponent;
15 | public pacedStyledVideoComponent: VideoComponent;
16 | public karaokeVideoComponent: VideoComponent;
17 | public translatedTranscriptVideoComponent: VideoComponent;
18 |
19 | constructor(page: Page) {
20 | super(page);
21 | this.srtAndVttVideoComponent = new VideoComponent(page, SRT_AND_VTT_VIDEO_SELECTOR);
22 | this.playlistSubtitlesVideoComponent = new VideoComponent(page, PLAYLIST_SUBTITLES_VIDEO_SELECTOR);
23 | this.pacedStyledVideoComponent = new VideoComponent(page, PACED_STYLES_CAPTIONS_VIDEO_SELECTOR);
24 | this.karaokeVideoComponent = new VideoComponent(page, KARAOKE_VIDEO_SELECTOR);
25 | this.translatedTranscriptVideoComponent = new VideoComponent(page, TRANSLATED_TRANSCRIPT_VIDEO_SELECTOR);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/vastAndVpaidPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const SINGLE_VIDEO_WITH_ADS_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 | const PLAYLIST_WITH_ADS_VIDEO_SELECTOR = '//*[@id="player-playlist_html5_api"]';
6 |
7 | /**
8 | * Video player examples vast and vpaid page object
9 | */
10 | export class VastAndVpaidPage extends BasePage {
11 | public singleVideoWithAdsVideoComponent: VideoComponent;
12 | public playlistWithAdsVideoComponent: VideoComponent;
13 |
14 | constructor(page: Page) {
15 | super(page);
16 | this.singleVideoWithAdsVideoComponent = new VideoComponent(page, SINGLE_VIDEO_WITH_ADS_VIDEO_SELECTOR);
17 | this.playlistWithAdsVideoComponent = new VideoComponent(page, PLAYLIST_WITH_ADS_VIDEO_SELECTOR);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/videoTransformationsPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const VIA_SOURCE_VIDEO_SELECTOR = '//*[@id="player-1_html5_api"]';
5 | const VIA_PLAYER_VIDEO_SELECTOR = '//*[@id="player-2_html5_api"]';
6 | const VIA_DATA_CLD_TRANSFORMATIONS_VIDEO_SELECTOR = '//*[@id="player-3_html5_api"]';
7 |
8 | /**
9 | * Video player examples video transformations page object
10 | */
11 | export class VideoTransformationsPage extends BasePage {
12 | public viaSourceVideoComponent: VideoComponent;
13 | public viaPlayerVideoComponent: VideoComponent;
14 | public viaDataCldTransformationsVideoComponent: VideoComponent;
15 |
16 | constructor(page: Page) {
17 | super(page);
18 | this.viaSourceVideoComponent = new VideoComponent(page, VIA_SOURCE_VIDEO_SELECTOR);
19 | this.viaPlayerVideoComponent = new VideoComponent(page, VIA_PLAYER_VIDEO_SELECTOR);
20 | this.viaDataCldTransformationsVideoComponent = new VideoComponent(page, VIA_DATA_CLD_TRANSFORMATIONS_VIDEO_SELECTOR);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/visualSearchPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 |
5 | const VISUAL_SEARCH_PAGE_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
6 | const VISUAL_SEARCH_PLAYLIST_VIDEO_SELECTOR = '//*[@id="player-playlist_html5_api"]';
7 |
8 | /**
9 | * Video player examples visual search page object
10 | */
11 | export class VisualSearchPage extends BasePage {
12 | public visualSearchVideoComponent: VideoComponent;
13 | public visualSearchPlaylistVideoComponent: VideoComponent;
14 |
15 | constructor(page: Page) {
16 | super(page);
17 | this.visualSearchVideoComponent = new VideoComponent(page, VISUAL_SEARCH_PAGE_VIDEO_SELECTOR);
18 | this.visualSearchPlaylistVideoComponent = new VideoComponent(page, VISUAL_SEARCH_PLAYLIST_VIDEO_SELECTOR);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/e2e/src/pom/vr360VideosPage.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test';
2 | import { VideoComponent } from '../../components/videoComponent';
3 | import { BasePage } from './BasePage';
4 | const VR_360_VIDEO_SELECTOR = '//*[@id="player_html5_api"]';
5 |
6 | /**
7 | * Video player examples VR 360 videos page object
8 | */
9 | export class Vr360VideosPage extends BasePage {
10 | public vr360VideoComponent: VideoComponent;
11 |
12 | constructor(page: Page) {
13 | super(page);
14 | this.vr360VideoComponent = new VideoComponent(page, VR_360_VIDEO_SELECTOR);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/testData/ExampleLinkNames.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Enum representing the names of example pages.
3 | */
4 | export enum ExampleLinkName {
5 | AdaptiveStreaming = 'Adaptive Streaming',
6 | AIHighlightsGraph = 'AI Highlights Graph',
7 | Analytics = 'Analytics',
8 | APIAndEvents = 'API and Events',
9 | AudioPlayer = 'Audio Player',
10 | AutoplayOnScroll = 'Autoplay on Scroll',
11 | Chapters = 'Chapters',
12 | CloudinaryAnalytics = 'Cloudinary Analytics',
13 | CodecsAndFormats = 'Codecs and formats',
14 | ColorsAPI = 'Colors API',
15 | Components = 'Components',
16 | CustomErrors = 'Custom Errors',
17 | DisplayConfigurations = 'Display Configurations',
18 | DebugMode = 'Debug mode',
19 | FloatingPlayer = 'Floating Player',
20 | FluidLayouts = 'Fluid Layouts',
21 | ForceHLSSubtitles = 'Force HLS Subtitles',
22 | InteractionArea = 'Interaction Area',
23 | MultiplePlayers = 'Multiple Players',
24 | Playlist = 'Playlist',
25 | PlaylistByTag = 'Playlist by Tag',
26 | PosterOptions = 'Poster Options',
27 | Profiles = 'Profiles',
28 | RawURL = 'Raw URL',
29 | Recommendations = 'Recommendations',
30 | SeekThumbnails = 'Seek Thumbnails',
31 | ShoppableVideos = 'Shoppable Videos',
32 | SubtitlesAndCaptions = 'Subtitles & Captions',
33 | VideoTransformations = 'Video Transformations',
34 | VASTAndVPAIDSupport = 'VAST & VPAID Support',
35 | VR360Videos = 'VR/360 Videos',
36 | EmbeddedIframePlayer = 'Embedded (iframe) player',
37 | ESMImports = 'ESM Imports',
38 | AllBuild = '/all build',
39 | VisualSearch = 'Visual Search',
40 | }
41 |
--------------------------------------------------------------------------------
/test/e2e/testData/esmUrl.ts:
--------------------------------------------------------------------------------
1 | // On PR level it will use the preview deploy URL and locally it will use the latest EDGE.
2 | export const ESM_URL = process.env.PREVIEW_URL ?? 'https://cld-vp-esm-pages.netlify.app/';
3 |
--------------------------------------------------------------------------------
/test/e2e/types/exampleLinkType.ts:
--------------------------------------------------------------------------------
1 | import { ExampleLinkName } from '../testData/ExampleLinkNames';
2 |
3 | /**
4 | * Example links type
5 | */
6 | export type ExampleLinkType = { name: ExampleLinkName; endpoint: string };
7 |
--------------------------------------------------------------------------------
/test/fluid.test.js:
--------------------------------------------------------------------------------
1 | describe('Fluid tests', () => {
2 |
3 | beforeEach(async () => {
4 | await page.setViewport({width: 1280, height: 800});
5 | await page.goto('http://localhost:3000/fluid.html', {waitUntil: 'load'});
6 | await page.evaluate(() => {
7 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
8 | get: function() {
9 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
10 | this.readyState > 2);
11 | }
12 | });
13 | });
14 | }, 10000);
15 |
16 | it('Test fluid change', async () => {
17 | await page.waitFor(1000);
18 | const origWidth = await page.$eval('#player_html5_api', p => p.clientWidth);
19 | await page.setViewport({ width: 800, height: 800 });
20 | await page.waitFor(1000);
21 | const currWidth = await page.$eval('#player_html5_api', p => p.clientWidth);
22 | expect(origWidth).toBeGreaterThan(currWidth);
23 | });
24 |
25 | it('Test no fluid change', async () => {
26 | await page.waitFor(1000);
27 | await page.click('#toggle-fluid');
28 | await page.waitFor(500);
29 | const origWidth = await page.$eval('#player_html5_api', p => p.clientWidth);
30 | await page.setViewport({ width: 800, height: 800 });
31 | await page.waitFor(1000);
32 | const currWidth = await page.$eval('#player_html5_api', p => p.clientWidth);
33 | expect(origWidth).toEqual(currWidth);
34 | });
35 |
36 | });
37 |
--------------------------------------------------------------------------------
/test/mocks/cloudinary-core-mock.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudinary/cloudinary-video-player/847f64997b6c8527858e16c45f718fcf9da2ff46/test/mocks/cloudinary-core-mock.js
--------------------------------------------------------------------------------
/test/mocks/styleMock.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/test/multiplayer.test.js:
--------------------------------------------------------------------------------
1 | describe('Multi-player tests', () => {
2 |
3 | beforeEach(async () => {
4 | await page.setViewport({ width: 1280, height: 1800 });
5 | await page.goto('http://localhost:3000/multiple-players.html', { waitUntil: 'load' });
6 | await page.evaluate(() => {
7 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
8 | get: function() {
9 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
10 | this.readyState > 2);
11 | }
12 | });
13 | });
14 | }, 10000);
15 |
16 | it('Test play', async () => {
17 | page.waitFor(1500);
18 | const players = await page.$$eval('video', v => v);
19 | for (const player of players) {
20 | const id = '#' + player.playerId + '_html5_api';
21 | await page.waitFor(1500);
22 | expect(await page.$eval(id, el => el.playing)).toBe(true);
23 | }
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/puppeteer/vp-env.js:
--------------------------------------------------------------------------------
1 | const PuppeteerEnvironment = require('jest-environment-puppeteer');
2 |
3 | class VideoPlayerEnvironment extends PuppeteerEnvironment {
4 | async setup() {
5 | await super.setup();
6 | // Your setup
7 | }
8 |
9 | async teardown() {
10 | // Your teardown
11 | try {
12 | await super.teardown();
13 | } catch (e) {
14 | console.log(e);
15 | }
16 | }
17 | }
18 |
19 | module.exports = VideoPlayerEnvironment;
20 |
--------------------------------------------------------------------------------
/test/title-bar.test.js:
--------------------------------------------------------------------------------
1 | describe('basic player tests', () => {
2 | beforeAll(async () => {
3 | jest.setTimeout(35000);
4 | await page.setViewport({width: 1280, height: 800});
5 | await page.goto('http://localhost:3000/', {waitUntil: 'load'});
6 | await page.evaluate(() => {
7 | Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
8 | get: function() {
9 | return !!(this.currentTime > 0 && !this.paused && !this.ended &&
10 | this.readyState > 2);
11 | }
12 | });
13 | });
14 | }, 10000);
15 | it('Test title bar title', async () => {
16 | let ds = JSON.parse(await page.$eval('#player', v => v.getAttribute('data-cld-source')));
17 | let titlebarTitle = await page.$eval('#player > .vjs-title-bar > .vjs-title-bar-title', t => t.textContent);
18 | await expect(ds.info.title).toEqual(titlebarTitle);
19 | });
20 | it('Test title bar subtitle', async () => {
21 | let ds = JSON.parse(await page.$eval('#player', v => v.getAttribute('data-cld-source')));
22 | let sub = await page.$eval('#player > .vjs-title-bar > .vjs-title-bar-subtitle', t => t.textContent);
23 | await expect(ds.info.subtitle).toEqual(sub);
24 | });
25 |
26 | });
27 |
28 |
29 |
--------------------------------------------------------------------------------
/test/unit/cloudinaryConfig.test.js:
--------------------------------------------------------------------------------
1 | import '../../src/';
2 | import VideoPlayer from '../../src/video-player';
3 |
4 | describe('secure true test', () => {
5 | it('test force secure true', async () => {
6 | jest.useFakeTimers();
7 | document.body.innerHTML = '';
8 | const vp = new VideoPlayer('test', { hideContextMenu: true, cloudinaryConfig: { cloud_name: 'demo' } }, false);
9 | const conf = vp.videojs.cloudinary.cloudinaryConfig();
10 | expect(conf.secure).toEqual(true);
11 | });
12 | it('test explicit secure false', async () => {
13 | jest.useFakeTimers();
14 | document.body.innerHTML = '';
15 | const vp = new VideoPlayer('test', { hideContextMenu: true, cloudinaryConfig: { cloud_name: 'demo', secure: false } }, false);
16 | const conf = vp.videojs.cloudinary.cloudinaryConfig();
17 | expect(conf.secure).toEqual(false);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/unit/cloudinaryUtils.test.js:
--------------------------------------------------------------------------------
1 | const tracks = [
2 | {
3 | default: false,
4 | kind: 'subtitles',
5 | label: 'German subtitles',
6 | src: 'https://res.cloudinary.com/yaronr/raw/upload/v1558966008/Meetup_german.vtt',
7 | srclang: 'de'
8 | },
9 | {
10 | default: false,
11 | kind: 'subtitles',
12 | label: 'Hebrew subtitles',
13 | src: 'https://res.cloudinary.com/yaronr/raw/upload/v1558966008/Meetup_hebrew.vtt',
14 | srclang: 'he'
15 | },
16 | {
17 | default: false,
18 | kind: 'subtitles',
19 | label: 'Swedish subtitles',
20 | src: 'https://res.cloudinary.com/yaronr/raw/upload/v1558966008/Meetup_swedish.vtt',
21 | srclang: 'se'
22 | }
23 | ];
24 |
25 | global.fetch = jest.fn((url, options) => {
26 | return new Promise((resolve) => {
27 | if (url === tracks[2].src) {
28 | resolve({ status: 404 });
29 | } else {
30 | resolve({ status: 200 });
31 | }
32 | });
33 | });
34 |
35 | import { addTextTracks } from '../../src/utils/cloudinary';
36 |
37 | describe('video source tests', () => {
38 | it('test filter out bad vtt', async () => {
39 | let vjs = jest.createMockFromModule('video.js');
40 | vjs.addRemoteTextTrack = jest.fn();
41 | jest.spyOn(vjs, 'addRemoteTextTrack');
42 | addTextTracks(tracks, vjs);
43 | setTimeout(() => {
44 | expect(vjs.addRemoteTextTrack).toBeCalledTimes(2);
45 | }, 1000);
46 | });
47 |
48 | });
49 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "noImplicitAny": true,
5 | "esModuleInterop": true,
6 | "allowSyntheticDefaultImports": true,
7 | "declaration": true,
8 | "declarationDir": "./types"
9 | },
10 | "include": [
11 | "dist/**/*",
12 | "lib/**/*",
13 | "types/**/*"
14 | ],
15 | "exclude": [
16 | "node_modules"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/types/cld-video-player-tests.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is used to validate the type spec. The code does not actually run.
3 | */
4 |
5 | import cloudinary, { videoPlayer, videoPlayerWithProfile, VideoPlayer } from './cld-video-player';
6 |
7 | const player: VideoPlayer = cloudinary.videoPlayer('player', {}, () => {});
8 |
9 | player.source('test', {
10 | sourceTypes: ['mp4/h264']
11 | });
12 | player.play();
13 | player.pause();
14 |
15 | const vPlayer: VideoPlayer = videoPlayer('player', {});
16 |
17 | vPlayer.source('test');
18 |
19 | async () => {
20 | const profilePlayer = await videoPlayerWithProfile('player', {
21 | constrols: false,
22 | fontFace: 'Merienda'
23 | });
24 |
25 | profilePlayer.source({
26 | publicId: 'elephants',
27 | profile: 'default'
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/webpack/analyzer.config.js:
--------------------------------------------------------------------------------
1 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
2 | const { merge } = require('webpack-merge');
3 | const buildConf = require('./build.config');
4 |
5 | module.exports = merge(buildConf, {
6 | plugins: [
7 | new BundleAnalyzerPlugin()
8 | ]
9 | });
10 |
--------------------------------------------------------------------------------
/webpack/build-utils.js:
--------------------------------------------------------------------------------
1 | const isMin = !!process.env.WEBPACK_BUILD_MIN;
2 |
3 | const minFilenamePart = isMin ? '.min' : '';
4 |
5 | module.exports = { isMin, minFilenamePart };
6 |
--------------------------------------------------------------------------------
/webpack/build.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const webpackCommon = require('./common.config');
3 | const TerserPlugin = require('terser-webpack-plugin');
4 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
5 | const { isMin } = require('./build-utils');
6 |
7 | module.exports = merge(webpackCommon, {
8 | mode: 'production',
9 |
10 | optimization: isMin ? {
11 | minimize: true,
12 | minimizer: [
13 | new CssMinimizerPlugin(),
14 | new TerserPlugin()
15 | ]
16 | } : undefined
17 | });
18 |
--------------------------------------------------------------------------------
/webpack/copy-light-bundle.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | const src = path.resolve(__dirname, '../dist/cld-video-player.js');
5 | const dest = path.resolve(__dirname, '../dist/cld-video-player.light.js');
6 | const srcMin = path.resolve(__dirname, '../dist/cld-video-player.min.js');
7 | const destMin = path.resolve(__dirname, '../dist/cld-video-player.light.min.js');
8 | const warning = 'console.warn(\'[Cloudinary] The "light" video-player is deprecated and will be removed in a future release. The main player is now light by default. Please use that instead.\');\n';
9 |
10 | fs.readFile(src, 'utf8', (err, data) => {
11 | if (err) throw err;
12 | fs.writeFile(dest, warning + data, 'utf8', (err) => {
13 | if (err) throw err;
14 | console.log('Light bundle created with deprecation warning.');
15 | });
16 | });
17 |
18 | fs.readFile(srcMin, 'utf8', (err, data) => {
19 | if (err) throw err;
20 | fs.writeFile(destMin, warning + data, 'utf8', (err) => {
21 | if (err) throw err;
22 | console.log('Light minified bundle created with deprecation warning.');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/webpack/dev.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const webpackCommon = require('./common.config');
3 | const path = require('path');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const CopyWebpackPlugin = require('copy-webpack-plugin');
6 |
7 | const env = require('../env');
8 |
9 | module.exports = merge(webpackCommon, {
10 | mode: 'development',
11 |
12 | plugins: [
13 | new HtmlWebpackPlugin({
14 | inject: false,
15 | template: path.resolve(__dirname, '../docs/index.html')
16 | }),
17 | new CopyWebpackPlugin({
18 | patterns: [{
19 | from: path.resolve(__dirname, '../docs'),
20 | globOptions: {
21 | ignore: [
22 | '**/node_modules',
23 | ],
24 | },
25 | }],
26 | })
27 | ],
28 |
29 | devServer: {
30 | port: env.devServer.port || 3000,
31 | open: ['index.html'],
32 | headers: {
33 | 'Access-Control-Allow-Origin': '*',
34 | 'Access-Control-Allow-Headers': '*'
35 | }
36 | }
37 |
38 | });
39 |
--------------------------------------------------------------------------------
/webpack/es6.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const webpackCommon = require('./common.config');
3 | const path = require('path');
4 |
5 | delete webpackCommon.output; // overwrite
6 |
7 | const outputPath = path.resolve(__dirname, '../lib');
8 |
9 | module.exports = merge(webpackCommon, {
10 | mode: 'production',
11 |
12 | entry: {
13 | 'cld-video-player': './index.es.js', // default
14 | 'videoPlayer': './index.videoPlayer.js',
15 | 'player': './index.player.js',
16 | 'all': './index.all.js'
17 | },
18 |
19 | output: {
20 | filename: '[name].js',
21 | path: outputPath,
22 | chunkFilename: '[name].js',
23 | publicPath: '',
24 | library: {
25 | type: 'module'
26 | },
27 | chunkLoadingGlobal: 'cloudinaryVideoPlayerChunkLoading'
28 | },
29 |
30 | experiments: {
31 | outputModule: true
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/webpack/puppeteer.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const devConf = require('./dev.config');
3 |
4 | module.exports = merge(devConf, {
5 | devServer: {
6 | open: false
7 | }
8 | });
9 |
--------------------------------------------------------------------------------