From 3cf68b869decf07ff7435fe1436d4f3134df1bf4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 14 Dec 2021 17:17:01 +0100 Subject: [PATCH] Ability for admins to set default upload values --- client/e2e/src/po/video-watch.po.ts | 29 ++++- .../custom-server-defaults.e2e-spec.ts | 37 ++++++ .../src/suites-local/videos-list.e2e-spec.ts | 24 ++++ client/e2e/src/utils/hooks.ts | 64 ++++++++++ client/e2e/src/utils/index.ts | 2 + client/e2e/src/utils/server.ts | 63 ++++++++++ client/e2e/wdio.browserstack.conf.ts | 7 +- client/e2e/wdio.local-test.conf.ts | 9 +- client/e2e/wdio.local.conf.ts | 13 +- .../shared/video-edit.component.ts | 9 +- .../video-go-live.component.ts | 2 - .../video-import-torrent.component.ts | 2 - .../video-import-url.component.ts | 2 - .../video-add-components/video-send.ts | 4 +- .../video-upload.component.ts | 2 - .../action-buttons.component.html | 2 +- .../action-buttons.component.scss | 3 +- .../metadata/video-attributes.component.html | 14 +-- config/default.yaml | 39 ++++-- config/production.yaml.example | 39 ++++-- scripts/e2e/browserstack.sh | 6 +- scripts/e2e/local.sh | 14 +-- server/controllers/api/videos/import.ts | 6 +- server/initializers/checker-before-init.ts | 1 + server/initializers/config.ts | 11 +- server/lib/server-config-manager.ts | 9 ++ server/lib/video.ts | 7 +- server/tests/api/server/config-defaults.ts | 116 ++++++++++++++++++ server/tests/api/server/index.ts | 2 + shared/models/server/server-config.model.ts | 10 ++ support/doc/development/tests.md | 8 +- 31 files changed, 467 insertions(+), 89 deletions(-) create mode 100644 client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts create mode 100644 client/e2e/src/utils/hooks.ts create mode 100644 client/e2e/src/utils/server.ts create mode 100644 server/tests/api/server/config-defaults.ts diff --git a/client/e2e/src/po/video-watch.po.ts b/client/e2e/src/po/video-watch.po.ts index 41425f4d7..1406c971a 100644 --- a/client/e2e/src/po/video-watch.po.ts +++ b/client/e2e/src/po/video-watch.po.ts @@ -21,6 +21,24 @@ export class VideoWatchPage { return this.getVideoNameElement().then(e => e.getText()) } + getPrivacy () { + return $('.attribute-privacy .attribute-value').getText() + } + + getLicence () { + return $('.attribute-licence .attribute-value').getText() + } + + async isDownloadEnabled () { + await this.clickOnMoreDropdownIcon() + + return $('.dropdown-item .icon-download').isExisting() + } + + areCommentsEnabled () { + return $('my-video-comment-add').isExisting() + } + async goOnAssociatedEmbed () { let url = await browser.getUrl() url = url.replace('/w/', '/videos/embed/') @@ -38,10 +56,8 @@ export class VideoWatchPage { } async clickOnUpdate () { - const dropdown = $('my-video-actions-dropdown .action-button') - await dropdown.click() + await this.clickOnMoreDropdownIcon() - await $('.dropdown-menu.show .dropdown-item').waitForDisplayed() const items = await $$('.dropdown-menu.show .dropdown-item') for (const item of items) { @@ -86,6 +102,13 @@ export class VideoWatchPage { }, { 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 () => { diff --git a/client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts b/client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts new file mode 100644 index 000000000..c2c8edcc9 --- /dev/null +++ b/client/e2e/src/suites-local/custom-server-defaults.e2e-spec.ts @@ -0,0 +1,37 @@ +import { LoginPage } from '../po/login.po' +import { VideoUploadPage } from '../po/video-upload.po' +import { VideoWatchPage } from '../po/video-watch.po' +import { isMobileDevice, isSafari, waitServerUp } from '../utils' + +describe('Custom server defaults', () => { + let videoUploadPage: VideoUploadPage + let loginPage: LoginPage + let videoWatchPage: VideoWatchPage + + before(async () => { + await waitServerUp() + }) + + beforeEach(async () => { + loginPage = new LoginPage() + videoUploadPage = new VideoUploadPage() + videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari()) + + await browser.maximizeWindow() + }) + + it('Should upload a video with custom default values', async function () { + await loginPage.loginAsRootUser() + await videoUploadPage.navigateTo() + await videoUploadPage.uploadVideo() + await videoUploadPage.validSecondUploadStep('video') + + await videoWatchPage.waitWatchVideoName('video') + + expect(await videoWatchPage.getPrivacy()).toBe('Internal') + expect(await videoWatchPage.getLicence()).toBe('Attribution - Non Commercial') + expect(await videoWatchPage.isDownloadEnabled()).toBeFalsy() + expect(await videoWatchPage.areCommentsEnabled()).toBeFalsy() + }) + +}) diff --git a/client/e2e/src/suites-local/videos-list.e2e-spec.ts b/client/e2e/src/suites-local/videos-list.e2e-spec.ts index 1e0a88859..bca6018b9 100644 --- a/client/e2e/src/suites-local/videos-list.e2e-spec.ts +++ b/client/e2e/src/suites-local/videos-list.e2e-spec.ts @@ -4,6 +4,7 @@ import { MyAccountPage } from '../po/my-account' 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' @@ -14,6 +15,7 @@ describe('Videos list', () => { let loginPage: LoginPage let myAccountPage: MyAccountPage let videoSearchPage: VideoSearchPage + let videoWatchPage: VideoWatchPage const seed = Math.random() const nsfwVideo = seed + ' - nsfw' @@ -108,6 +110,7 @@ describe('Videos list', () => { videoUploadPage = new VideoUploadPage() myAccountPage = new MyAccountPage() videoSearchPage = new VideoSearchPage() + videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari()) await browser.maximizeWindow() }) @@ -191,5 +194,26 @@ describe('Videos list', () => { 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() + 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() + }) }) }) diff --git a/client/e2e/src/utils/hooks.ts b/client/e2e/src/utils/hooks.ts new file mode 100644 index 000000000..e42c6a5d8 --- /dev/null +++ b/client/e2e/src/utils/hooks.ts @@ -0,0 +1,64 @@ +import { ChildProcessWithoutNullStreams } from 'child_process' +import { basename } from 'path' +import { runCommand, runServer } from './server' + +let appInstance: string +let app: ChildProcessWithoutNullStreams + +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 +} + +function beforeLocalSession (config: { baseUrl: string }, capabilities: { browserName: string }) { + appInstance = capabilities['browserName'] === 'chrome' ? '1' : '2' + config.baseUrl = 'http://localhost:900' + appInstance +} + +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_enabled: false, + privacy: 4, + licence: 4 + } + } + } + } + + return {} +} diff --git a/client/e2e/src/utils/index.ts b/client/e2e/src/utils/index.ts index 5da1ad517..354352ee2 100644 --- a/client/e2e/src/utils/index.ts +++ b/client/e2e/src/utils/index.ts @@ -1,3 +1,5 @@ export * from './common' export * from './elements' +export * from './hooks' +export * from './server' export * from './urls' diff --git a/client/e2e/src/utils/server.ts b/client/e2e/src/utils/server.ts new file mode 100644 index 000000000..7089a5c9c --- /dev/null +++ b/client/e2e/src/utils/server.ts @@ -0,0 +1,63 @@ +import { exec, spawn } from 'child_process' +import { join, resolve } from 'path' + +function runServer (appInstance: string, config: any = {}) { + const env = Object.create(process.env) + 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' + }, + signup: { + enabled: false + }, + transcoding: { + 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((res, rej) => { + const p = exec(command, { 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('../..') +} diff --git a/client/e2e/wdio.browserstack.conf.ts b/client/e2e/wdio.browserstack.conf.ts index 43614a862..b89cdbc2e 100644 --- a/client/e2e/wdio.browserstack.conf.ts +++ b/client/e2e/wdio.browserstack.conf.ts @@ -1,3 +1,4 @@ +import { onBrowserStackComplete, onBrowserStackPrepare } from './src/utils' import { config as mainConfig } from './wdio.main.conf' const user = process.env.BROWSERSTACK_USER @@ -114,6 +115,10 @@ module.exports = { if (capabilities['bstack:options'].realMobile === true) { capabilities['bstack:options'].local = false } - } + }, + + onPrepare: onBrowserStackPrepare, + onComplete: onBrowserStackComplete + } as WebdriverIO.Config } diff --git a/client/e2e/wdio.local-test.conf.ts b/client/e2e/wdio.local-test.conf.ts index 32e6d340c..5389ebcf0 100644 --- a/client/e2e/wdio.local-test.conf.ts +++ b/client/e2e/wdio.local-test.conf.ts @@ -1,3 +1,4 @@ +import { afterLocalSuite, beforeLocalSuite, beforeLocalSession } from './src/utils' import { config as mainConfig } from './wdio.main.conf' const prefs = { @@ -21,12 +22,16 @@ module.exports = { browserName: 'chrome', acceptInsecureCerts: true, 'goog:chromeOptions': { - args: [ '--headless', '--disable-gpu', '--window-size=1280,1024' ], + args: [ '--disable-gpu', '--window-size=1280,1024' ], prefs } } ], - services: [ 'chromedriver' ] + services: [ 'chromedriver' ], + + beforeSession: beforeLocalSession, + beforeSuite: beforeLocalSuite, + afterSuite: afterLocalSuite } as WebdriverIO.Config } diff --git a/client/e2e/wdio.local.conf.ts b/client/e2e/wdio.local.conf.ts index 43b820ca6..d02679e06 100644 --- a/client/e2e/wdio.local.conf.ts +++ b/client/e2e/wdio.local.conf.ts @@ -1,3 +1,4 @@ +import { afterLocalSuite, beforeLocalSession, beforeLocalSuite } from './src/utils' import { config as mainConfig } from './wdio.main.conf' const prefs = { @@ -11,7 +12,7 @@ module.exports = { runner: 'local', - maxInstances: 2, + maxInstancesPerCapability: 1, capabilities: [ { @@ -34,12 +35,8 @@ module.exports = { services: [ 'chromedriver', 'geckodriver' ], - beforeSession: function (config, capabilities) { - if (capabilities['browserName'] === 'chrome') { - config.baseUrl = 'http://localhost:9001' - } else { - config.baseUrl = 'http://localhost:9002' - } - } + beforeSession: beforeLocalSession, + beforeSuite: beforeLocalSuite, + afterSuite: afterLocalSuite } as WebdriverIO.Config } diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts index 39767f258..2bec933e9 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts @@ -110,9 +110,10 @@ export class VideoEditComponent implements OnInit, OnDestroy { updateForm () { const defaultValues: any = { nsfw: 'false', - commentsEnabled: 'true', - downloadEnabled: 'true', + commentsEnabled: this.serverConfig.defaults.publish.commentsEnabled, + downloadEnabled: this.serverConfig.defaults.publish.downloadEnabled, waitTranscoding: 'true', + licence: this.serverConfig.defaults.publish.licence, tags: [] } const obj: any = { @@ -160,6 +161,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { } ngOnInit () { + this.serverConfig = this.serverService.getHTMLConfig() + this.updateForm() this.pluginService.ensurePluginsAreLoaded('video-edit') @@ -200,8 +203,6 @@ export class VideoEditComponent implements OnInit, OnDestroy { } }) - this.serverConfig = this.serverService.getHTMLConfig() - this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) this.ngZone.runOutsideAngular(() => { diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts index 01eabb0d7..46a7ebb0b 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts @@ -70,8 +70,6 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView privacy: this.highestPrivacy, nsfw: this.serverConfig.instance.isNSFW, waitTranscoding: true, - commentsEnabled: true, - downloadEnabled: true, permanentLive: this.firstStepPermanentLive, saveReplay: this.firstStepPermanentLive === false && this.isReplayAllowed(), channelId: this.firstStepChannelId diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts index 87e47683f..5e758910e 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts @@ -81,8 +81,6 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af const videoUpdate: VideoUpdate = { privacy: this.highestPrivacy, waitTranscoding: false, - commentsEnabled: true, - downloadEnabled: true, channelId: this.firstStepChannelId } diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts index 3487c1adf..2ea70ed55 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts @@ -68,8 +68,6 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV const videoUpdate: VideoUpdate = { privacy: this.highestPrivacy, waitTranscoding: false, - commentsEnabled: true, - downloadEnabled: true, channelId: this.firstStepChannelId } diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-send.ts b/client/src/app/+videos/+video-edit/video-add-components/video-send.ts index efa8c85a3..5e086ef42 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-send.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-send.ts @@ -49,7 +49,9 @@ export abstract class VideoSend extends FormReactive implements OnInit { this.serverService.getVideoPrivacies() .subscribe( privacies => { - const { videoPrivacies, defaultPrivacyId } = this.videoService.explainedPrivacyLabels(privacies) + const defaultPrivacy = this.serverConfig.defaults.publish.privacy + + const { videoPrivacies, defaultPrivacyId } = this.videoService.explainedPrivacyLabels(privacies, defaultPrivacy) this.videoPrivacies = videoPrivacies this.firstStepPrivacyId = defaultPrivacyId diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts index 28d7ec458..76f154249 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts @@ -277,8 +277,6 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy private uploadFile (file: File, previewfile?: File) { const metadata = { waitTranscoding: true, - commentsEnabled: true, - downloadEnabled: true, channelId: this.firstStepChannelId, nsfw: this.serverConfig.instance.isNSFW, privacy: this.highestPrivacy.toString(), diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html index 8fb244cc4..f23efca98 100644 --- a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html @@ -36,7 +36,7 @@