はじまりの大地

このコミットが含まれているのは:
2024-07-15 09:14:04 +09:00
コミット 6632905f32
3501個のファイルの変更1439465行の追加0行の削除
+12
ファイルの表示
@@ -0,0 +1,12 @@
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)
+65
ファイルの表示
@@ -0,0 +1,65 @@
import { browserSleep, getCheckbox, go, isCheckboxSelected } from '../utils'
export class AdminConfigPage {
async navigateTo (tab: 'instance-homepage' | 'basic-configuration' | 'instance-information') {
const waitTitles = {
'instance-homepage': 'INSTANCE HOMEPAGE',
'basic-configuration': 'APPEARANCE',
'instance-information': 'INSTANCE'
}
await go('/admin/config/edit-custom#' + tab)
await $('h2=' + waitTitles[tab]).waitForDisplayed()
}
async updateNSFWSetting (newValue: 'do_not_list' | 'blur' | 'display') {
const elem = $('#instanceDefaultNSFWPolicy')
await elem.waitForDisplayed()
await elem.scrollIntoView({ block: 'center' }) // Avoid issues with fixed header
await elem.waitForClickable()
return elem.selectByAttribute('value', newValue)
}
updateHomepage (newValue: string) {
return $('#instanceCustomHomepageContent').setValue(newValue)
}
async toggleSignup (enabled: boolean) {
if (await isCheckboxSelected('signupEnabled') === enabled) return
const checkbox = await getCheckbox('signupEnabled')
await checkbox.waitForClickable()
await checkbox.click()
}
async toggleSignupApproval (required: boolean) {
if (await isCheckboxSelected('signupRequiresApproval') === required) return
const checkbox = await getCheckbox('signupRequiresApproval')
await checkbox.waitForClickable()
await checkbox.click()
}
async toggleSignupEmailVerification (required: boolean) {
if (await isCheckboxSelected('signupRequiresEmailVerification') === required) return
const checkbox = await getCheckbox('signupRequiresEmailVerification')
await checkbox.waitForClickable()
await checkbox.click()
}
async save () {
const button = $('input[type=submit]')
await button.waitForClickable()
await button.click()
await browserSleep(1000)
}
}
+31
ファイルの表示
@@ -0,0 +1,31 @@
import { browserSleep, go } from '../utils'
export class AdminPluginPage {
async navigateToPluginSearch () {
await go('/admin/plugins/search')
await $('my-plugin-search').waitForDisplayed()
}
async search (name: string) {
const input = $('.search-bar input')
await input.waitForDisplayed()
await input.clearValue()
await input.setValue(name)
await browserSleep(1000)
}
async installHelloWorld () {
$('.plugin-name=hello-world').waitForDisplayed()
await $('.card-body my-button[icon=cloud-download]').click()
const submitModalButton = $('.modal-content input[type=submit]')
await submitModalButton.waitForClickable()
await submitModalButton.click()
await $('.card-body my-edit-button').waitForDisplayed()
}
}
+35
ファイルの表示
@@ -0,0 +1,35 @@
import { browserSleep, findParentElement, go } from '../utils'
export class AdminRegistrationPage {
async navigateToRegistratonsList () {
await go('/admin/moderation/registrations/list')
await $('my-registration-list').waitForDisplayed()
}
async accept (username: string, moderationResponse: string) {
const usernameEl = await $('*=' + username)
await usernameEl.waitForDisplayed()
const tr = await findParentElement(usernameEl, async el => await el.getTagName() === 'tr')
await tr.$('.action-cell .dropdown-root').click()
const accept = await $('span*=Accept this request')
await accept.waitForClickable()
await accept.click()
const moderationResponseTextarea = await $('#moderationResponse')
await moderationResponseTextarea.waitForDisplayed()
await moderationResponseTextarea.setValue(moderationResponse)
const submitButton = $('.modal-footer input[type=submit]')
await submitButton.waitForClickable()
await submitButton.click()
await browserSleep(1000)
}
}
+21
ファイルの表示
@@ -0,0 +1,21 @@
import { getCheckbox } from '../utils'
export class AnonymousSettingsPage {
async openSettings () {
const link = await $$('.menu-link').filter(async i => {
return await i.getText() === 'My settings'
}).then(links => links[0])
await link.click()
await $('my-user-video-settings').waitForDisplayed()
}
async clickOnP2PCheckbox () {
const p2p = await getCheckbox('p2pEnabled')
await p2p.waitForClickable()
await p2p.click()
}
}
+109
ファイルの表示
@@ -0,0 +1,109 @@
import { browserSleep, go, isAndroid } from '../utils'
export class LoginPage {
constructor (private isMobileDevice: boolean) {
}
async login (options: {
username: string
password: string
displayName?: string
url?: string
}) {
const { username, password, url = '/login', displayName = username } = options
await go(url)
await browser.execute(`window.localStorage.setItem('no_account_setup_warning_modal', 'true')`)
await browser.execute(`window.localStorage.setItem('no_instance_config_warning_modal', 'true')`)
await browser.execute(`window.localStorage.setItem('no_welcome_modal', 'true')`)
await $('input#username').setValue(username)
await $('input#password').setValue(password)
await browserSleep(1000)
const submit = $('.login-form-and-externals > form input[type=submit]')
await submit.click()
// Have to do this on Android, don't really know why
// I think we need to "escape" from the password input, so click twice on the submit button
if (isAndroid()) {
await browserSleep(2000)
await submit.click()
}
if (this.isMobileDevice) {
const menuToggle = $('.top-left-block button')
await $('h2=Our content selection').waitForDisplayed()
await menuToggle.click()
await this.ensureIsLoggedInAs(displayName)
await menuToggle.click()
} else {
await this.ensureIsLoggedInAs(displayName)
}
}
async getLoginError (username: string, password: string) {
await go('/login')
await $('input#username').setValue(username)
await $('input#password').setValue(password)
await browser.pause(1000)
await $('form input[type=submit]').click()
return $('.alert-danger').getText()
}
async loginAsRootUser () {
return this.login({ username: 'root', password: 'test' + this.getSuffix() })
}
loginOnPeerTube2 () {
if (!process.env.PEERTUBE2_E2E_PASSWORD) {
throw new Error('PEERTUBE2_E2E_PASSWORD env is missing for user e2e on peertube2.cpy.re')
}
return this.login({ username: 'e2e', password: process.env.PEERTUBE2_E2E_PASSWORD, url: 'https://peertube2.cpy.re/login' })
}
async logout () {
const loggedInDropdown = $('.logged-in-more .logged-in-info')
await loggedInDropdown.waitForClickable()
await loggedInDropdown.click()
const logout = $('.dropdown-item*=Log out')
await logout.waitForClickable()
await logout.click()
await browser.waitUntil(() => {
return $$('.login-buttons-block, my-error-page a[href="/login"]').some(e => e.isDisplayed())
})
}
async ensureIsLoggedInAs (displayName: string) {
await this.getLoggedInInfoElem().waitForExist()
await expect(this.getLoggedInInfoElem()).toHaveText(displayName)
}
private getLoggedInInfoElem () {
return $('.logged-in-display-name')
}
private getSuffix () {
return browser.options.baseUrl
? browser.options.baseUrl.slice(-1)
: '1'
}
}
+179
ファイルの表示
@@ -0,0 +1,179 @@
import { getCheckbox, go, selectCustomSelect } from '../utils'
export class MyAccountPage {
navigateToMyVideos () {
return $('a[href="/my-library/videos"]').click()
}
navigateToMyPlaylists () {
return $('a[href="/my-library/video-playlists"]').click()
}
navigateToMyHistory () {
return $('a[href="/my-library/history/videos"]').click()
}
// Settings
navigateToMySettings () {
return $('a[href="/my-account"]').click()
}
async updateNSFW (newValue: 'do_not_list' | 'blur' | 'display') {
const nsfw = $('#nsfwPolicy')
await nsfw.waitForDisplayed()
await nsfw.scrollIntoView({ block: 'center' }) // Avoid issues with fixed header
await nsfw.waitForClickable()
await nsfw.selectByAttribute('value', newValue)
await this.submitVideoSettings()
}
async clickOnP2PCheckbox () {
const p2p = await getCheckbox('p2pEnabled')
await p2p.waitForClickable()
await p2p.scrollIntoView({ block: 'center' }) // Avoid issues with fixed header
await p2p.click()
await this.submitVideoSettings()
}
private async submitVideoSettings () {
const submit = $('my-user-video-settings input[type=submit]')
await submit.waitForClickable()
await submit.scrollIntoView({ block: 'center' }) // Avoid issues with fixed header
await submit.click()
}
// My account Videos
async removeVideo (name: string) {
const container = await this.getVideoElement(name)
await container.$('my-action-dropdown .dropdown-toggle').click()
const deleteItem = () => {
return $$('.dropdown-menu .dropdown-item').find<WebdriverIO.Element>(async v => {
const text = await v.getText()
return text.includes('Delete')
})
}
await (await deleteItem()).waitForClickable()
return (await deleteItem()).click()
}
validRemove () {
return $('input[type=submit]').click()
}
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
async getPlaylistVideosText (name: string) {
const elem = await this.getPlaylist(name)
return elem.$('.miniature-playlist-info-overlay').getText()
}
async clickOnPlaylist (name: string) {
const elem = await this.getPlaylist(name)
return elem.$('.miniature-thumbnail').click()
}
async countTotalPlaylistElements () {
await $('<my-video-playlist-element-miniature>').waitForDisplayed()
return $$('<my-video-playlist-element-miniature>').length
}
playPlaylist () {
return $('.playlist-info .miniature-thumbnail').click()
}
async goOnAssociatedPlaylistEmbed () {
let url = await browser.getUrl()
url = url.replace('/w/p/', '/video-playlists/embed/')
url = url.replace(':3333', ':9001')
return go(url)
}
async updatePlaylistPrivacy (playlistUUID: string, privacy: 'Public' | 'Private' | 'Unlisted') {
go('/my-library/video-playlists/update/' + playlistUUID)
await $('a[href*="/my-library/video-playlists/update/"]').waitForDisplayed()
await selectCustomSelect('videoChannelId', 'Main root channel')
await selectCustomSelect('privacy', privacy)
const submit = await $('form input[type=submit]')
await submit.waitForClickable()
await submit.scrollIntoView()
await submit.click()
return browser.waitUntil(async () => {
return (await browser.getUrl()).includes('my-library/video-playlists')
})
}
// My account Videos
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 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()
}
}
+86
ファイルの表示
@@ -0,0 +1,86 @@
import { browserSleep, isIOS, isMobileDevice, isSafari } from '../utils'
export class PlayerPage {
getWatchVideoPlayerCurrentTime () {
const elem = $('video')
const p = isIOS()
? elem.getAttribute('currentTime')
: elem.getProperty('currentTime')
return p.then(t => parseInt(t + '', 10))
.then(t => Math.ceil(t))
}
waitUntilPlaylistInfo (text: string, maxTime: number) {
return browser.waitUntil(async () => {
// Without this we have issues on iphone
await $('.video-js').click()
return (await $('.video-js .vjs-playlist-info').getText()).includes(text)
}, { timeout: maxTime })
}
waitUntilPlayerWrapper () {
return browser.waitUntil(async () => {
return !!(await $('#placeholder-preview'))
})
}
async playAndPauseVideo (isAutoplay: boolean, waitUntilSec: number) {
// Autoplay is disabled on mobile and Safari
if (isIOS() || isSafari() || isMobileDevice() || isAutoplay === false) {
await this.playVideo()
}
await $('div.video-js.vjs-has-started').waitForExist()
await browserSleep(2000)
await browser.waitUntil(async () => {
return (await this.getWatchVideoPlayerCurrentTime()) >= waitUntilSec
}, { timeout: Math.max(waitUntilSec * 2 * 1000, 30000) })
// Pause video
await $('div.video-js').click()
}
async playVideo () {
await $('div.video-js.vjs-paused, div.video-js.vjs-playing').waitForExist()
if (await $('div.video-js.vjs-playing').isExisting()) {
if (!isIOS()) return
// On iOS, the web browser may have aborted player autoplay, so check the video is still autoplayed
await browserSleep(5000)
if (await $('div.video-js.vjs-playing').isExisting()) return
}
// Autoplay is disabled on iOS and Safari
if (isIOS() || isSafari() || isMobileDevice()) {
// We can't play the video if it is not muted
await browser.execute(`document.querySelector('video').muted = true`)
}
return this.clickOnPlayButton()
}
private async clickOnPlayButton () {
const playButton = () => $('.vjs-big-play-button')
await playButton().waitForClickable()
await playButton().click()
}
async fillEmbedVideoPassword (videoPassword: string) {
const videoPasswordInput = $('input#video-password-input')
const confirmButton = await $('button#video-password-submit')
await videoPasswordInput.clearValue()
await videoPasswordInput.setValue(videoPassword)
await confirmButton.waitForClickable()
return confirmButton.click()
}
}
+87
ファイルの表示
@@ -0,0 +1,87 @@
import { getCheckbox } from '../utils'
export class SignupPage {
getRegisterMenuButton () {
return $('.create-account-button')
}
async clickOnRegisterInMenu () {
const button = this.getRegisterMenuButton()
await button.waitForClickable()
await button.click()
}
async validateStep () {
const next = $('button[type=submit]')
await next.waitForClickable()
await next.click()
}
async checkTerms () {
const terms = await getCheckbox('terms')
await terms.waitForClickable()
return terms.click()
}
async getEndMessage () {
const alert = $('.pt-alert-primary')
await alert.waitForDisplayed()
return alert.getText()
}
async fillRegistrationReason (reason: string) {
await $('#registrationReason').setValue(reason)
}
async fillAccountStep (options: {
username: string
password?: string
displayName?: string
email?: string
}) {
await $('#displayName').setValue(options.displayName || `${options.username} display name`)
await $('#username').setValue(options.username)
await $('#password').setValue(options.password || 'password')
// Fix weird bug on firefox that "cannot scroll into view" when using just `setValue`
await $('#email').scrollIntoView({ block: 'center' })
await $('#email').waitForClickable()
await $('#email').setValue(options.email || `${options.username}@example.com`)
}
async fillChannelStep (options: {
name: string
displayName?: string
}) {
await $('#displayName').setValue(options.displayName || `${options.name} channel display name`)
await $('#name').setValue(options.name)
}
async fullSignup ({ accountInfo, channelInfo }: {
accountInfo: {
username: string
password?: string
displayName?: string
email?: string
}
channelInfo: {
name: string
}
}) {
await this.clickOnRegisterInMenu()
await this.validateStep()
await this.checkTerms()
await this.validateStep()
await this.fillAccountStep(accountInfo)
await this.validateStep()
await this.fillChannelStep(channelInfo)
await this.validateStep()
await this.getEndMessage()
}
}
+128
ファイルの表示
@@ -0,0 +1,128 @@
import { browserSleep, go } from '../utils'
export class VideoListPage {
constructor (private isMobileDevice: boolean, private isSafari: boolean) {
}
async goOnVideosList () {
let url: string
// We did not upload a file on a mobile device
if (this.isMobileDevice === true || this.isSafari === true) {
url = 'https://peertube2.cpy.re/videos/local'
} else {
url = '/videos/recently-added'
}
await go(url)
// Waiting the following element does not work on Safari...
if (this.isSafari) return browserSleep(3000)
await this.waitForList()
}
async goOnLocal () {
await $('.menu-link[href="/videos/local"]').click()
await this.waitForTitle('Local videos')
}
async goOnRecentlyAdded () {
await $('.menu-link[href="/videos/recently-added"]').click()
await this.waitForTitle('Recently added')
}
async goOnTrending () {
await $('.menu-link[href="/videos/trending"]').click()
await this.waitForTitle('Trending')
}
async goOnHomepage () {
await go('/home')
await this.waitForList()
}
async goOnRootChannel () {
await go('/c/root_channel/videos')
await this.waitForList()
}
async goOnRootAccount () {
await go('/a/root/videos')
await this.waitForList()
}
async goOnRootAccountChannels () {
await go('/a/root/video-channels')
await this.waitForList()
}
getNSFWFilter () {
return $$('.active-filter').filter(async a => {
return (await a.getText()).includes('Sensitive')
}).then(f => f[0])
}
async getVideosListName () {
const elems = await $$('.videos .video-miniature .video-miniature-name')
const texts = await elems.map(e => e.getText())
return texts.map(t => t.trim())
}
videoExists (name: string) {
return $('.video-miniature-name=' + name).isDisplayed()
}
async videoIsBlurred (name: string) {
const filter = await $('.video-miniature-name=' + name).getCSSProperty('filter')
return filter.value !== 'none'
}
async clickOnVideo (videoName: string) {
const video = async () => {
const videos = await $$('.videos .video-miniature .video-miniature-name').filter(async e => {
const t = await e.getText()
return t === videoName
})
return videos[0]
}
await browser.waitUntil(async () => {
const elem = await video()
return elem?.isClickable()
});
(await video()).click()
await browser.waitUntil(async () => (await browser.getUrl()).includes('/w/'))
}
async clickOnFirstVideo () {
const video = () => $('.videos .video-miniature .video-thumbnail')
const videoName = () => $('.videos .video-miniature .video-miniature-name')
await video().waitForClickable()
const textToReturn = await videoName().getText()
await video().click()
await browser.waitUntil(async () => (await browser.getUrl()).includes('/w/'))
return textToReturn
}
private waitForList () {
return $('.videos .video-miniature .video-miniature-name').waitForDisplayed()
}
private waitForTitle (title: string) {
return $('h1=' + title).waitForDisplayed()
}
}
+11
ファイルの表示
@@ -0,0 +1,11 @@
export class VideoSearchPage {
async search (search: string) {
await $('#search-video').setValue(search)
await $('.search-button').click()
await browser.waitUntil(() => {
return $('my-video-miniature').isDisplayed()
})
}
}
+20
ファイルの表示
@@ -0,0 +1,20 @@
export class VideoUpdatePage {
async updateName (videoName: string) {
const nameInput = $('input#name')
await nameInput.waitForDisplayed()
await nameInput.clearValue()
await nameInput.setValue(videoName)
}
async validUpdate () {
const submitButton = await this.getSubmitButton()
return submitButton.click()
}
private getSubmitButton () {
return $('.submit-container .action-button')
}
}
+80
ファイルの表示
@@ -0,0 +1,80 @@
import { join } from 'path'
import { getCheckbox, selectCustomSelect } from '../utils'
export class VideoUploadPage {
async navigateTo () {
const publishButton = await $('.root-header .publish-button')
await publishButton.waitForClickable()
await publishButton.click()
await $('.upload-video-container').waitForDisplayed()
}
async uploadVideo (fixtureName: 'video.mp4' | 'video2.mp4' | 'video3.mp4') {
const fileToUpload = join(__dirname, '../../fixtures/' + fixtureName)
const fileInputSelector = '.upload-video-container input[type=file]'
const parentFileInput = '.upload-video-container .button-file'
// Avoid sending keys on non visible element
await browser.execute(`document.querySelector('${fileInputSelector}').style.opacity = 1`)
await browser.execute(`document.querySelector('${parentFileInput}').style.overflow = 'initial'`)
await browser.pause(1000)
const elem = await $(fileInputSelector)
await elem.chooseFile(fileToUpload)
// Wait for the upload to finish
await browser.waitUntil(async () => {
const warning = await $('=Publish will be available when upload is finished').isDisplayed()
const progress = await $('.progress-bar=100%').isDisplayed()
return !warning && progress
})
}
async setAsNSFW () {
const checkbox = await getCheckbox('nsfw')
await checkbox.waitForClickable()
return checkbox.click()
}
async validSecondUploadStep (videoName: string) {
const nameInput = $('input#name')
await nameInput.clearValue()
await nameInput.setValue(videoName)
const button = this.getSecondStepSubmitButton()
await button.waitForClickable()
await button.click()
return browser.waitUntil(async () => {
return (await browser.getUrl()).includes('/w/')
})
}
setAsPublic () {
return selectCustomSelect('privacy', 'Public')
}
setAsPrivate () {
return selectCustomSelect('privacy', 'Private')
}
async setAsPasswordProtected (videoPassword: string) {
selectCustomSelect('privacy', 'Password protected')
const videoPasswordInput = $('input#videoPassword')
await videoPasswordInput.waitForClickable()
await videoPasswordInput.clearValue()
return videoPasswordInput.setValue(videoPassword)
}
private getSecondStepSubmitButton () {
return $('.submit-container my-button')
}
}
+229
ファイルの表示
@@ -0,0 +1,229 @@
import { browserSleep, FIXTURE_URLS, go } from '../utils'
export class VideoWatchPage {
constructor (private isMobileDevice: boolean, private isSafari: boolean) {
}
waitWatchVideoName (videoName: string) {
if (this.isSafari) return browserSleep(5000)
// On mobile we display the first node, on desktop the second one
const index = this.isMobileDevice ? 0 : 1
return browser.waitUntil(async () => {
if (!await $('.video-info .video-info-name').isExisting()) return false
const elem = await $$('.video-info .video-info-name')[index]
return (await elem.getText()).includes(videoName) && elem.isDisplayed()
})
}
getVideoName () {
return this.getVideoNameElement().then(e => e.getText())
}
getPrivacy () {
return $('.attribute-privacy .attribute-value').getText()
}
getLicence () {
return $('.attribute-licence .attribute-value').getText()
}
async isDownloadEnabled () {
try {
await this.clickOnMoreDropdownIcon()
return await $('.dropdown-item .icon-download').isExisting()
} catch {
return $('.action-button-download').isDisplayed()
}
}
areCommentsEnabled () {
return $('my-video-comment-add').isExisting()
}
isPrivacyWarningDisplayed () {
return $('my-privacy-concerns').isDisplayed()
}
async goOnAssociatedEmbed (passwordProtected = false) {
let url = await browser.getUrl()
url = url.replace('/w/', '/videos/embed/')
url = url.replace(':3333', ':9001')
await go(url)
if (passwordProtected) await this.waitEmbedForVideoPasswordForm()
else await this.waitEmbedForDisplayed()
}
waitEmbedForDisplayed () {
return $('.vjs-big-play-button').waitForDisplayed()
}
waitEmbedForVideoPasswordForm () {
return $('#video-password-input').waitForDisplayed()
}
isEmbedWarningDisplayed () {
return $('.peertube-dock-description').isDisplayed()
}
goOnP2PMediaLoaderEmbed () {
return go(FIXTURE_URLS.HLS_EMBED)
}
goOnP2PMediaLoaderPlaylistEmbed () {
return go(FIXTURE_URLS.HLS_PLAYLIST_EMBED)
}
async clickOnUpdate () {
await this.clickOnMoreDropdownIcon()
const items = await $$('.dropdown-menu.show .dropdown-item')
for (const item of items) {
const href = await item.getAttribute('href')
if (href?.includes('/update/')) {
await item.click()
return
}
}
}
clickOnSave () {
return $('.action-button-save').click()
}
async createPlaylist (name: string) {
const newPlaylistButton = () => $('.new-playlist-button')
await newPlaylistButton().waitForClickable()
await newPlaylistButton().click()
const displayName = () => $('#displayName')
await displayName().waitForDisplayed()
await displayName().setValue(name)
return $('.new-playlist-block input[type=submit]').click()
}
async saveToPlaylist (name: string) {
const playlist = () => $('my-video-add-to-playlist').$(`.playlist=${name}`)
await playlist().waitForDisplayed()
return playlist().click()
}
waitUntilVideoName (name: string, maxTime: number) {
return browser.waitUntil(async () => {
return (await this.getVideoName()) === name
}, { timeout: maxTime })
}
async clickOnMoreDropdownIcon () {
const dropdown = $('my-video-actions-dropdown .action-button')
await dropdown.click()
await $('.dropdown-menu.show .dropdown-item').waitForDisplayed()
}
private async getVideoNameElement () {
// We have 2 video info name block, pick the first that is not empty
const elem = async () => {
const elems = await $$('.video-info-first-row .video-info-name').filter(e => e.isDisplayed())
return elems[0]
}
await browser.waitUntil(async () => {
const e = await elem()
return e?.isDisplayed()
})
return elem()
}
isPasswordProtected () {
return $('#confirmInput').isExisting()
}
async fillVideoPassword (videoPassword: string) {
const videoPasswordInput = await $('input#confirmInput')
await videoPasswordInput.waitForClickable()
await videoPasswordInput.clearValue()
await videoPasswordInput.setValue(videoPassword)
const confirmButton = await $('input[value="Confirm"]')
await confirmButton.waitForClickable()
return confirmButton.click()
}
async like () {
const likeButton = await $('.action-button-like')
const isActivated = (await likeButton.getAttribute('class')).includes('activated')
let count: number
try {
count = parseInt(await $('.action-button-like > .count').getText())
} catch (error) {
count = 0
}
await likeButton.waitForClickable()
await likeButton.click()
if (isActivated) {
if (count === 1) {
return expect(!await $('.action-button-like > .count').isExisting())
} else {
return expect(parseInt(await $('.action-button-like > .count').getText())).toBe(count - 1)
}
} else {
return expect(parseInt(await $('.action-button-like > .count').getText())).toBe(count + 1)
}
}
async createThread (comment: string) {
const textarea = await $('my-video-comment-add textarea')
await textarea.waitForClickable()
await textarea.setValue(comment)
const confirmButton = await $('.comment-buttons .orange-button')
await confirmButton.waitForClickable()
await confirmButton.click()
const createdComment = await (await $('.comment-html p')).getText()
return expect(createdComment).toBe(comment)
}
async createReply (comment: string) {
const replyButton = await $('button.comment-action-reply')
await replyButton.waitForClickable()
await replyButton.scrollIntoView()
await replyButton.click()
const textarea = await $('my-video-comment my-video-comment-add textarea')
await textarea.waitForClickable()
await textarea.setValue(comment)
const confirmButton = await $('my-video-comment .comment-buttons .orange-button')
await confirmButton.waitForClickable()
await confirmButton.click()
const createdComment = await (await $('.is-child .comment-html p')).getText()
return expect(createdComment).toBe(comment)
}
}
+35
ファイルの表示
@@ -0,0 +1,35 @@
import { PlayerPage } from '../po/player.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { FIXTURE_URLS, go, isMobileDevice, isSafari } from '../utils'
describe('Live all workflow', () => {
let videoWatchPage: VideoWatchPage
let playerPage: PlayerPage
beforeEach(async () => {
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
playerPage = new PlayerPage()
if (!isMobileDevice()) {
await browser.maximizeWindow()
}
})
it('Should go to the live page', async () => {
await go(FIXTURE_URLS.LIVE_VIDEO)
return videoWatchPage.waitWatchVideoName('E2E - Live')
})
it('Should play the live', async () => {
await playerPage.playAndPauseVideo(false, 45)
expect(await playerPage.getWatchVideoPlayerCurrentTime()).toBeGreaterThanOrEqual(45)
})
it('Should watch the associated live embed', async () => {
await videoWatchPage.goOnAssociatedEmbed()
await playerPage.playAndPauseVideo(false, 45)
expect(await playerPage.getWatchVideoPlayerCurrentTime()).toBeGreaterThanOrEqual(45)
})
})
+75
ファイルの表示
@@ -0,0 +1,75 @@
import { LoginPage } from '../po/login.po'
import { PlayerPage } from '../po/player.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { FIXTURE_URLS, go, isMobileDevice, isSafari } from '../utils'
async function checkCorrectlyPlay (playerPage: PlayerPage) {
await playerPage.playAndPauseVideo(false, 2)
expect(await playerPage.getWatchVideoPlayerCurrentTime()).toBeGreaterThanOrEqual(2)
}
describe('Private videos all workflow', () => {
let videoWatchPage: VideoWatchPage
let loginPage: LoginPage
let playerPage: PlayerPage
const internalVideoName = 'Internal E2E test'
const internalHLSOnlyVideoName = 'Internal E2E test - HLS only'
beforeEach(async () => {
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
loginPage = new LoginPage(isMobileDevice())
playerPage = new PlayerPage()
if (!isMobileDevice()) {
await browser.maximizeWindow()
}
})
it('Should log in', async () => {
return loginPage.loginOnPeerTube2()
})
it('Should play an internal web video', async () => {
await go(FIXTURE_URLS.INTERNAL_WEB_VIDEO)
await videoWatchPage.waitWatchVideoName(internalVideoName)
await checkCorrectlyPlay(playerPage)
})
it('Should play an internal HLS video', async () => {
await go(FIXTURE_URLS.INTERNAL_HLS_VIDEO)
await videoWatchPage.waitWatchVideoName(internalVideoName)
await checkCorrectlyPlay(playerPage)
})
it('Should play an internal HLS only video', async () => {
await go(FIXTURE_URLS.INTERNAL_HLS_ONLY_VIDEO)
await videoWatchPage.waitWatchVideoName(internalHLSOnlyVideoName)
await checkCorrectlyPlay(playerPage)
})
it('Should play an internal Web Video in embed', async () => {
await go(FIXTURE_URLS.INTERNAL_EMBED_WEB_VIDEO)
await videoWatchPage.waitEmbedForDisplayed()
await checkCorrectlyPlay(playerPage)
})
it('Should play an internal HLS video in embed', async () => {
await go(FIXTURE_URLS.INTERNAL_EMBED_HLS_VIDEO)
await videoWatchPage.waitEmbedForDisplayed()
await checkCorrectlyPlay(playerPage)
})
it('Should play an internal HLS only video in embed', async () => {
await go(FIXTURE_URLS.INTERNAL_EMBED_HLS_ONLY_VIDEO)
await videoWatchPage.waitEmbedForDisplayed()
await checkCorrectlyPlay(playerPage)
})
})
+234
ファイルの表示
@@ -0,0 +1,234 @@
import { LoginPage } from '../po/login.po'
import { MyAccountPage } from '../po/my-account.po'
import { PlayerPage } from '../po/player.po'
import { VideoListPage } from '../po/video-list.po'
import { VideoUpdatePage } from '../po/video-update.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { FIXTURE_URLS, go, isIOS, isMobileDevice, isSafari, waitServerUp } from '../utils'
function isUploadUnsupported () {
if (isMobileDevice() || isSafari()) {
console.log('Skipping because we are on a real device or Safari and BrowserStack does not support file upload.')
return true
}
return false
}
describe('Videos all workflow', () => {
let videoWatchPage: VideoWatchPage
let videoListPage: VideoListPage
let videoUploadPage: VideoUploadPage
let videoUpdatePage: VideoUpdatePage
let myAccountPage: MyAccountPage
let loginPage: LoginPage
let playerPage: PlayerPage
let videoName = Math.random() + ' video'
const video2Name = Math.random() + ' second video'
const playlistName = Math.random() + ' playlist'
let videoWatchUrl: string
before(async () => {
if (isIOS()) {
console.log('iOS detected')
} else if (isMobileDevice()) {
console.log('Android detected.')
} else if (isSafari()) {
console.log('Safari detected.')
}
if (isUploadUnsupported()) return
await waitServerUp()
})
beforeEach(async () => {
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
videoUploadPage = new VideoUploadPage()
videoUpdatePage = new VideoUpdatePage()
myAccountPage = new MyAccountPage()
loginPage = new LoginPage(isMobileDevice())
playerPage = new PlayerPage()
videoListPage = new VideoListPage(isMobileDevice(), isSafari())
if (!isMobileDevice()) {
await browser.maximizeWindow()
}
})
it('Should log in', async () => {
if (isMobileDevice() || isSafari()) {
console.log('Skipping because we are on a real device or Safari and BrowserStack does not support file upload.')
return
}
return loginPage.loginAsRootUser()
})
it('Should upload a video', async () => {
if (isUploadUnsupported()) return
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video.mp4')
return videoUploadPage.validSecondUploadStep(videoName)
})
it('Should list videos', async () => {
await videoListPage.goOnVideosList()
if (isUploadUnsupported()) return
const videoNames = await videoListPage.getVideosListName()
expect(videoNames).toContain(videoName)
})
it('Should go on video watch page', async () => {
let videoNameToExcept = videoName
if (isMobileDevice() || isSafari()) {
await go(FIXTURE_URLS.WEB_VIDEO)
videoNameToExcept = 'E2E tests'
} else {
await videoListPage.clickOnVideo(videoName)
}
return videoWatchPage.waitWatchVideoName(videoNameToExcept)
})
it('Should play the video', async () => {
videoWatchUrl = await browser.getUrl()
await playerPage.playAndPauseVideo(true, 2)
expect(await playerPage.getWatchVideoPlayerCurrentTime()).toBeGreaterThanOrEqual(2)
})
it('Should watch the associated embed video', async () => {
await videoWatchPage.goOnAssociatedEmbed()
await playerPage.playAndPauseVideo(false, 2)
expect(await playerPage.getWatchVideoPlayerCurrentTime()).toBeGreaterThanOrEqual(2)
})
it('Should watch the p2p media loader embed video', async () => {
await videoWatchPage.goOnP2PMediaLoaderEmbed()
await playerPage.playAndPauseVideo(false, 2)
expect(await playerPage.getWatchVideoPlayerCurrentTime()).toBeGreaterThanOrEqual(2)
})
it('Should update the video', async () => {
if (isUploadUnsupported()) return
await go(videoWatchUrl)
await videoWatchPage.clickOnUpdate()
videoName += ' updated'
await videoUpdatePage.updateName(videoName)
await videoUpdatePage.validUpdate()
const name = await videoWatchPage.getVideoName()
expect(name).toEqual(videoName)
})
it('Should add the video in my playlist', async () => {
if (isUploadUnsupported()) return
await videoWatchPage.clickOnSave()
await videoWatchPage.createPlaylist(playlistName)
await videoWatchPage.saveToPlaylist(playlistName)
await browser.pause(5000)
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video2.mp4')
await videoUploadPage.validSecondUploadStep(video2Name)
await videoWatchPage.clickOnSave()
await videoWatchPage.saveToPlaylist(playlistName)
})
it('Should have the playlist in my account', async () => {
if (isUploadUnsupported()) return
await myAccountPage.navigateToMyPlaylists()
const videosNumberText = await myAccountPage.getPlaylistVideosText(playlistName)
expect(videosNumberText).toEqual('2 videos')
await myAccountPage.clickOnPlaylist(playlistName)
const count = await myAccountPage.countTotalPlaylistElements()
expect(count).toEqual(2)
})
it('Should watch the playlist', async () => {
if (isUploadUnsupported()) return
await myAccountPage.playPlaylist()
await videoWatchPage.waitUntilVideoName(video2Name, 40 * 1000)
})
it('Should watch the Web Video playlist in the embed', async () => {
if (isUploadUnsupported()) return
const accessToken = await browser.execute(`return window.localStorage.getItem('access_token');`)
const refreshToken = await browser.execute(`return window.localStorage.getItem('refresh_token');`)
await myAccountPage.goOnAssociatedPlaylistEmbed()
await playerPage.waitUntilPlayerWrapper()
console.log('Will set %s and %s tokens in local storage.', accessToken, refreshToken)
await browser.execute(`window.localStorage.setItem('access_token', '${accessToken}');`)
await browser.execute(`window.localStorage.setItem('refresh_token', '${refreshToken}');`)
await browser.execute(`window.localStorage.setItem('token_type', 'Bearer');`)
await browser.refresh()
await playerPage.playVideo()
await playerPage.waitUntilPlaylistInfo('2/2', 30 * 1000)
})
it('Should watch the HLS playlist in the embed', async () => {
await videoWatchPage.goOnP2PMediaLoaderPlaylistEmbed()
await playerPage.playVideo()
await playerPage.waitUntilPlaylistInfo('2/2', 30 * 1000)
})
it('Should delete the video 2', async () => {
if (isUploadUnsupported()) return
// Go to the dev website
await go(videoWatchUrl)
await myAccountPage.navigateToMyVideos()
await myAccountPage.removeVideo(video2Name)
await myAccountPage.validRemove()
await browser.waitUntil(async () => {
const count = await myAccountPage.countVideos([ videoName, video2Name ])
return count === 1
})
})
it('Should delete the first video', async () => {
if (isUploadUnsupported()) return
await myAccountPage.removeVideo(videoName)
await myAccountPage.validRemove()
})
})
+97
ファイルの表示
@@ -0,0 +1,97 @@
import { LoginPage } from '../po/login.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { getScreenshotPath, go, isMobileDevice, isSafari, waitServerUp } from '../utils'
describe('Custom server defaults', () => {
let videoUploadPage: VideoUploadPage
let loginPage: LoginPage
let videoWatchPage: VideoWatchPage
before(async () => {
await waitServerUp()
loginPage = new LoginPage(isMobileDevice())
videoUploadPage = new VideoUploadPage()
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
await browser.maximizeWindow()
})
describe('Publish default values', function () {
before(async function () {
await loginPage.loginAsRootUser()
})
it('Should upload a video with custom default values', async function () {
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video.mp4')
await videoUploadPage.validSecondUploadStep('video')
await videoWatchPage.waitWatchVideoName('video')
const videoUrl = await browser.getUrl()
expect(await videoWatchPage.getPrivacy()).toBe('Unlisted')
expect(await videoWatchPage.getLicence()).toBe('Attribution - Non Commercial')
expect(await videoWatchPage.areCommentsEnabled()).toBeFalsy()
// Owners can download their videos
expect(await videoWatchPage.isDownloadEnabled()).toBeTruthy()
// Logout to see if the download enabled is correct for anonymous users
await loginPage.logout()
await browser.url(videoUrl)
await videoWatchPage.waitWatchVideoName('video')
expect(await videoWatchPage.isDownloadEnabled()).toBeFalsy()
})
})
describe('P2P', function () {
let videoUrl: string
async function goOnVideoWatchPage () {
await go(videoUrl)
await videoWatchPage.waitWatchVideoName('video')
}
async function checkP2P (enabled: boolean) {
await goOnVideoWatchPage()
expect(await videoWatchPage.isPrivacyWarningDisplayed()).toEqual(enabled)
await videoWatchPage.goOnAssociatedEmbed()
expect(await videoWatchPage.isEmbedWarningDisplayed()).toEqual(enabled)
}
before(async () => {
await loginPage.loginAsRootUser()
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video2.mp4')
await videoUploadPage.setAsPublic()
await videoUploadPage.validSecondUploadStep('video')
await videoWatchPage.waitWatchVideoName('video')
videoUrl = await browser.getUrl()
})
beforeEach(async function () {
await goOnVideoWatchPage()
})
it('Should have P2P disabled for a logged in user', async function () {
await checkP2P(false)
})
it('Should have P2P disabled for anonymous users', async function () {
await loginPage.logout()
await checkP2P(false)
})
})
after(async () => {
await browser.saveScreenshot(getScreenshotPath('after-test.png'))
})
})
+82
ファイルの表示
@@ -0,0 +1,82 @@
import { AdminPluginPage } from '../po/admin-plugin.po'
import { LoginPage } from '../po/login.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { getCheckbox, isMobileDevice, waitServerUp } from '../utils'
describe('Plugins', () => {
let videoUploadPage: VideoUploadPage
let loginPage: LoginPage
let adminPluginPage: AdminPluginPage
function getPluginCheckbox () {
return getCheckbox('hello-world-field-4')
}
async function expectSubmitState ({ disabled }: { disabled: boolean }) {
const disabledSubmit = await $('my-button .disabled')
if (disabled) expect(await disabledSubmit.isDisplayed()).toBeTruthy()
else expect(await disabledSubmit.isDisplayed()).toBeFalsy()
}
before(async () => {
await waitServerUp()
})
beforeEach(async () => {
loginPage = new LoginPage(isMobileDevice())
videoUploadPage = new VideoUploadPage()
adminPluginPage = new AdminPluginPage()
await browser.maximizeWindow()
})
it('Should install hello world plugin', async () => {
await loginPage.loginAsRootUser()
await adminPluginPage.navigateToPluginSearch()
await adminPluginPage.search('hello-world')
await adminPluginPage.installHelloWorld()
await browser.refresh()
})
it('Should have checkbox in video edit page', async () => {
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video.mp4')
await $('span=Super field 4 in main tab').waitForDisplayed()
const checkbox = await getPluginCheckbox()
expect(await checkbox.isDisplayed()).toBeTruthy()
await expectSubmitState({ disabled: true })
})
it('Should check the checkbox and be able to submit the video', async function () {
const checkbox = await getPluginCheckbox()
await checkbox.waitForClickable()
await checkbox.click()
await expectSubmitState({ disabled: false })
})
it('Should uncheck the checkbox and not be able to submit the video', async function () {
const checkbox = await getPluginCheckbox()
await checkbox.waitForClickable()
await checkbox.click()
await expectSubmitState({ disabled: true })
const error = await $('.form-error*=Should be enabled')
expect(await error.isDisplayed()).toBeTruthy()
})
it('Should change the privacy and should hide the checkbox', async function () {
await videoUploadPage.setAsPrivate()
await expectSubmitState({ disabled: false })
})
})
+413
ファイルの表示
@@ -0,0 +1,413 @@
import { AdminConfigPage } from '../po/admin-config.po'
import { AdminRegistrationPage } from '../po/admin-registration.po'
import { LoginPage } from '../po/login.po'
import { SignupPage } from '../po/signup.po'
import {
browserSleep,
findEmailTo,
getScreenshotPath,
getVerificationLink,
go,
isMobileDevice,
MockSMTPServer,
waitServerUp
} from '../utils'
function checkEndMessage (options: {
message: string
requiresEmailVerification: boolean
requiresApproval: boolean
afterEmailVerification: boolean
}) {
const { message, requiresApproval, requiresEmailVerification, afterEmailVerification } = options
{
const created = 'account has been created'
const request = 'account request has been sent'
if (requiresApproval) {
expect(message).toContain(request)
expect(message).not.toContain(created)
} else {
expect(message).not.toContain(request)
expect(message).toContain(created)
}
}
{
const checkEmail = 'Check your email'
if (requiresEmailVerification) {
expect(message).toContain(checkEmail)
} else {
expect(message).not.toContain(checkEmail)
const moderatorsApproval = 'moderator will check your registration request'
if (requiresApproval) {
expect(message).toContain(moderatorsApproval)
} else {
expect(message).not.toContain(moderatorsApproval)
}
}
}
{
const emailVerified = 'email has been verified'
if (afterEmailVerification) {
expect(message).toContain(emailVerified)
} else {
expect(message).not.toContain(emailVerified)
}
}
}
describe('Signup', () => {
let loginPage: LoginPage
let adminConfigPage: AdminConfigPage
let signupPage: SignupPage
let adminRegistrationPage: AdminRegistrationPage
async function prepareSignup (options: {
enabled: boolean
requiresApproval?: boolean
requiresEmailVerification?: boolean
}) {
await loginPage.loginAsRootUser()
await adminConfigPage.navigateTo('basic-configuration')
await adminConfigPage.toggleSignup(options.enabled)
if (options.enabled) {
if (options.requiresApproval !== undefined) {
await adminConfigPage.toggleSignupApproval(options.requiresApproval)
}
if (options.requiresEmailVerification !== undefined) {
await adminConfigPage.toggleSignupEmailVerification(options.requiresEmailVerification)
}
}
await adminConfigPage.save()
await loginPage.logout()
await browser.refresh()
}
before(async () => {
await waitServerUp()
})
beforeEach(async () => {
loginPage = new LoginPage(isMobileDevice())
adminConfigPage = new AdminConfigPage()
signupPage = new SignupPage()
adminRegistrationPage = new AdminRegistrationPage()
await browser.maximizeWindow()
})
describe('Signup disabled', function () {
it('Should disable signup', async () => {
await prepareSignup({ enabled: false })
await expect(signupPage.getRegisterMenuButton()).not.toBeDisplayed()
})
})
describe('Email verification disabled', function () {
describe('Direct registration', function () {
it('Should enable signup without approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: false, requiresEmailVerification: false })
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.validateStep()
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({ username: 'user_1', displayName: 'user_1_dn' })
await signupPage.validateStep()
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({ name: 'user_1_channel' })
await signupPage.validateStep()
})
it('Should be logged in', async function () {
await loginPage.ensureIsLoggedInAs('user_1_dn')
})
it('Should have a valid end message', async function () {
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: false,
requiresApproval: false,
afterEmailVerification: false
})
await browser.saveScreenshot(getScreenshotPath('direct-without-email.png'))
await loginPage.logout()
})
})
describe('Registration with approval', function () {
it('Should enable signup with approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: true, requiresEmailVerification: false })
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.fillRegistrationReason('my super reason')
await signupPage.validateStep()
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({ username: 'user_2', displayName: 'user_2 display name', password: 'password' })
await signupPage.validateStep()
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({ name: 'user_2_channel' })
await signupPage.validateStep()
})
it('Should have a valid end message', async function () {
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: false,
requiresApproval: true,
afterEmailVerification: false
})
await browser.saveScreenshot(getScreenshotPath('request-without-email.png'))
})
it('Should display a message when trying to login with this account', async function () {
const error = await loginPage.getLoginError('user_2', 'password')
expect(error).toContain('awaiting approval')
})
it('Should accept the registration', async function () {
await loginPage.loginAsRootUser()
await adminRegistrationPage.navigateToRegistratonsList()
await adminRegistrationPage.accept('user_2', 'moderation response')
await loginPage.logout()
})
it('Should be able to login with this new account', async function () {
await loginPage.login({ username: 'user_2', password: 'password', displayName: 'user_2 display name' })
await loginPage.logout()
})
})
})
describe('Email verification enabled', function () {
const emails: any[] = []
let emailPort: number
before(async () => {
const key = browser.options.baseUrl + '-emailPort'
// FIXME: typings are wrong, get returns a promise
// FIXME: use * because the key is not properly escaped by the shared store when using get(key)
emailPort = (await (browser.sharedStore.get('*') as unknown as Promise<number>))[key]
await MockSMTPServer.Instance.collectEmails(emailPort, emails)
})
describe('Direct registration', function () {
it('Should enable signup without approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: false, requiresEmailVerification: true })
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.validateStep()
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({ username: 'user_3', displayName: 'user_3 display name', email: 'user_3@example.com' })
await signupPage.validateStep()
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({ name: 'user_3_channel' })
await signupPage.validateStep()
})
it('Should have a valid end message', async function () {
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: true,
requiresApproval: false,
afterEmailVerification: false
})
await browser.saveScreenshot(getScreenshotPath('direct-with-email.png'))
})
it('Should validate the email', async function () {
let email: { text: string }
while (!(email = findEmailTo(emails, 'user_3@example.com'))) {
await browserSleep(100)
}
await go(getVerificationLink(email))
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: false,
requiresApproval: false,
afterEmailVerification: true
})
await browser.saveScreenshot(getScreenshotPath('direct-after-email.png'))
})
})
describe('Registration with approval', function () {
it('Should enable signup without approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: true, requiresEmailVerification: true })
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.fillRegistrationReason('my super reason 2')
await signupPage.validateStep()
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({
username: 'user_4',
displayName: 'user_4 display name',
email: 'user_4@example.com',
password: 'password'
})
await signupPage.validateStep()
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({ name: 'user_4_channel' })
await signupPage.validateStep()
})
it('Should have a valid end message', async function () {
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: true,
requiresApproval: true,
afterEmailVerification: false
})
await browser.saveScreenshot(getScreenshotPath('request-with-email.png'))
})
it('Should display a message when trying to login with this account', async function () {
const error = await loginPage.getLoginError('user_4', 'password')
expect(error).toContain('awaiting approval')
})
it('Should accept the registration', async function () {
await loginPage.loginAsRootUser()
await adminRegistrationPage.navigateToRegistratonsList()
await adminRegistrationPage.accept('user_4', 'moderation response 2')
await loginPage.logout()
})
it('Should validate the email', async function () {
let email: { text: string }
while (!(email = findEmailTo(emails, 'user_4@example.com'))) {
await browserSleep(100)
}
await go(getVerificationLink(email))
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: false,
requiresApproval: true,
afterEmailVerification: true
})
await browser.saveScreenshot(getScreenshotPath('request-after-email.png'))
})
})
after(() => {
MockSMTPServer.Instance.kill()
})
})
})
+82
ファイルの表示
@@ -0,0 +1,82 @@
import { AnonymousSettingsPage } from '../po/anonymous-settings.po'
import { LoginPage } from '../po/login.po'
import { MyAccountPage } from '../po/my-account.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { go, isMobileDevice, isSafari, waitServerUp } from '../utils'
describe('User settings', () => {
let videoUploadPage: VideoUploadPage
let loginPage: LoginPage
let videoWatchPage: VideoWatchPage
let myAccountPage: MyAccountPage
let anonymousSettingsPage: AnonymousSettingsPage
before(async () => {
await waitServerUp()
loginPage = new LoginPage(isMobileDevice())
videoUploadPage = new VideoUploadPage()
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
myAccountPage = new MyAccountPage()
anonymousSettingsPage = new AnonymousSettingsPage()
await browser.maximizeWindow()
})
describe('P2P', function () {
let videoUrl: string
async function goOnVideoWatchPage () {
await go(videoUrl)
await videoWatchPage.waitWatchVideoName('video')
}
async function checkP2P (enabled: boolean) {
await goOnVideoWatchPage()
expect(await videoWatchPage.isPrivacyWarningDisplayed()).toEqual(enabled)
await videoWatchPage.goOnAssociatedEmbed()
expect(await videoWatchPage.isEmbedWarningDisplayed()).toEqual(enabled)
}
before(async () => {
await loginPage.loginAsRootUser()
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video.mp4')
await videoUploadPage.validSecondUploadStep('video')
await videoWatchPage.waitWatchVideoName('video')
videoUrl = await browser.getUrl()
})
beforeEach(async function () {
await goOnVideoWatchPage()
})
it('Should have P2P enabled for a logged in user', async function () {
await checkP2P(true)
})
it('Should disable P2P for a logged in user', async function () {
await myAccountPage.navigateToMySettings()
await myAccountPage.clickOnP2PCheckbox()
await checkP2P(false)
})
it('Should have P2P enabled for anonymous users', async function () {
await loginPage.logout()
await checkP2P(true)
})
it('Should disable P2P for an anonymous user', async function () {
await anonymousSettingsPage.openSettings()
await anonymousSettingsPage.clickOnP2PCheckbox()
await checkP2P(false)
})
})
})
+232
ファイルの表示
@@ -0,0 +1,232 @@
import { LoginPage } from '../po/login.po'
import { MyAccountPage } from '../po/my-account.po'
import { PlayerPage } from '../po/player.po'
import { SignupPage } from '../po/signup.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { getScreenshotPath, go, isMobileDevice, isSafari, waitServerUp } from '../utils'
describe('Password protected videos', () => {
let videoUploadPage: VideoUploadPage
let loginPage: LoginPage
let videoWatchPage: VideoWatchPage
let signupPage: SignupPage
let playerPage: PlayerPage
let myAccountPage: MyAccountPage
let passwordProtectedVideoUrl: string
let playlistUrl: string
const seed = Math.random()
const passwordProtectedVideoName = seed + ' - password protected'
const publicVideoName1 = seed + ' - public 1'
const publicVideoName2 = seed + ' - public 2'
const videoPassword = 'password'
const regularUsername = 'user_1'
const regularUserPassword = 'user password'
const playlistName = seed + ' - playlist'
function testRateAndComment () {
it('Should add and remove like on video', async function () {
await videoWatchPage.like()
await videoWatchPage.like()
})
it('Should create thread on video', async function () {
await videoWatchPage.createThread('My first comment')
})
it('Should reply to thread on video', async function () {
await videoWatchPage.createReply('My first reply')
})
}
before(async () => {
await waitServerUp()
loginPage = new LoginPage(isMobileDevice())
videoUploadPage = new VideoUploadPage()
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
signupPage = new SignupPage()
playerPage = new PlayerPage()
myAccountPage = new MyAccountPage()
await browser.maximizeWindow()
})
describe('Owner', function () {
before(async () => {
await loginPage.loginAsRootUser()
})
it('Should login, upload a public video and save it to a playlist', async () => {
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video.mp4')
await videoUploadPage.validSecondUploadStep(publicVideoName1)
await videoWatchPage.clickOnSave()
await videoWatchPage.createPlaylist(playlistName)
await videoWatchPage.saveToPlaylist(playlistName)
await browser.pause(5000)
})
it('Should upload a password protected video', async () => {
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video2.mp4')
await videoUploadPage.setAsPasswordProtected(videoPassword)
await videoUploadPage.validSecondUploadStep(passwordProtectedVideoName)
await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName)
passwordProtectedVideoUrl = await browser.getUrl()
})
it('Should save to playlist the password protected video', async () => {
await videoWatchPage.clickOnSave()
await videoWatchPage.saveToPlaylist(playlistName)
})
it('Should upload a second public video and save it to playlist', async () => {
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video3.mp4')
await videoUploadPage.validSecondUploadStep(publicVideoName2)
await videoWatchPage.clickOnSave()
await videoWatchPage.saveToPlaylist(playlistName)
})
it('Should play video without password', async function () {
await go(passwordProtectedVideoUrl)
expect(!await videoWatchPage.isPasswordProtected())
await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName)
expect(await videoWatchPage.getPrivacy()).toBe('Password protected')
await playerPage.playAndPauseVideo(false, 2)
})
testRateAndComment()
it('Should play video on embed without password', async function () {
await videoWatchPage.goOnAssociatedEmbed()
await playerPage.playAndPauseVideo(false, 2)
})
it('Should have the playlist in my account', async function () {
await go('/')
await myAccountPage.navigateToMyPlaylists()
const videosNumberText = await myAccountPage.getPlaylistVideosText(playlistName)
expect(videosNumberText).toEqual('3 videos')
await myAccountPage.clickOnPlaylist(playlistName)
const count = await myAccountPage.countTotalPlaylistElements()
expect(count).toEqual(3)
})
it('Should update the playlist to public', async () => {
const url = await browser.getUrl()
const regex = /\/my-library\/video-playlists\/([^/]+)/i
const match = url.match(regex)
const uuid = match ? match[1] : null
expect(uuid).not.toBeNull()
await myAccountPage.updatePlaylistPrivacy(uuid, 'Public')
})
it('Should watch the playlist', async () => {
await myAccountPage.clickOnPlaylist(playlistName)
await myAccountPage.playPlaylist()
await videoWatchPage.waitUntilVideoName(publicVideoName1, 40 * 1000)
playlistUrl = await browser.getUrl()
await videoWatchPage.waitUntilVideoName(passwordProtectedVideoName, 40 * 1000)
await videoWatchPage.waitUntilVideoName(publicVideoName2, 40 * 1000)
})
after(async () => {
await loginPage.logout()
})
})
describe('Regular users', function () {
before(async () => {
await signupPage.fullSignup({
accountInfo: {
username: regularUsername,
password: regularUserPassword
},
channelInfo: {
name: 'user_1_channel'
}
})
})
it('Should requires password to play video', async function () {
await go(passwordProtectedVideoUrl)
expect(await videoWatchPage.isPasswordProtected())
await videoWatchPage.fillVideoPassword(videoPassword)
await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName)
expect(await videoWatchPage.getPrivacy()).toBe('Password protected')
await playerPage.playAndPauseVideo(true, 2)
})
testRateAndComment()
it('Should requires password to play video on embed', async function () {
await videoWatchPage.goOnAssociatedEmbed(true)
await playerPage.fillEmbedVideoPassword(videoPassword)
await playerPage.playAndPauseVideo(false, 2)
})
it('Should watch the playlist without password protected video', async () => {
await go(playlistUrl)
await playerPage.playVideo()
await videoWatchPage.waitUntilVideoName(publicVideoName2, 40 * 1000)
})
after(async () => {
await loginPage.logout()
})
})
describe('Anonymous users', function () {
it('Should requires password to play video', async function () {
await go(passwordProtectedVideoUrl)
expect(await videoWatchPage.isPasswordProtected())
await videoWatchPage.fillVideoPassword(videoPassword)
await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName)
expect(await videoWatchPage.getPrivacy()).toBe('Password protected')
await playerPage.playAndPauseVideo(true, 2)
})
it('Should requires password to play video on embed', async function () {
await videoWatchPage.goOnAssociatedEmbed(true)
await playerPage.fillEmbedVideoPassword(videoPassword)
await playerPage.playAndPauseVideo(false, 2)
})
it('Should watch the playlist without password protected video', async () => {
await go(playlistUrl)
await playerPage.playVideo()
await videoWatchPage.waitUntilVideoName(publicVideoName2, 40 * 1000)
})
})
after(async () => {
await browser.saveScreenshot(getScreenshotPath('after-test.png'))
})
})
+219
ファイルの表示
@@ -0,0 +1,219 @@
import { AdminConfigPage } from '../po/admin-config.po'
import { LoginPage } from '../po/login.po'
import { MyAccountPage } from '../po/my-account.po'
import { VideoListPage } from '../po/video-list.po'
import { VideoSearchPage } from '../po/video-search.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
import { NSFWPolicy } from '../types/common'
import { isMobileDevice, isSafari, waitServerUp } from '../utils'
describe('Videos list', () => {
let videoListPage: VideoListPage
let videoUploadPage: VideoUploadPage
let adminConfigPage: AdminConfigPage
let loginPage: LoginPage
let myAccountPage: MyAccountPage
let videoSearchPage: VideoSearchPage
let videoWatchPage: VideoWatchPage
const seed = Math.random()
const nsfwVideo = seed + ' - nsfw'
const normalVideo = seed + ' - normal'
async function checkNormalVideo () {
expect(await videoListPage.videoExists(normalVideo)).toBeTruthy()
expect(await videoListPage.videoIsBlurred(normalVideo)).toBeFalsy()
}
async function checkNSFWVideo (policy: NSFWPolicy, filterText?: string) {
if (policy === 'do_not_list') {
if (filterText) expect(filterText).toContain('hidden')
expect(await videoListPage.videoExists(nsfwVideo)).toBeFalsy()
return
}
if (policy === 'blur') {
if (filterText) expect(filterText).toContain('blurred')
expect(await videoListPage.videoExists(nsfwVideo)).toBeTruthy()
expect(await videoListPage.videoIsBlurred(nsfwVideo)).toBeTruthy()
return
}
// display
if (filterText) expect(filterText).toContain('displayed')
expect(await videoListPage.videoExists(nsfwVideo)).toBeTruthy()
expect(await videoListPage.videoIsBlurred(nsfwVideo)).toBeFalsy()
}
async function checkCommonVideoListPages (policy: NSFWPolicy) {
const promisesWithFilters = [
videoListPage.goOnRootAccount.bind(videoListPage),
videoListPage.goOnLocal.bind(videoListPage),
videoListPage.goOnRecentlyAdded.bind(videoListPage),
videoListPage.goOnTrending.bind(videoListPage),
videoListPage.goOnRootChannel.bind(videoListPage)
]
for (const p of promisesWithFilters) {
await p()
const filter = await videoListPage.getNSFWFilter()
const filterText = await filter.getText()
await checkNormalVideo()
await checkNSFWVideo(policy, filterText)
}
const promisesWithoutFilters = [
videoListPage.goOnRootAccountChannels.bind(videoListPage),
videoListPage.goOnHomepage.bind(videoListPage)
]
for (const p of promisesWithoutFilters) {
await p()
await checkNormalVideo()
await checkNSFWVideo(policy)
}
}
async function checkSearchPage (policy: NSFWPolicy) {
await videoSearchPage.search(normalVideo)
await checkNormalVideo()
await videoSearchPage.search(nsfwVideo)
await checkNSFWVideo(policy)
}
async function updateAdminNSFW (nsfw: NSFWPolicy) {
await adminConfigPage.navigateTo('instance-information')
await adminConfigPage.updateNSFWSetting(nsfw)
await adminConfigPage.save()
}
async function updateUserNSFW (nsfw: NSFWPolicy) {
await myAccountPage.navigateToMySettings()
await myAccountPage.updateNSFW(nsfw)
}
before(async () => {
await waitServerUp()
})
beforeEach(async () => {
videoListPage = new VideoListPage(isMobileDevice(), isSafari())
adminConfigPage = new AdminConfigPage()
loginPage = new LoginPage(isMobileDevice())
videoUploadPage = new VideoUploadPage()
myAccountPage = new MyAccountPage()
videoSearchPage = new VideoSearchPage()
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
await browser.maximizeWindow()
})
it('Should login and disable NSFW', async () => {
await loginPage.loginAsRootUser()
await updateUserNSFW('display')
})
it('Should set the homepage', async () => {
await adminConfigPage.navigateTo('instance-homepage')
await adminConfigPage.updateHomepage('<peertube-videos-list data-sort="-publishedAt"></peertube-videos-list>')
await adminConfigPage.save()
})
it('Should upload 2 videos (NSFW and classic videos)', async () => {
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video.mp4')
await videoUploadPage.setAsNSFW()
await videoUploadPage.validSecondUploadStep(nsfwVideo)
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video2.mp4')
await videoUploadPage.validSecondUploadStep(normalVideo)
})
it('Should logout', async function () {
await loginPage.logout()
})
describe('Anonymous users', function () {
it('Should correctly handle do not list', async () => {
await loginPage.loginAsRootUser()
await updateAdminNSFW('do_not_list')
await loginPage.logout()
await checkCommonVideoListPages('do_not_list')
await checkSearchPage('do_not_list')
})
it('Should correctly handle blur', async () => {
await loginPage.loginAsRootUser()
await updateAdminNSFW('blur')
await loginPage.logout()
await checkCommonVideoListPages('blur')
await checkSearchPage('blur')
})
it('Should correctly handle display', async () => {
await loginPage.loginAsRootUser()
await updateAdminNSFW('display')
await loginPage.logout()
await checkCommonVideoListPages('display')
await checkSearchPage('display')
})
})
describe('Logged in users', function () {
before(async () => {
await loginPage.loginAsRootUser()
})
it('Should correctly handle do not list', async () => {
await updateUserNSFW('do_not_list')
await checkCommonVideoListPages('do_not_list')
await checkSearchPage('do_not_list')
})
it('Should correctly handle blur', async () => {
await updateUserNSFW('blur')
await checkCommonVideoListPages('blur')
await checkSearchPage('blur')
})
it('Should correctly handle display', async () => {
await updateUserNSFW('display')
await checkCommonVideoListPages('display')
await checkSearchPage('display')
})
after(async () => {
await loginPage.logout()
})
})
describe('Default upload values', function () {
it('Should have default video values', async function () {
await loginPage.loginAsRootUser()
await videoUploadPage.navigateTo()
await videoUploadPage.uploadVideo('video3.mp4')
await videoUploadPage.validSecondUploadStep('video')
await videoWatchPage.waitWatchVideoName('video')
expect(await videoWatchPage.getPrivacy()).toBe('Public')
expect(await videoWatchPage.getLicence()).toBe('Unknown')
expect(await videoWatchPage.isDownloadEnabled()).toBeTruthy()
expect(await videoWatchPage.areCommentsEnabled()).toBeTruthy()
})
})
})
+1
ファイルの表示
@@ -0,0 +1 @@
export type NSFWPolicy = 'do_not_list' | 'blur' | 'display'
ベンダーファイル
+9
ファイルの表示
@@ -0,0 +1,9 @@
declare global {
namespace WebdriverIO {
interface Element {
chooseFile: (path: string) => Promise<void>
}
}
}
export {}
+53
ファイルの表示
@@ -0,0 +1,53 @@
async function browserSleep (amount: number) {
await browser.pause(amount)
}
function isMobileDevice () {
const platformName = (browser.capabilities['platformName'] || '').toLowerCase()
return platformName === 'android' || platformName === 'ios'
}
function isAndroid () {
const platformName = (browser.capabilities['platformName'] || '').toLowerCase()
return platformName === 'android'
}
function isSafari () {
return browser.capabilities['browserName'] &&
browser.capabilities['browserName'].toLowerCase() === 'safari'
}
function isIOS () {
return isMobileDevice() && isSafari()
}
async function go (url: string) {
await browser.url(url)
await browser.execute(() => {
const style = document.createElement('style')
style.innerHTML = 'p-toast { display: none }'
document.head.appendChild(style)
})
}
async function waitServerUp () {
await browser.waitUntil(async () => {
await go('/')
await browserSleep(500)
return $('<my-app>').isDisplayed()
}, { timeout: 20 * 1000 })
}
export {
isMobileDevice,
isSafari,
isIOS,
isAndroid,
waitServerUp,
go,
browserSleep
}
+43
ファイルの表示
@@ -0,0 +1,43 @@
async function getCheckbox (name: string) {
const input = $(`my-peertube-checkbox input[id=${name}]`)
await input.waitForExist()
return input.parentElement()
}
function isCheckboxSelected (name: string) {
return $(`input[id=${name}]`).isSelected()
}
async function selectCustomSelect (id: string, valueLabel: string) {
const wrapper = $(`[formcontrolname=${id}] .ng-arrow-wrapper`)
await wrapper.waitForClickable()
await wrapper.click()
const option = await $$(`[formcontrolname=${id}] .ng-option`).filter(async o => {
const text = await o.getText()
return text.trimStart().startsWith(valueLabel)
}).then(options => options[0])
await option.waitForDisplayed()
return option.click()
}
async function findParentElement (
el: WebdriverIO.Element,
finder: (el: WebdriverIO.Element) => Promise<boolean>
) {
if (await finder(el) === true) return el
return findParentElement(await el.parentElement(), finder)
}
export {
getCheckbox,
isCheckboxSelected,
selectCustomSelect,
findParentElement
}
+31
ファイルの表示
@@ -0,0 +1,31 @@
function getVerificationLink (email: { text: string }) {
const { text } = email
const regexp = /\[(?<link>http:\/\/[^\]]+)\]/g
const matched = text.matchAll(regexp)
if (!matched) throw new Error('Could not find verification link in email')
for (const match of matched) {
const link = match.groups.link
if (link.includes('/verify-account/')) return link
}
throw new Error('Could not find /verify-account/ link')
}
function findEmailTo (emails: { text: string, to: { address: string }[] }[], to: string) {
for (const email of emails) {
for (const { address } of email.to) {
if (address === to) return email
}
}
return undefined
}
export {
getVerificationLink,
findEmailTo
}
+17
ファイルの表示
@@ -0,0 +1,17 @@
import { mkdirSync } from 'fs'
import { join } from 'path'
const SCREENSHOTS_DIRECTORY = 'screenshots'
function createScreenshotsDirectory () {
mkdirSync(SCREENSHOTS_DIRECTORY, { recursive: true })
}
function getScreenshotPath (filename: string) {
return join(SCREENSHOTS_DIRECTORY, filename)
}
export {
createScreenshotsDirectory,
getScreenshotPath
}
+106
ファイルの表示
@@ -0,0 +1,106 @@
import { ChildProcessWithoutNullStreams } from 'child_process'
import { basename } from 'path'
import { setValue } from '@wdio/shared-store-service'
import { createScreenshotsDirectory } from './files'
import { runCommand, runServer } from './server'
let appInstance: number
let app: ChildProcessWithoutNullStreams
let emailPort: number
async function beforeLocalSuite (suite: any) {
const config = buildConfig(suite.file)
await runCommand('npm run clean:server:test -- ' + appInstance)
app = runServer(appInstance, config)
}
function afterLocalSuite () {
app.kill()
app = undefined
}
async function beforeLocalSession (config: { baseUrl: string }, capabilities: { browserName: string }) {
createScreenshotsDirectory()
appInstance = capabilities['browserName'] === 'chrome'
? 1
: 2
emailPort = 1025 + appInstance
config.baseUrl = 'http://localhost:900' + appInstance
await setValue(config.baseUrl + '-emailPort', emailPort)
}
async function onBrowserStackPrepare () {
const appInstance = 1
await runCommand('npm run clean:server:test -- ' + appInstance)
app = runServer(appInstance)
}
function onBrowserStackComplete () {
app.kill()
app = undefined
}
export {
beforeLocalSession,
afterLocalSuite,
beforeLocalSuite,
onBrowserStackPrepare,
onBrowserStackComplete
}
// ---------------------------------------------------------------------------
function buildConfig (suiteFile: string = undefined) {
const filename = basename(suiteFile)
if (filename === 'custom-server-defaults.e2e-spec.ts') {
return {
defaults: {
publish: {
download_enabled: false,
comments_policy: 2,
privacy: 2,
licence: 4
},
p2p: {
webapp: {
enabled: false
},
embed: {
enabled: false
}
}
}
}
}
if (filename === 'signup.e2e-spec.ts') {
return {
signup: {
limit: -1
},
smtp: {
hostname: '127.0.0.1',
port: emailPort
}
}
}
if (filename === 'video-password.e2e-spec.ts') {
return {
signup: {
enabled: true,
limit: -1
}
}
}
return {}
}
+8
ファイルの表示
@@ -0,0 +1,8 @@
export * from './common'
export * from './elements'
export * from './email'
export * from './files'
export * from './hooks'
export * from './mock-smtp'
export * from './server'
export * from './urls'
+57
ファイルの表示
@@ -0,0 +1,57 @@
import MailDev from '@peertube/maildev'
class MockSMTPServer {
private static instance: MockSMTPServer
private started = false
private maildev: any
private emails: object[]
collectEmails (port: number, emailsCollection: object[]) {
return new Promise<number>((res, rej) => {
this.emails = emailsCollection
if (this.started) {
return res(undefined)
}
this.maildev = new MailDev({
ip: '127.0.0.1',
smtp: port,
disableWeb: true,
silent: true
})
this.maildev.on('new', email => {
this.emails.push(email)
})
this.maildev.listen(err => {
if (err) return rej(err)
this.started = true
return res(port)
})
})
}
kill () {
if (!this.maildev) return
this.maildev.close()
this.maildev = null
MockSMTPServer.instance = null
}
static get Instance () {
return this.instance || (this.instance = new this())
}
}
// ---------------------------------------------------------------------------
export {
MockSMTPServer
}
+68
ファイルの表示
@@ -0,0 +1,68 @@
import { exec, spawn } from 'child_process'
import { join, resolve } from 'path'
function runServer (appInstance: number, config: any = {}) {
const env = Object.create(process.env)
env['NODE_OPTIONS'] = ''
env['NODE_ENV'] = 'test'
env['NODE_APP_INSTANCE'] = appInstance + ''
env['NODE_CONFIG'] = JSON.stringify({
rates_limit: {
api: {
max: 5000
},
login: {
max: 5000
}
},
log: {
level: 'warn'
},
transcoding: {
enabled: false
},
video_studio: {
enabled: false
},
...config
})
const forkOptions = {
env,
cwd: getRootCWD(),
detached: false
}
const p = spawn('node', [ join('dist', 'server.js') ], forkOptions)
p.stderr.on('data', data => console.error(data.toString()))
p.stdout.on('data', data => console.error(data.toString()))
return p
}
function runCommand (command: string) {
return new Promise<void>((res, rej) => {
// Reset NODE_OPTIONS env set by webdriverio
const env = { ...process.env, NODE_OPTIONS: '' }
const p = exec(command, { env, cwd: getRootCWD() })
p.stderr.on('data', data => console.error(data.toString()))
p.on('error', err => rej(err))
p.on('exit', () => res())
})
}
export {
runServer,
runCommand
}
// ---------------------------------------------------------------------------
function getRootCWD () {
return resolve('../..')
}
+21
ファイルの表示
@@ -0,0 +1,21 @@
const FIXTURE_URLS = {
INTERNAL_WEB_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?mode=web-video&start=0',
INTERNAL_HLS_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?start=0',
INTERNAL_EMBED_WEB_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?mode=web-video&start=0',
INTERNAL_EMBED_HLS_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?start=0',
INTERNAL_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/w/tKQmHcqdYZRdCszLUiWM3V?start=0',
INTERNAL_EMBED_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/videos/embed/tKQmHcqdYZRdCszLUiWM3V?start=0',
WEB_VIDEO: 'https://peertube2.cpy.re/w/122d093a-1ede-43bd-bd34-59d2931ffc5e',
HLS_EMBED: 'https://peertube2.cpy.re/videos/embed/969bf103-7818-43b5-94a0-de159e13de50',
HLS_PLAYLIST_EMBED: 'https://peertube2.cpy.re/video-playlists/embed/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a',
LIVE_VIDEO: 'https://peertube2.cpy.re/w/oBw6LwsMWWRkmXYfuYRpJd'
}
export {
FIXTURE_URLS
}