diff --git a/client/e2e/fixtures/video.mp4 b/client/e2e/fixtures/video.mp4 index f9c9e2dd6..c9ba8fd04 100644 Binary files a/client/e2e/fixtures/video.mp4 and b/client/e2e/fixtures/video.mp4 differ diff --git a/client/e2e/src/po/app.po.ts b/client/e2e/src/po/app.po.ts new file mode 100644 index 000000000..a636e825f --- /dev/null +++ b/client/e2e/src/po/app.po.ts @@ -0,0 +1,21 @@ +import { browser, by, element } from 'protractor' + +export class AppPage { + + async closeWelcomeModal () { + const firstHandle = await browser.getWindowHandle() + + if (await element(by.css('.configure-instance-button')).isPresent() === false) return + + await element(by.css('.configure-instance-button')).click() + + await browser.switchTo().window(firstHandle) + + await browser.refresh() + + await element(by.css('.form-group-checkbox')).click() + await element(by.css('.action-button-cancel')).click() + + await browser.switchTo().window(firstHandle) + } +} diff --git a/client/e2e/src/videos.e2e-spec.ts b/client/e2e/src/videos.e2e-spec.ts index 27706a506..075add531 100644 --- a/client/e2e/src/videos.e2e-spec.ts +++ b/client/e2e/src/videos.e2e-spec.ts @@ -4,6 +4,7 @@ import { LoginPage } from './po/login.po' import { browser } from 'protractor' import { VideoUpdatePage } from './po/video-update.po' import { MyAccountPage } from './po/my-account' +import { AppPage } from './po/app.po' async function skipIfUploadNotSupported () { if (await isMobileDevice() || await isSafari()) { @@ -30,6 +31,7 @@ describe('Videos workflow', () => { let videoUpdatePage: VideoUpdatePage let myAccountPage: MyAccountPage let loginPage: LoginPage + let appPage: AppPage let videoName = new Date().getTime() + ' video' const video2Name = new Date().getTime() + ' second video' @@ -42,6 +44,7 @@ describe('Videos workflow', () => { videoUpdatePage = new VideoUpdatePage() myAccountPage = new MyAccountPage() loginPage = new LoginPage() + appPage = new AppPage() if (await isMobileDevice()) { console.log('Mobile device detected.') @@ -61,6 +64,12 @@ describe('Videos workflow', () => { return loginPage.loginAsRootUser() }) + it('Should close the welcome modal', async () => { + if (await skipIfUploadNotSupported()) return + + await appPage.closeWelcomeModal() + }) + it('Should upload a video', async () => { if (await skipIfUploadNotSupported()) return diff --git a/client/src/app/shared/rxjs/zone.ts b/client/src/app/shared/rxjs/zone.ts new file mode 100644 index 000000000..74eed7032 --- /dev/null +++ b/client/src/app/shared/rxjs/zone.ts @@ -0,0 +1,40 @@ +import { SchedulerLike, Subscription } from 'rxjs' +import { NgZone } from '@angular/core' + +class LeaveZoneScheduler implements SchedulerLike { + constructor (private zone: NgZone, private scheduler: SchedulerLike) { + } + + schedule (...args: any[]): Subscription { + return this.zone.runOutsideAngular(() => + this.scheduler.schedule.apply(this.scheduler, args) + ) + } + + now (): number { + return this.scheduler.now() + } +} + +class EnterZoneScheduler implements SchedulerLike { + constructor (private zone: NgZone, private scheduler: SchedulerLike) { + } + + schedule (...args: any[]): Subscription { + return this.zone.run(() => + this.scheduler.schedule.apply(this.scheduler, args) + ) + } + + now (): number { + return this.scheduler.now() + } +} + +export function leaveZone (zone: NgZone, scheduler: SchedulerLike): SchedulerLike { + return new LeaveZoneScheduler(zone, scheduler) +} + +export function enterZone (zone: NgZone, scheduler: SchedulerLike): SchedulerLike { + return new EnterZoneScheduler(zone, scheduler) +} diff --git a/client/src/app/shared/user-subscription/user-subscription.service.ts b/client/src/app/shared/user-subscription/user-subscription.service.ts index bfb5848bc..9af9ba23e 100644 --- a/client/src/app/shared/user-subscription/user-subscription.service.ts +++ b/client/src/app/shared/user-subscription/user-subscription.service.ts @@ -1,7 +1,7 @@ -import { bufferTime, catchError, filter, map, tap, share, switchMap } from 'rxjs/operators' -import { Observable, ReplaySubject, Subject, of, merge } from 'rxjs' +import { bufferTime, catchError, filter, map, observeOn, share, switchMap, tap } from 'rxjs/operators' +import { asyncScheduler, merge, Observable, of, ReplaySubject, Subject } from 'rxjs' import { HttpClient, HttpParams } from '@angular/common/http' -import { Injectable } from '@angular/core' +import { Injectable, NgZone } from '@angular/core' import { ResultList } from '../../../../../shared' import { environment } from '../../../environments/environment' import { RestExtractor, RestService } from '../rest' @@ -11,6 +11,7 @@ import { VideoChannel as VideoChannelServer } from '../../../../../shared/models import { ComponentPaginationLight } from '@app/shared/rest/component-pagination.model' import { uniq } from 'lodash-es' import * as debug from 'debug' +import { enterZone, leaveZone } from '@app/shared/rxjs/zone' const logger = debug('peertube:subscriptions:UserSubscriptionService') @@ -32,13 +33,16 @@ export class UserSubscriptionService { constructor ( private authHttp: HttpClient, private restExtractor: RestExtractor, - private restService: RestService + private restService: RestService, + private ngZone: NgZone ) { this.existsObservable = merge( this.existsSubject.pipe( - bufferTime(500), + // We leave Angular zone so Protractor does not get stuck + bufferTime(500, leaveZone(this.ngZone, asyncScheduler)), filter(uris => uris.length !== 0), map(uris => uniq(uris)), + observeOn(enterZone(this.ngZone, asyncScheduler)), switchMap(uris => this.doSubscriptionsExist(uris)), share() ), diff --git a/client/src/app/shared/video-playlist/video-playlist.service.ts b/client/src/app/shared/video-playlist/video-playlist.service.ts index fc5eb5337..bae6f9e04 100644 --- a/client/src/app/shared/video-playlist/video-playlist.service.ts +++ b/client/src/app/shared/video-playlist/video-playlist.service.ts @@ -1,6 +1,6 @@ -import { bufferTime, catchError, filter, map, share, switchMap, tap } from 'rxjs/operators' -import { Injectable } from '@angular/core' -import { merge, Observable, of, ReplaySubject, Subject } from 'rxjs' +import { bufferTime, catchError, filter, map, observeOn, share, switchMap, tap } from 'rxjs/operators' +import { Injectable, NgZone } from '@angular/core' +import { asyncScheduler, merge, Observable, of, ReplaySubject, Subject } from 'rxjs' import { RestExtractor } from '../rest/rest-extractor.service' import { HttpClient, HttpParams } from '@angular/common/http' import { ResultList, VideoPlaylistElementCreate, VideoPlaylistElementUpdate } from '../../../../../shared' @@ -23,6 +23,7 @@ import { VideoPlaylistElement as ServerVideoPlaylistElement } from '@shared/mode import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-element.model' import { uniq } from 'lodash-es' import * as debug from 'debug' +import { enterZone, leaveZone } from '@app/shared/rxjs/zone' const logger = debug('peertube:playlists:VideoPlaylistService') @@ -49,13 +50,16 @@ export class VideoPlaylistService { private authHttp: HttpClient, private serverService: ServerService, private restExtractor: RestExtractor, - private restService: RestService + private restService: RestService, + private ngZone: NgZone ) { this.videoExistsInPlaylistObservable = merge( this.videoExistsInPlaylistNotifier.pipe( - bufferTime(500), + // We leave Angular zone so Protractor does not get stuck + bufferTime(500, leaveZone(this.ngZone, asyncScheduler)), filter(videoIds => videoIds.length !== 0), map(videoIds => uniq(videoIds)), + observeOn(enterZone(this.ngZone, asyncScheduler)), switchMap(videoIds => this.doVideosExistInPlaylist(videoIds)), share() ), diff --git a/scripts/e2e/local.sh b/scripts/e2e/local.sh index b883ccaa3..e8db2e808 100755 --- a/scripts/e2e/local.sh +++ b/scripts/e2e/local.sh @@ -11,4 +11,4 @@ npm run clean:server:test npm run concurrently -- -k -s first \ "cd client && npm run ng -- e2e --port 3333 -c local" \ - "NODE_ENV=test NODE_APP_INSTANCE=1 NODE_CONFIG='{ \"log\": { \"level\": \"warning\" } }' npm start" + "NODE_ENV=test NODE_APP_INSTANCE=1 NODE_CONFIG='{ \"log\": { \"level\": \"warning\" } }' node dist/server" diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts index 9dec12703..d1ac48292 100644 --- a/shared/extra-utils/videos/videos.ts +++ b/shared/extra-utils/videos/videos.ts @@ -587,19 +587,17 @@ async function completeVideoCheck ( 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')') .to.be.above(minSize).and.below(maxSize) - { - await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath) - } - - if (attributes.previewfile) { - await testImage(url, attributes.previewfile, videoDetails.previewPath) - } - const torrent = await webtorrentAdd(file.magnetUri, true) expect(torrent.files).to.be.an('array') expect(torrent.files.length).to.equal(1) expect(torrent.files[0].path).to.exist.and.to.not.equal('') } + + await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath) + + if (attributes.previewfile) { + await testImage(url, attributes.previewfile, videoDetails.previewPath) + } } async function videoUUIDToId (url: string, id: number | string) {