diff options
author | Wicklow <123956049+wickloww@users.noreply.github.com> | 2023-07-17 09:31:42 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-17 11:31:42 +0200 |
commit | cbe06f779f485935b227ccbb48a4493e4acda725 (patch) | |
tree | 93556507650a20ee544e1eb0a51588318156732f | |
parent | 260242decdd740bfee8b9f7c9536d98a6a951bcf (diff) | |
download | PeerTube-cbe06f779f485935b227ccbb48a4493e4acda725.tar.gz PeerTube-cbe06f779f485935b227ccbb48a4493e4acda725.tar.zst PeerTube-cbe06f779f485935b227ccbb48a4493e4acda725.zip |
Add e2e tests for password protected videos (#5860)
-rw-r--r-- | client/e2e/src/po/my-account.po.ts | 21 | ||||
-rw-r--r-- | client/e2e/src/po/player.po.ts | 11 | ||||
-rw-r--r-- | client/e2e/src/po/signup.po.ts | 22 | ||||
-rw-r--r-- | client/e2e/src/po/video-upload.po.ts | 9 | ||||
-rw-r--r-- | client/e2e/src/po/video-watch.po.ts | 81 | ||||
-rw-r--r-- | client/e2e/src/suites-local/video-password.e2e-spec.ts | 226 | ||||
-rw-r--r-- | client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html | 6 |
7 files changed, 370 insertions, 6 deletions
diff --git a/client/e2e/src/po/my-account.po.ts b/client/e2e/src/po/my-account.po.ts index 5188eca11..73eb6162c 100644 --- a/client/e2e/src/po/my-account.po.ts +++ b/client/e2e/src/po/my-account.po.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { getCheckbox, go } from '../utils' | 1 | import { getCheckbox, go, selectCustomSelect } from '../utils' |
2 | 2 | ||
3 | export class MyAccountPage { | 3 | export class MyAccountPage { |
4 | 4 | ||
@@ -117,6 +117,25 @@ export class MyAccountPage { | |||
117 | return go(url) | 117 | return go(url) |
118 | } | 118 | } |
119 | 119 | ||
120 | async updatePlaylistPrivacy (playlistUUID: string, privacy: 'Public' | 'Private' | 'Unlisted') { | ||
121 | go('/my-library/video-playlists/update/' + playlistUUID) | ||
122 | |||
123 | await browser.waitUntil(async () => { | ||
124 | return (await $('form .video-playlist-title').getText() === 'PLAYLIST') | ||
125 | }) | ||
126 | |||
127 | await selectCustomSelect('videoChannelId', 'Main root channel') | ||
128 | await selectCustomSelect('privacy', privacy) | ||
129 | |||
130 | const submit = await $('form input[type=submit]') | ||
131 | submit.waitForClickable() | ||
132 | await submit.click() | ||
133 | |||
134 | return browser.waitUntil(async () => { | ||
135 | return (await browser.getUrl()).includes('my-library/video-playlists') | ||
136 | }) | ||
137 | } | ||
138 | |||
120 | // My account Videos | 139 | // My account Videos |
121 | 140 | ||
122 | private async getVideoElement (name: string) { | 141 | private async getVideoElement (name: string) { |
diff --git a/client/e2e/src/po/player.po.ts b/client/e2e/src/po/player.po.ts index a20e683bc..33719da25 100644 --- a/client/e2e/src/po/player.po.ts +++ b/client/e2e/src/po/player.po.ts | |||
@@ -61,4 +61,15 @@ export class PlayerPage { | |||
61 | await playButton().waitForClickable() | 61 | await playButton().waitForClickable() |
62 | await playButton().click() | 62 | await playButton().click() |
63 | } | 63 | } |
64 | |||
65 | async fillEmbedVideoPassword (videoPassword: string) { | ||
66 | const videoPasswordInput = $('input#video-password-input') | ||
67 | const confirmButton = await $('button#video-password-submit') | ||
68 | |||
69 | await videoPasswordInput.clearValue() | ||
70 | await videoPasswordInput.setValue(videoPassword) | ||
71 | await confirmButton.waitForClickable() | ||
72 | |||
73 | return confirmButton.click() | ||
74 | } | ||
64 | } | 75 | } |
diff --git a/client/e2e/src/po/signup.po.ts b/client/e2e/src/po/signup.po.ts index 84a7a1847..4da35a7d4 100644 --- a/client/e2e/src/po/signup.po.ts +++ b/client/e2e/src/po/signup.po.ts | |||
@@ -62,4 +62,26 @@ export class SignupPage { | |||
62 | await $('#displayName').setValue(options.displayName || `${options.name} channel display name`) | 62 | await $('#displayName').setValue(options.displayName || `${options.name} channel display name`) |
63 | await $('#name').setValue(options.name) | 63 | await $('#name').setValue(options.name) |
64 | } | 64 | } |
65 | |||
66 | async fullSignup ({ accountInfo, channelInfo }: { | ||
67 | accountInfo: { | ||
68 | username: string | ||
69 | password?: string | ||
70 | displayName?: string | ||
71 | email?: string | ||
72 | } | ||
73 | channelInfo: { | ||
74 | name: string | ||
75 | } | ||
76 | }) { | ||
77 | await this.clickOnRegisterInMenu() | ||
78 | await this.validateStep() | ||
79 | await this.checkTerms() | ||
80 | await this.validateStep() | ||
81 | await this.fillAccountStep(accountInfo) | ||
82 | await this.validateStep() | ||
83 | await this.fillChannelStep(channelInfo) | ||
84 | await this.validateStep() | ||
85 | await this.getEndMessage() | ||
86 | } | ||
65 | } | 87 | } |
diff --git a/client/e2e/src/po/video-upload.po.ts b/client/e2e/src/po/video-upload.po.ts index 7e7989763..ff3841a02 100644 --- a/client/e2e/src/po/video-upload.po.ts +++ b/client/e2e/src/po/video-upload.po.ts | |||
@@ -64,6 +64,15 @@ export class VideoUploadPage { | |||
64 | return selectCustomSelect('privacy', 'Private') | 64 | return selectCustomSelect('privacy', 'Private') |
65 | } | 65 | } |
66 | 66 | ||
67 | async setAsPasswordProtected (videoPassword: string) { | ||
68 | selectCustomSelect('privacy', 'Password protected') | ||
69 | |||
70 | const videoPasswordInput = $('input#videoPassword') | ||
71 | await videoPasswordInput.clearValue() | ||
72 | |||
73 | return videoPasswordInput.setValue(videoPassword) | ||
74 | } | ||
75 | |||
67 | private getSecondStepSubmitButton () { | 76 | private getSecondStepSubmitButton () { |
68 | return $('.submit-container my-button') | 77 | return $('.submit-container my-button') |
69 | } | 78 | } |
diff --git a/client/e2e/src/po/video-watch.po.ts b/client/e2e/src/po/video-watch.po.ts index 982c90908..56870511d 100644 --- a/client/e2e/src/po/video-watch.po.ts +++ b/client/e2e/src/po/video-watch.po.ts | |||
@@ -43,19 +43,25 @@ export class VideoWatchPage { | |||
43 | return $('my-privacy-concerns').isDisplayed() | 43 | return $('my-privacy-concerns').isDisplayed() |
44 | } | 44 | } |
45 | 45 | ||
46 | async goOnAssociatedEmbed () { | 46 | async goOnAssociatedEmbed (passwordProtected = false) { |
47 | let url = await browser.getUrl() | 47 | let url = await browser.getUrl() |
48 | url = url.replace('/w/', '/videos/embed/') | 48 | url = url.replace('/w/', '/videos/embed/') |
49 | url = url.replace(':3333', ':9001') | 49 | url = url.replace(':3333', ':9001') |
50 | 50 | ||
51 | await go(url) | 51 | await go(url) |
52 | await this.waitEmbedForDisplayed() | 52 | |
53 | if (passwordProtected) await this.waitEmbedForVideoPasswordForm() | ||
54 | else await this.waitEmbedForDisplayed() | ||
53 | } | 55 | } |
54 | 56 | ||
55 | waitEmbedForDisplayed () { | 57 | waitEmbedForDisplayed () { |
56 | return $('.vjs-big-play-button').waitForDisplayed() | 58 | return $('.vjs-big-play-button').waitForDisplayed() |
57 | } | 59 | } |
58 | 60 | ||
61 | waitEmbedForVideoPasswordForm () { | ||
62 | return $('#video-password-input').waitForDisplayed() | ||
63 | } | ||
64 | |||
59 | isEmbedWarningDisplayed () { | 65 | isEmbedWarningDisplayed () { |
60 | return $('.peertube-dock-description').isDisplayed() | 66 | return $('.peertube-dock-description').isDisplayed() |
61 | } | 67 | } |
@@ -138,4 +144,75 @@ export class VideoWatchPage { | |||
138 | 144 | ||
139 | return elem() | 145 | return elem() |
140 | } | 146 | } |
147 | |||
148 | isPasswordProtected () { | ||
149 | return $('#confirmInput').isExisting() | ||
150 | } | ||
151 | |||
152 | async fillVideoPassword (videoPassword: string) { | ||
153 | const videoPasswordInput = $('input#confirmInput') | ||
154 | const confirmButton = await $('input[value="Confirm"]') | ||
155 | |||
156 | await videoPasswordInput.clearValue() | ||
157 | await videoPasswordInput.setValue(videoPassword) | ||
158 | await confirmButton.waitForClickable() | ||
159 | |||
160 | return confirmButton.click() | ||
161 | } | ||
162 | |||
163 | async like () { | ||
164 | const likeButton = await $('.action-button-like') | ||
165 | const isActivated = (await likeButton.getAttribute('class')).includes('activated') | ||
166 | |||
167 | let count: number | ||
168 | try { | ||
169 | count = parseInt(await $('.action-button-like > .count').getText()) | ||
170 | } catch (error) { | ||
171 | count = 0 | ||
172 | } | ||
173 | |||
174 | await likeButton.waitForClickable() | ||
175 | await likeButton.click() | ||
176 | |||
177 | if (isActivated) { | ||
178 | if (count === 1) { | ||
179 | return expect(!await $('.action-button-like > .count').isExisting()) | ||
180 | } else { | ||
181 | return expect(parseInt(await $('.action-button-like > .count').getText())).toBe(count - 1) | ||
182 | } | ||
183 | } else { | ||
184 | return expect(parseInt(await $('.action-button-like > .count').getText())).toBe(count + 1) | ||
185 | } | ||
186 | } | ||
187 | |||
188 | async createThread (comment: string) { | ||
189 | const textarea = await $('my-video-comment-add textarea') | ||
190 | |||
191 | await textarea.setValue(comment) | ||
192 | |||
193 | const confirmButton = await $('.comment-buttons .orange-button') | ||
194 | await confirmButton.waitForClickable() | ||
195 | await confirmButton.click() | ||
196 | |||
197 | const createdComment = await (await $('.comment-html p')).getText() | ||
198 | |||
199 | return expect(createdComment).toBe(comment) | ||
200 | } | ||
201 | |||
202 | async createReply (comment: string) { | ||
203 | const replyButton = await $('button.comment-action-reply') | ||
204 | |||
205 | await replyButton.click() | ||
206 | const textarea = await $('my-video-comment my-video-comment-add textarea') | ||
207 | |||
208 | await textarea.setValue(comment) | ||
209 | |||
210 | const confirmButton = await $('my-video-comment .comment-buttons .orange-button') | ||
211 | await confirmButton.waitForClickable() | ||
212 | await confirmButton.click() | ||
213 | |||
214 | const createdComment = await (await $('.is-child .comment-html p')).getText() | ||
215 | |||
216 | return expect(createdComment).toBe(comment) | ||
217 | } | ||
141 | } | 218 | } |
diff --git a/client/e2e/src/suites-local/video-password.e2e-spec.ts b/client/e2e/src/suites-local/video-password.e2e-spec.ts new file mode 100644 index 000000000..f4d836c05 --- /dev/null +++ b/client/e2e/src/suites-local/video-password.e2e-spec.ts | |||
@@ -0,0 +1,226 @@ | |||
1 | import { LoginPage } from '../po/login.po' | ||
2 | import { SignupPage } from '../po/signup.po' | ||
3 | import { PlayerPage } from '../po/player.po' | ||
4 | import { VideoUploadPage } from '../po/video-upload.po' | ||
5 | import { VideoWatchPage } from '../po/video-watch.po' | ||
6 | import { go, isMobileDevice, isSafari, waitServerUp } from '../utils' | ||
7 | import { MyAccountPage } from '../po/my-account.po' | ||
8 | |||
9 | describe('Password protected videos', () => { | ||
10 | let videoUploadPage: VideoUploadPage | ||
11 | let loginPage: LoginPage | ||
12 | let videoWatchPage: VideoWatchPage | ||
13 | let signupPage: SignupPage | ||
14 | let playerPage: PlayerPage | ||
15 | let myAccountPage: MyAccountPage | ||
16 | let passwordProtectedVideoUrl: string | ||
17 | let playlistUrl: string | ||
18 | |||
19 | const seed = Math.random() | ||
20 | const passwordProtectedVideoName = seed + ' - password protected' | ||
21 | const publicVideoName1 = seed + ' - public 1' | ||
22 | const publicVideoName2 = seed + ' - public 2' | ||
23 | const videoPassword = 'password' | ||
24 | const regularUsername = 'user_1' | ||
25 | const regularUserPassword = 'user password' | ||
26 | const playlistName = seed + ' - playlist' | ||
27 | |||
28 | function testRateAndComment () { | ||
29 | it('Should add and remove like on video', async function () { | ||
30 | await videoWatchPage.like() | ||
31 | await videoWatchPage.like() | ||
32 | }) | ||
33 | |||
34 | it('Should create thread on video', async function () { | ||
35 | await videoWatchPage.createThread('My first comment') | ||
36 | }) | ||
37 | |||
38 | it('Should reply to thread on video', async function () { | ||
39 | await videoWatchPage.createReply('My first reply') | ||
40 | }) | ||
41 | } | ||
42 | |||
43 | before(async () => { | ||
44 | await waitServerUp() | ||
45 | }) | ||
46 | |||
47 | beforeEach(async () => { | ||
48 | loginPage = new LoginPage(isMobileDevice()) | ||
49 | videoUploadPage = new VideoUploadPage() | ||
50 | videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari()) | ||
51 | signupPage = new SignupPage() | ||
52 | playerPage = new PlayerPage() | ||
53 | myAccountPage = new MyAccountPage() | ||
54 | |||
55 | await browser.maximizeWindow() | ||
56 | }) | ||
57 | |||
58 | describe('Owner', function () { | ||
59 | before(async () => { | ||
60 | await loginPage.loginAsRootUser() | ||
61 | }) | ||
62 | |||
63 | it('Should login, upload a public video and save it to a playlist', async () => { | ||
64 | await videoUploadPage.navigateTo() | ||
65 | await videoUploadPage.uploadVideo('video.mp4') | ||
66 | await videoUploadPage.validSecondUploadStep(publicVideoName1) | ||
67 | |||
68 | await videoWatchPage.clickOnSave() | ||
69 | |||
70 | await videoWatchPage.createPlaylist(playlistName) | ||
71 | |||
72 | await videoWatchPage.saveToPlaylist(playlistName) | ||
73 | await browser.pause(5000) | ||
74 | |||
75 | }) | ||
76 | |||
77 | it('Should upload a password protected video', async () => { | ||
78 | await videoUploadPage.navigateTo() | ||
79 | await videoUploadPage.uploadVideo('video2.mp4') | ||
80 | await videoUploadPage.setAsPasswordProtected(videoPassword) | ||
81 | await videoUploadPage.validSecondUploadStep(passwordProtectedVideoName) | ||
82 | |||
83 | await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName) | ||
84 | |||
85 | passwordProtectedVideoUrl = await browser.getUrl() | ||
86 | }) | ||
87 | |||
88 | it('Should save to playlist the password protected video', async () => { | ||
89 | await videoWatchPage.clickOnSave() | ||
90 | await videoWatchPage.saveToPlaylist(playlistName) | ||
91 | }) | ||
92 | |||
93 | it('Should upload a second public video and save it to playlist', async () => { | ||
94 | await videoUploadPage.navigateTo() | ||
95 | |||
96 | await videoUploadPage.uploadVideo('video3.mp4') | ||
97 | await videoUploadPage.validSecondUploadStep(publicVideoName2) | ||
98 | |||
99 | await videoWatchPage.clickOnSave() | ||
100 | await videoWatchPage.saveToPlaylist(playlistName) | ||
101 | }) | ||
102 | |||
103 | it('Should play video without password', async function () { | ||
104 | await go(passwordProtectedVideoUrl) | ||
105 | |||
106 | expect(!await videoWatchPage.isPasswordProtected()) | ||
107 | |||
108 | await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName) | ||
109 | |||
110 | expect(await videoWatchPage.getPrivacy()).toBe('Password protected') | ||
111 | await playerPage.playAndPauseVideo(false, 2) | ||
112 | }) | ||
113 | |||
114 | testRateAndComment() | ||
115 | |||
116 | it('Should play video on embed without password', async function () { | ||
117 | await videoWatchPage.goOnAssociatedEmbed() | ||
118 | await playerPage.playAndPauseVideo(false, 2) | ||
119 | }) | ||
120 | |||
121 | it('Should have the playlist in my account', async function () { | ||
122 | await go('/') | ||
123 | await myAccountPage.navigateToMyPlaylists() | ||
124 | const videosNumberText = await myAccountPage.getPlaylistVideosText(playlistName) | ||
125 | |||
126 | expect(videosNumberText).toEqual('3 videos') | ||
127 | await myAccountPage.clickOnPlaylist(playlistName) | ||
128 | |||
129 | const count = await myAccountPage.countTotalPlaylistElements() | ||
130 | expect(count).toEqual(3) | ||
131 | }) | ||
132 | |||
133 | it('Should update the playlist to public', async () => { | ||
134 | const url = await browser.getUrl() | ||
135 | const regex = /\/([a-f0-9-]+)$/i | ||
136 | const match = url.match(regex) | ||
137 | const uuid = match ? match[1] : null | ||
138 | |||
139 | await myAccountPage.updatePlaylistPrivacy(uuid, 'Public') | ||
140 | }) | ||
141 | |||
142 | it('Should watch the playlist', async () => { | ||
143 | await myAccountPage.clickOnPlaylist(playlistName) | ||
144 | await myAccountPage.playPlaylist() | ||
145 | playlistUrl = await browser.getUrl() | ||
146 | |||
147 | await videoWatchPage.waitUntilVideoName(publicVideoName1, 40 * 1000) | ||
148 | await videoWatchPage.waitUntilVideoName(passwordProtectedVideoName, 40 * 1000) | ||
149 | await videoWatchPage.waitUntilVideoName(publicVideoName2, 40 * 1000) | ||
150 | }) | ||
151 | |||
152 | after(async () => { | ||
153 | await loginPage.logout() | ||
154 | }) | ||
155 | }) | ||
156 | |||
157 | describe('Regular users', function () { | ||
158 | before(async () => { | ||
159 | await signupPage.fullSignup({ | ||
160 | accountInfo: { | ||
161 | username: regularUsername, | ||
162 | password: regularUserPassword | ||
163 | }, | ||
164 | channelInfo: { | ||
165 | name: 'user_1_channel' | ||
166 | } | ||
167 | }) | ||
168 | }) | ||
169 | |||
170 | it('Should requires password to play video', async function () { | ||
171 | await go(passwordProtectedVideoUrl) | ||
172 | |||
173 | expect(await videoWatchPage.isPasswordProtected()) | ||
174 | |||
175 | await videoWatchPage.fillVideoPassword(videoPassword) | ||
176 | await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName) | ||
177 | |||
178 | expect(await videoWatchPage.getPrivacy()).toBe('Password protected') | ||
179 | await playerPage.playAndPauseVideo(true, 2) | ||
180 | }) | ||
181 | |||
182 | testRateAndComment() | ||
183 | |||
184 | it('Should requires password to play video on embed', async function () { | ||
185 | await videoWatchPage.goOnAssociatedEmbed(true) | ||
186 | await playerPage.fillEmbedVideoPassword(videoPassword) | ||
187 | await playerPage.playAndPauseVideo(false, 2) | ||
188 | }) | ||
189 | |||
190 | it('Should watch the playlist without password protected video', async () => { | ||
191 | await go(playlistUrl) | ||
192 | await playerPage.playVideo() | ||
193 | await videoWatchPage.waitUntilVideoName(publicVideoName2, 40 * 1000) | ||
194 | }) | ||
195 | |||
196 | after(async () => { | ||
197 | await loginPage.logout() | ||
198 | }) | ||
199 | }) | ||
200 | |||
201 | describe('Anonymous users', function () { | ||
202 | it('Should requires password to play video', async function () { | ||
203 | await go(passwordProtectedVideoUrl) | ||
204 | |||
205 | expect(await videoWatchPage.isPasswordProtected()) | ||
206 | |||
207 | await videoWatchPage.fillVideoPassword(videoPassword) | ||
208 | await videoWatchPage.waitWatchVideoName(passwordProtectedVideoName) | ||
209 | |||
210 | expect(await videoWatchPage.getPrivacy()).toBe('Password protected') | ||
211 | await playerPage.playAndPauseVideo(true, 2) | ||
212 | }) | ||
213 | |||
214 | it('Should requires password to play video on embed', async function () { | ||
215 | await videoWatchPage.goOnAssociatedEmbed(true) | ||
216 | await playerPage.fillEmbedVideoPassword(videoPassword) | ||
217 | await playerPage.playAndPauseVideo(false, 2) | ||
218 | }) | ||
219 | |||
220 | it('Should watch the playlist without password protected video', async () => { | ||
221 | await go(playlistUrl) | ||
222 | await playerPage.playVideo() | ||
223 | await videoWatchPage.waitUntilVideoName(publicVideoName2, 40 * 1000) | ||
224 | }) | ||
225 | }) | ||
226 | }) | ||
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html index a3c2aab44..9475820ac 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html | |||
@@ -50,9 +50,9 @@ | |||
50 | <div class="form-group"> | 50 | <div class="form-group"> |
51 | <label i18n for="privacy">Privacy</label> | 51 | <label i18n for="privacy">Privacy</label> |
52 | <div class="peertube-select-container"> | 52 | <div class="peertube-select-container"> |
53 | <select id="privacy" formControlName="privacy" class="form-control"> | 53 | <my-select-options |
54 | <option *ngFor="let privacy of videoPlaylistPrivacies" [value]="privacy.id">{{ privacy.label }}</option> | 54 | labelForId="privacy" [items]="videoPlaylistPrivacies" formControlName="privacy" [clearable]="false" |
55 | </select> | 55 | ></my-select-options> |
56 | </div> | 56 | </div> |
57 | 57 | ||
58 | <div *ngIf="formErrors.privacy" class="form-error"> | 58 | <div *ngIf="formErrors.privacy" class="form-error"> |