Verified Commit 3419e0e1 authored by Chocobozzz's avatar Chocobozzz
Browse files

Migrate to webdriverio

parent 2a4c9669
......@@ -22,6 +22,8 @@
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"jsdoc/newline-after-description": "off",
"jsdoc/check-alignment": "off",
"lines-between-class-members": "off",
"@typescript-eslint/lines-between-class-members": [ "off" ],
"arrow-body-style": "off",
......
......@@ -8,3 +8,4 @@
/src/locale/target/iso639_*.xml
/src/locale/target/player_*.xml
/src/locale/target/server_*.xml
/e2e/local.log
......@@ -233,21 +233,6 @@
"with": "src/environments/environment.hmr.ts"
}
]
},
"e2e": {
"localize": false,
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.e2e.ts"
}
]
}
}
},
......@@ -266,10 +251,6 @@
},
"ar-locale": {
"browserTarget": "PeerTube:build:ar-locale"
},
"e2e": {
"browserTarget": "PeerTube:build:e2e",
"proxyConfig": "e2e/proxy.config.json"
}
}
},
......@@ -361,25 +342,6 @@
}
}
}
},
"PeerTube-e2e": {
"root": "e2e/",
"sourceRoot": "",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "PeerTube:serve:e2e"
},
"configurations": {
"local": {
"protractorConfig": "e2e/local-protractor.conf.js"
}
}
}
}
}
},
"defaultProject": "PeerTube",
......
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter} = require('jasmine-spec-reporter')
exports.config = {
allScriptsTimeout: 25000,
specs: ['./src/**/*.e2e-spec.ts'],
directConnect: true,
multiCapabilities: [
{
'browserName': 'firefox',
'name': 'Firefox',
'moz:firefoxOptions': {
binary: '/usr/bin/firefox-developer-edition',
// args: ["-headless"],
log: {
"level": "info" // default is "info"
}
}
},
{
'browserName': 'firefox',
'name': 'Firefox ESR',
'moz:firefoxOptions': {
binary: '/usr/bin/firefox-esr',
// args: ["-headless"],
log: {
"level": "info" // default is "info"
}
}
},
{
'browserName': 'chrome',
'name': 'Chromium'
}
],
maxSessions: 1,
baseUrl: 'http://localhost:3000/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 45000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
})
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }))
}
}
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter} = require('jasmine-spec-reporter')
exports.config = {
allScriptsTimeout: 25000,
specs: [ './src/**/*.e2e-spec.ts' ],
seleniumAddress: 'http://hub-cloud.browserstack.com/wd/hub',
commonCapabilities: {
'browserstack.user': process.env.BROWSERSTACK_USER,
'browserstack.key': process.env.BROWSERSTACK_KEY,
'browserstack.local': true,
'browserstack.console': 'verbose',
'browserstack.networkLogs': true,
'browserstack.debug': true,
project: 'PeerTube',
build: 'Main',
name: 'Bstack-[Protractor] Parallel Test'
},
multiCapabilities: [
{
browserName: 'Safari',
version: '11.1',
name: 'Safari Desktop',
resolution: '1280x1024'
},
{
browserName: 'Chrome',
name: 'Latest Chrome Desktop',
resolution: '1280x1024'
},
{
browserName: 'Firefox',
version: '68', // ESR
name: 'Firefox ESR Desktop',
resolution: '1280x1024'
},
{
browserName: 'Firefox',
name: 'Latest Firefox Desktop',
resolution: '1280x1024'
},
{
browserName: 'Edge',
name: 'Latest Edge Desktop',
resolution: '1280x1024'
},
{
browserName: 'Chrome',
device: 'Google Nexus 6',
real_mobile: 'true',
os_version: '5.0',
name: 'Latest Chrome Android'
},
{
browserName: 'Safari',
device: 'iPhone 8 Plus',
real_mobile: 'true',
os_version: '11',
name: 'Safari iPhone'
},
{
browserName: 'Safari',
device: 'iPad 7th',
real_mobile: 'true',
os_version: '13',
name: 'Safari iPad'
}
],
// maxSessions: 1,
// BrowserStack compatible ports: https://www.browserstack.com/question/664
baseUrl: 'http://localhost:3333/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 45000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
})
jasmine.getEnv().addReporter(new SpecReporter({
spec: { displayStacktrace: 'raw' }
}))
}
}
exports.config.multiCapabilities.forEach(function (caps) {
for (var i in exports.config.commonCapabilities) caps[i] = caps[i] || exports.config.commonCapabilities[i]
})
{
"/api": {
"target": "http://localhost:9000",
"secure": false
},
"/plugins": {
"target": "http://localhost:9000",
"secure": false
},
"/themes": {
"target": "http://localhost:9000",
"secure": false
},
"/static": {
"target": "http://localhost:9000",
"secure": false
},
"/lazy-static": {
"target": "http://localhost:9000",
"secure": false
},
"/socket.io": {
"target": "ws://localhost:9000",
"secure": false,
"ws": true
},
"/!(client)**": {
"target": "http://localhost:3333/client/index.html",
"secure": false,
"logLevel": "debug"
},
"/!(client)**/**": {
"target": "http://localhost:3333/client/index.html",
"secure": false,
"logLevel": "debug"
}
}
browser.addCommand('chooseFile', async function (this: WebdriverIO.Element, localFilePath: string) {
try {
const remoteFile = await browser.uploadFile(localFilePath)
return this.addValue(remoteFile)
} catch {
console.log('Cannot upload file, fallback to add value.')
// Firefox does not support upload file, but if we're running the test in local we don't really need it
return this.addValue(localFilePath)
}
}, true)
import { browser, element, by } from 'protractor'
import { go } from '../utils'
export class LoginPage {
async loginAsRootUser () {
await browser.get('/login')
await go('/login')
await browser.executeScript(`window.localStorage.setItem('no_instance_config_warning_modal', 'true')`)
await browser.executeScript(`window.localStorage.setItem('no_welcome_modal', 'true')`)
await browser.execute(`window.localStorage.setItem('no_instance_config_warning_modal', 'true')`)
await browser.execute(`window.localStorage.setItem('no_welcome_modal', 'true')`)
element(by.css('input#username')).sendKeys('root')
element(by.css('input#password')).sendKeys('test1')
await $('input#username').setValue('root')
await $('input#password').setValue('test1')
await browser.sleep(1000)
await browser.pause(1000)
await element(by.css('form input[type=submit]')).click()
await $('form input[type=submit]').click()
expect(this.getLoggedInInfo().getText()).toContain('root')
await this.getLoggedInInfoElem().waitForExist()
await expect(this.getLoggedInInfoElem()).toHaveText('root')
}
private getLoggedInInfo () {
return element(by.css('.logged-in-display-name'))
private getLoggedInInfoElem () {
return $('.logged-in-display-name')
}
}
import { by, element, browser } from 'protractor'
import { go } from '../utils'
export class MyAccountPage {
navigateToMyVideos () {
return element(by.css('a[href="/my-library/videos"]')).click()
return $('a[href="/my-library/videos"]').click()
}
navigateToMyPlaylists () {
return element(by.css('a[href="/my-library/video-playlists"]')).click()
return $('a[href="/my-library/video-playlists"]').click()
}
navigateToMyHistory () {
return element(by.css('a[href="/my-library/history/videos"]')).click()
return $('a[href="/my-library/history/videos"]').click()
}
// My account Videos
async removeVideo (name: string) {
const container = this.getVideoElement(name)
const container = await this.getVideoElement(name)
await container.element(by.css('.dropdown-toggle')).click()
await container.$('.dropdown-toggle').click()
const dropdownMenu = container.element(by.css('.dropdown-menu .dropdown-item:nth-child(2)'))
await browser.wait(browser.ExpectedConditions.presenceOf(dropdownMenu))
const dropdownMenu = () => container.$('.dropdown-menu .dropdown-item:nth-child(2)')
return dropdownMenu.click()
await dropdownMenu().waitForDisplayed()
return dropdownMenu().click()
}
validRemove () {
return element(by.css('input[type=submit]')).click()
return $('input[type=submit]').click()
}
countVideos (names: string[]) {
return element.all(by.css('.video'))
.filter(e => {
return e.element(by.css('.video-miniature-name'))
.getText()
.then(t => names.some(n => t.includes(n)))
})
.count()
async countVideos (names: string[]) {
const elements = await $$('.video').filter(async e => {
const t = await e.$('.video-miniature-name').getText()
return names.some(n => t.includes(n))
})
return elements.length
}
// My account playlists
getPlaylistVideosText (name: string) {
return this.getPlaylist(name).element(by.css('.miniature-playlist-info-overlay')).getText()
async getPlaylistVideosText (name: string) {
const elem = await this.getPlaylist(name)
return elem.$('.miniature-playlist-info-overlay').getText()
}
clickOnPlaylist (name: string) {
return this.getPlaylist(name).element(by.css('.miniature-thumbnail')).click()
async clickOnPlaylist (name: string) {
const elem = await this.getPlaylist(name)
return elem.$('.miniature-thumbnail').click()
}
countTotalPlaylistElements () {
return element.all(by.css('my-video-playlist-element-miniature')).count()
async countTotalPlaylistElements () {
await $('<my-video-playlist-element-miniature>').waitForDisplayed()
return $$('<my-video-playlist-element-miniature>').length
}
playPlaylist () {
return element(by.css('.playlist-info .miniature-thumbnail')).click()
return $('.playlist-info .miniature-thumbnail').click()
}
async goOnAssociatedPlaylistEmbed () {
let url = await browser.getCurrentUrl()
let url = await browser.getUrl()
url = url.replace('/w/p/', '/video-playlists/embed/')
url = url.replace(':3333', ':9001')
return browser.get(url)
return go(url)
}
// My account Videos
private getVideoElement (name: string) {
return element.all(by.css('.video'))
.filter(e => e.element(by.css('.video-miniature-name')).getText().then(t => t.includes(name)))
.first()
private async getVideoElement (name: string) {
const video = async () => {
const videos = await $$('.video').filter(async e => {
const t = await e.$('.video-miniature-name').getText()
return t.includes(name)
})
return videos[0]
}
await browser.waitUntil(async () => {
return (await video()).isDisplayed()
})
return video()
}
// My account playlists
private getPlaylist (name: string) {
return element.all(by.css('my-video-playlist-miniature'))
.filter(e => e.element(by.css('.miniature-name')).getText().then(t => t.includes(name)))
.first()
private async getPlaylist (name: string) {
const playlist = () => {
return $$('my-video-playlist-miniature')
.filter(async e => {
const t = await e.$('.miniature-name').getText()
return t.includes(name)
})
.then(elems => elems[0])
}
await browser.waitUntil(async () => {
const el = await playlist()
return el?.isDisplayed()
})
return playlist()
}
}
import { browser, by, element } from 'protractor'
import { browserSleep, isIOS, isMobileDevice, isSafari } from '../utils'
export class PlayerPage {
async getWatchVideoPlayerCurrentTime () {
const elem = element(by.css('video'))
getWatchVideoPlayerCurrentTime () {
const elem = $('video')
return elem.getAttribute('currentTime')
}
if (isIOS()) {
return elem.getAttribute('currentTime')
.then(t => parseInt(t, 10))
.then(t => Math.round(t))
}
waitUntilPlaylistInfo (text: string) {
const elem = element(by.css('.video-js .vjs-playlist-info'))
return elem.getProperty('currentTime')
}
return browser.wait(browser.ExpectedConditions.textToBePresentInElement(elem, text))
waitUntilPlaylistInfo (text: string, maxTime: number) {
return browser.waitUntil(async () => {
return (await $('.video-js .vjs-playlist-info').getText()).includes(text)
}, { timeout: maxTime })
}
waitUntilPlayerWrapper () {
const elem = element(by.css('#placeholder-preview'))
return browser.wait(browser.ExpectedConditions.presenceOf(elem))
return browser.waitUntil(async () => {
return !!(await $('#placeholder-preview'))
})
}
async playAndPauseVideo (isAutoplay: boolean) {
const videojsEl = element(by.css('div.video-js'))
await browser.wait(browser.ExpectedConditions.elementToBeClickable(videojsEl))
const videojsElem = () => $('div.video-js')
await videojsElem().waitForExist()
// Autoplay is disabled on iOS and Safari
if (await isIOS() || await isSafari() || await isMobileDevice()) {
if (isIOS() || isSafari() || isMobileDevice()) {
// We can't play the video using protractor if it is not muted
await browser.executeScript(`document.querySelector('video').muted = true`)
await browser.execute(`document.querySelector('video').muted = true`)
await this.clickOnPlayButton()
} else if (isAutoplay === false) {
await this.clickOnPlayButton()
}
await browserSleep(2000)
await browser.wait(browser.ExpectedConditions.invisibilityOf(element(by.css('.vjs-loading-spinner'))))
await browserSleep(2000)
await browser.waitUntil(async () => {
return !await $('.vjs-loading-spinner').isDisplayedInViewport()
}, { timeout: 20 * 1000 })
await videojsEl.click()
await browserSleep(4000)
await videojsElem().click()
}
async playVideo () {
......@@ -47,8 +56,9 @@ export class PlayerPage {
}
private async clickOnPlayButton () {
const playButton = element(by.css('.vjs-big-play-button'))
await browser.wait(browser.ExpectedConditions.elementToBeClickable(playButton))
await playButton.click()
const playButton = () => $('.vjs-big-play-button')
await playButton().waitForClickable()
await playButton().click()
}
}
import { by, element } from 'protractor'
export class VideoUpdatePage {
async updateName (videoName: string) {
const nameInput = element(by.css('input#name'))
await nameInput.clear()
await nameInput.sendKeys(videoName)
const nameInput = $('input#name')
await nameInput.waitForDisplayed()
await nameInput.clearValue()
await nameInput.setValue(videoName)
}
async validUpdate () {
......@@ -15,6 +15,6 @@ export class VideoUpdatePage {
}
private getSubmitButton () {
return element(by.css('.submit-container .action-button'))
return $('.submit-container .action-button')
}
}
import { browser, by, element } from 'protractor'
import { FileDetector } from 'selenium-webdriver/remote'
import { join } from 'path'
export class VideoUploadPage {
async navigateTo () {
await element(by.css('.header .publish-button')).click()
await $('.header .publish-button').click()
return browser.wait(browser.ExpectedConditions.visibilityOf(element(by.css('.upload-video-container'))))
await $('.upload-video-container').waitForDisplayed()
}
async uploadVideo () {