mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-05 06:37:10 -04:00
Fixed admin tests.
This commit is contained in:
parent
cda035057d
commit
54572b2075
15 changed files with 300 additions and 292 deletions
61
.github/workflows/frontend-admin-tests.yml
vendored
61
.github/workflows/frontend-admin-tests.yml
vendored
|
@ -49,12 +49,12 @@ jobs:
|
||||||
${{ runner.os }}-pnpm-store-
|
${{ runner.os }}-pnpm-store-
|
||||||
- name: Only install direct dependencies
|
- name: Only install direct dependencies
|
||||||
run: pnpm config set auto-install-peers false
|
run: pnpm config set auto-install-peers false
|
||||||
-
|
#-
|
||||||
name: Install etherpad plugins
|
# name: Install etherpad plugins
|
||||||
# We intentionally install an old ep_align version to test upgrades to
|
# # We intentionally install an old ep_align version to test upgrades to
|
||||||
# the minor version number. The --legacy-peer-deps flag is required to
|
# # the minor version number. The --legacy-peer-deps flag is required to
|
||||||
# work around a bug in npm v7: https://github.com/npm/cli/issues/2199
|
# # work around a bug in npm v7: https://github.com/npm/cli/issues/2199
|
||||||
run: pnpm install --workspace-root ep_align@0.2.27
|
# run: pnpm install --workspace-root ep_align@0.2.27
|
||||||
# Etherpad core dependencies must be installed after installing the
|
# Etherpad core dependencies must be installed after installing the
|
||||||
# plugin's dependencies, otherwise npm will try to hoist common
|
# plugin's dependencies, otherwise npm will try to hoist common
|
||||||
# dependencies by removing them from src/node_modules and installing them
|
# dependencies by removing them from src/node_modules and installing them
|
||||||
|
@ -67,9 +67,9 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||||
run: bin/installDeps.sh
|
run: bin/installDeps.sh
|
||||||
-
|
#-
|
||||||
name: Install etherpad plugins
|
# name: Install etherpad plugins
|
||||||
run: rm -Rf node_modules/ep_align/static/tests/*
|
# run: rm -Rf node_modules/ep_align/static/tests/*
|
||||||
-
|
-
|
||||||
name: export GIT_HASH to env
|
name: export GIT_HASH to env
|
||||||
id: environment
|
id: environment
|
||||||
|
@ -79,7 +79,7 @@ jobs:
|
||||||
run: cp settings.json.template settings.json
|
run: cp settings.json.template settings.json
|
||||||
-
|
-
|
||||||
name: Write custom settings.json that enables the Admin UI tests
|
name: Write custom settings.json that enables the Admin UI tests
|
||||||
run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme\",\"is_admin\":true}}/' settings.json"
|
run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme1\",\"is_admin\":true}}/' settings.json"
|
||||||
-
|
-
|
||||||
name: increase maxHttpBufferSize
|
name: increase maxHttpBufferSize
|
||||||
run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 10000000/' settings.json"
|
run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 10000000/' settings.json"
|
||||||
|
@ -87,23 +87,26 @@ jobs:
|
||||||
name: Disable import/export rate limiting
|
name: Disable import/export rate limiting
|
||||||
run: |
|
run: |
|
||||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json
|
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json
|
||||||
-
|
#-
|
||||||
name: Remove standard frontend test files, so only admin tests are run
|
# uses: saucelabs/sauce-connect-action@v2.3.6
|
||||||
run: mv src/tests/frontend/specs/* /tmp && mv /tmp/admin*.js src/tests/frontend/specs
|
# with:
|
||||||
-
|
# username: ${{ secrets.SAUCE_USERNAME }}
|
||||||
uses: saucelabs/sauce-connect-action@v2.3.6
|
# accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||||
with:
|
# tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||||
username: ${{ secrets.SAUCE_USERNAME }}
|
#-
|
||||||
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
# name: Run the frontend admin tests
|
||||||
tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
# shell: bash
|
||||||
-
|
# env:
|
||||||
name: Run the frontend admin tests
|
# SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
||||||
shell: bash
|
# SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||||
env:
|
# SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
||||||
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
# TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||||
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
# GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
||||||
SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
# run: |
|
||||||
TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
# src/tests/frontend/travis/adminrunner.sh
|
||||||
GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
- name: Run the frontend admin tests
|
||||||
run: |
|
run: |
|
||||||
src/tests/frontend/travis/adminrunner.sh
|
pnpm run dev &
|
||||||
|
sleep 30
|
||||||
|
pnpm exec playwright install
|
||||||
|
pnpm run test-admin
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -26,3 +26,4 @@ plugin_packages
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
/src/templates/admin
|
/src/templates/admin
|
||||||
/src/test-results
|
/src/test-results
|
||||||
|
playwright-report
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {useStore} from "../store/store.ts";
|
import {useStore} from "../store/store.ts";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useMemo, useState} from "react";
|
||||||
import {InstalledPlugin, PluginDef, SearchParams} from "./Plugin.ts";
|
import {InstalledPlugin, PluginDef, SearchParams} from "./Plugin.ts";
|
||||||
import {useDebounce} from "../utils/useDebounce.ts";
|
import {useDebounce} from "../utils/useDebounce.ts";
|
||||||
import {Trans, useTranslation} from "react-i18next";
|
import {Trans, useTranslation} from "react-i18next";
|
||||||
|
@ -9,6 +9,18 @@ export const HomePage = () => {
|
||||||
const pluginsSocket = useStore(state=>state.pluginsSocket)
|
const pluginsSocket = useStore(state=>state.pluginsSocket)
|
||||||
const [plugins,setPlugins] = useState<PluginDef[]>([])
|
const [plugins,setPlugins] = useState<PluginDef[]>([])
|
||||||
const [installedPlugins, setInstalledPlugins] = useState<InstalledPlugin[]>([])
|
const [installedPlugins, setInstalledPlugins] = useState<InstalledPlugin[]>([])
|
||||||
|
const sortedInstalledPlugins = useMemo(()=>{
|
||||||
|
return installedPlugins.sort((a, b)=>{
|
||||||
|
if(a.name < b.name){
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if(a.name > b.name){
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
} ,[installedPlugins])
|
||||||
const [searchParams, setSearchParams] = useState<SearchParams>({
|
const [searchParams, setSearchParams] = useState<SearchParams>({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 99999,
|
limit: 99999,
|
||||||
|
@ -125,7 +137,7 @@ export const HomePage = () => {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody style={{overflow: 'auto'}}>
|
<tbody style={{overflow: 'auto'}}>
|
||||||
{installedPlugins.map((plugin, index) => {
|
{sortedInstalledPlugins.map((plugin, index) => {
|
||||||
return <tr key={index}>
|
return <tr key={index}>
|
||||||
<td>{plugin.name}</td>
|
<td>{plugin.name}</td>
|
||||||
<td>{plugin.version}</td>
|
<td>{plugin.version}</td>
|
||||||
|
|
|
@ -33,9 +33,9 @@ export const LoginScreen = ()=>{
|
||||||
<h1 className="login-title">Login Etherpad</h1>
|
<h1 className="login-title">Login Etherpad</h1>
|
||||||
<div className="login-inner-box">
|
<div className="login-inner-box">
|
||||||
<div>Username</div>
|
<div>Username</div>
|
||||||
<input className="login-textinput" type="text" value={username} onChange={v => setUsername(v.target.value)} placeholder="Username"/>
|
<input className="login-textinput" type="text" name="username" value={username} onChange={v => setUsername(v.target.value)} placeholder="Username"/>
|
||||||
<div>Passwort</div>
|
<div>Passwort</div>
|
||||||
<input className="login-textinput" type="password" value={password}
|
<input className="login-textinput" type="password" name="password" value={password}
|
||||||
onChange={v => setPassword(v.target.value)} placeholder="Password"/>
|
onChange={v => setPassword(v.target.value)} placeholder="Password"/>
|
||||||
<input type="button" value="Login" onClick={login} className="login-button"/>
|
<input type="button" value="Login" onClick={login} className="login-button"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,11 @@
|
||||||
"dev": "pnpm --filter ep_etherpad-lite run dev",
|
"dev": "pnpm --filter ep_etherpad-lite run dev",
|
||||||
"prod": "pnpm --filter ep_etherpad-lite run prod",
|
"prod": "pnpm --filter ep_etherpad-lite run prod",
|
||||||
"ts-check": "pnpm --filter ep_etherpad-lite run ts-check",
|
"ts-check": "pnpm --filter ep_etherpad-lite run ts-check",
|
||||||
"ts-check:watch": "pnpm --filter ep_etherpad-lite run ts-check:watch"
|
"ts-check:watch": "pnpm --filter ep_etherpad-lite run ts-check:watch",
|
||||||
|
"test-ui": "pnpm --filter ep_etherpad-lite run test-ui",
|
||||||
|
"test-ui:ui": "pnpm --filter ep_etherpad-lite run test-ui:ui",
|
||||||
|
"test-admin": "pnpm --filter ep_etherpad-lite run test-admin",
|
||||||
|
"test-admin:ui": "pnpm --filter ep_etherpad-lite run test-admin:ui"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ep_etherpad-lite": "workspace:./src"
|
"ep_etherpad-lite": "workspace:./src"
|
||||||
|
@ -35,4 +39,4 @@
|
||||||
},
|
},
|
||||||
"version": "1.9.7",
|
"version": "1.9.7",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||||
|
|
||||||
const ADMIN_PATH = path.join(settings.root, 'src', 'templates', 'admin');
|
const ADMIN_PATH = path.join(settings.root, 'src', 'templates', 'admin');
|
||||||
|
@ -16,7 +17,13 @@ exports.expressCreateServer = (hookName:string, args: ArgsExpressType, cb:Functi
|
||||||
args.app.get('/admin/*', (req:any, res:any, next:Function) => {
|
args.app.get('/admin/*', (req:any, res:any, next:Function) => {
|
||||||
if (req.path.includes('.')) {
|
if (req.path.includes('.')) {
|
||||||
const relativPath = req.path.split('/admin/')[1];
|
const relativPath = req.path.split('/admin/')[1];
|
||||||
res.sendFile(path.join(ADMIN_PATH, relativPath));
|
try {
|
||||||
|
if (fs.statSync(path.join(ADMIN_PATH, relativPath)).isFile()) {
|
||||||
|
res.sendFile(path.join(ADMIN_PATH, relativPath));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
res.status(404).send('404: Not Found');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
|
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
|
||||||
res.header('Expires', '-1');
|
res.header('Expires', '-1');
|
||||||
|
|
|
@ -121,7 +121,9 @@
|
||||||
"ts-check": "tsc --noEmit",
|
"ts-check": "tsc --noEmit",
|
||||||
"ts-check:watch": "tsc --noEmit --watch",
|
"ts-check:watch": "tsc --noEmit --watch",
|
||||||
"test-ui": "npx playwright test tests/frontend-new/specs",
|
"test-ui": "npx playwright test tests/frontend-new/specs",
|
||||||
"test-ui:ui": "npx playwright test tests/frontend-new/specs --ui"
|
"test-ui:ui": "npx playwright test tests/frontend-new/specs --ui",
|
||||||
|
"test-admin": "npx playwright test tests/frontend-new/admin-spec --workers 1",
|
||||||
|
"test-admin:ui": "npx playwright test tests/frontend-new/admin-spec --ui --workers 1"
|
||||||
},
|
},
|
||||||
"version": "1.9.7",
|
"version": "1.9.7",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
|
|
|
@ -5,16 +5,12 @@ import {defineConfig, devices, test} from '@playwright/test';
|
||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './tests/frontend-new/specs',
|
testDir: './tests/frontend-new/',
|
||||||
timeout: 90000,
|
timeout: 90000,
|
||||||
/* Run tests in files in parallel */
|
/* Run tests in files in parallel */
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
/* Retry on CI only */
|
|
||||||
retries: process.env.CI ? 2 : 0,
|
|
||||||
/* Opt out of parallel tests on CI. */
|
|
||||||
workers: process.env.CI ? 1 : undefined,
|
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: 'html',
|
reporter: 'html',
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
|
57
src/tests/frontend-new/admin-spec/adminsettings.spec.ts
Normal file
57
src/tests/frontend-new/admin-spec/adminsettings.spec.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import {expect, test} from "@playwright/test";
|
||||||
|
import {loginToAdmin, restartEtherpad, saveSettings} from "../helper/adminhelper";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page })=>{
|
||||||
|
await loginToAdmin(page, 'admin', 'changeme1');
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('admin settings',()=> {
|
||||||
|
|
||||||
|
|
||||||
|
test('Are Settings visible, populated, does save work', async ({page}) => {
|
||||||
|
await page.goto('http://localhost:9001/admin/settings');
|
||||||
|
await page.waitForSelector('.settings');
|
||||||
|
const settings = page.locator('.settings');
|
||||||
|
await expect(settings).not.toBeEmpty();
|
||||||
|
|
||||||
|
const settingsVal = await settings.inputValue()
|
||||||
|
const settingsLength = settingsVal.length
|
||||||
|
|
||||||
|
await settings.fill(`/* test */\n${settingsVal}`)
|
||||||
|
const newValue = await settings.inputValue()
|
||||||
|
expect(newValue).toContain('/* test */')
|
||||||
|
expect(newValue.length).toEqual(settingsLength+11)
|
||||||
|
await saveSettings(page)
|
||||||
|
|
||||||
|
// Check if the changes were actually saved
|
||||||
|
await page.reload()
|
||||||
|
await page.waitForSelector('.settings');
|
||||||
|
await expect(settings).not.toBeEmpty();
|
||||||
|
|
||||||
|
const newSettings = page.locator('.settings');
|
||||||
|
|
||||||
|
const newSettingsVal = await newSettings.inputValue()
|
||||||
|
expect(newSettingsVal).toContain('/* test */')
|
||||||
|
|
||||||
|
|
||||||
|
// Change back to old settings
|
||||||
|
await newSettings.fill(settingsVal)
|
||||||
|
await saveSettings(page)
|
||||||
|
|
||||||
|
await page.reload()
|
||||||
|
await page.waitForSelector('.settings');
|
||||||
|
await expect(settings).not.toBeEmpty();
|
||||||
|
const oldSettings = page.locator('.settings');
|
||||||
|
const oldSettingsVal = await oldSettings.inputValue()
|
||||||
|
expect(oldSettingsVal).toEqual(settingsVal)
|
||||||
|
expect(oldSettingsVal.length).toEqual(settingsLength)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('restart works', async function ({page}) {
|
||||||
|
await page.goto('http://localhost:9001/admin/settings');
|
||||||
|
await restartEtherpad(page)
|
||||||
|
await page.waitForSelector('.settings')
|
||||||
|
const settings = page.locator('.settings');
|
||||||
|
await expect(settings).not.toBeEmpty();
|
||||||
|
});
|
||||||
|
})
|
|
@ -0,0 +1,39 @@
|
||||||
|
import {expect, test} from "@playwright/test";
|
||||||
|
import {loginToAdmin} from "../helper/adminhelper";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page })=>{
|
||||||
|
await loginToAdmin(page, 'admin', 'changeme1');
|
||||||
|
await page.goto('http://localhost:9001/admin/help')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shows troubleshooting page manager', async ({page}) => {
|
||||||
|
await page.goto('http://localhost:9001/admin/help')
|
||||||
|
await page.waitForSelector('.menu')
|
||||||
|
const menu = page.locator('.menu');
|
||||||
|
await expect(menu.locator('li')).toHaveCount(4);
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shows a version number', async function ({page}) {
|
||||||
|
await page.goto('http://localhost:9001/admin/help')
|
||||||
|
await page.waitForSelector('.menu')
|
||||||
|
const helper = page.locator('.help-block').locator('div').nth(1)
|
||||||
|
const version = (await helper.textContent())!.split('.');
|
||||||
|
expect(version.length).toBe(3)
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lists installed parts', async function ({page}) {
|
||||||
|
await page.goto('http://localhost:9001/admin/help')
|
||||||
|
await page.waitForSelector('.menu')
|
||||||
|
await page.waitForSelector('.innerwrapper ul')
|
||||||
|
const parts = page.locator('.innerwrapper ul').nth(1);
|
||||||
|
expect(await parts.textContent()).toContain('ep_etherpad-lite/adminsettings');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lists installed hooks', async function ({page}) {
|
||||||
|
await page.goto('http://localhost:9001/admin/help')
|
||||||
|
await page.waitForSelector('.menu')
|
||||||
|
await page.waitForSelector('.innerwrapper ul')
|
||||||
|
const helper = page.locator('.innerwrapper ul').nth(2);
|
||||||
|
expect(await helper.textContent()).toContain('express');
|
||||||
|
});
|
||||||
|
|
109
src/tests/frontend-new/admin-spec/adminupdateplugins.spec.ts
Normal file
109
src/tests/frontend-new/admin-spec/adminupdateplugins.spec.ts
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import {expect, test} from "@playwright/test";
|
||||||
|
import {loginToAdmin} from "../helper/adminhelper";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page })=>{
|
||||||
|
await loginToAdmin(page, 'admin', 'changeme1');
|
||||||
|
await page.goto('http://localhost:9001/admin/plugins')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test.describe('Plugins page', ()=> {
|
||||||
|
|
||||||
|
test('List some plugins', async ({page}) => {
|
||||||
|
await page.waitForSelector('.search-field');
|
||||||
|
const pluginTable = page.locator('table tbody').nth(1);
|
||||||
|
await expect(pluginTable).not.toBeEmpty()
|
||||||
|
const plugins = await pluginTable.locator('tr').count()
|
||||||
|
expect(plugins).toBeGreaterThan(10)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Searches for a plugin', async ({page}) => {
|
||||||
|
await page.waitForSelector('.search-field');
|
||||||
|
await page.click('.search-field')
|
||||||
|
await page.keyboard.type('ep_font_color3')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
const pluginTable = page.locator('table tbody').nth(1);
|
||||||
|
await expect(pluginTable.locator('tr')).toHaveCount(1)
|
||||||
|
await expect(pluginTable.locator('tr').first()).toContainText('ep_font_color3')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test('Attempt to Install and Uninstall a plugin', async ({page}) => {
|
||||||
|
await page.waitForSelector('.search-field');
|
||||||
|
const pluginTable = page.locator('table tbody').nth(1);
|
||||||
|
await expect(pluginTable).not.toBeEmpty()
|
||||||
|
const plugins = await pluginTable.locator('tr').count()
|
||||||
|
expect(plugins).toBeGreaterThan(10)
|
||||||
|
|
||||||
|
// Now everything is loaded, lets install a plugin
|
||||||
|
|
||||||
|
await page.click('.search-field')
|
||||||
|
await page.keyboard.type('ep_font_color3')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(pluginTable.locator('tr')).toHaveCount(1)
|
||||||
|
const pluginRow = pluginTable.locator('tr').first()
|
||||||
|
await expect(pluginRow).toContainText('ep_font_color3')
|
||||||
|
|
||||||
|
// Select Install button
|
||||||
|
await pluginRow.locator('td').nth(4).locator('button').first().click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.waitForSelector('table tbody')
|
||||||
|
const installedPlugins = page.locator('table tbody').first()
|
||||||
|
const installedPluginsRows = installedPlugins.locator('tr')
|
||||||
|
await expect(installedPluginsRows).toHaveCount(2, {
|
||||||
|
timeout: 15000
|
||||||
|
})
|
||||||
|
|
||||||
|
const installedPluginRow = installedPluginsRows.nth(1)
|
||||||
|
|
||||||
|
await expect(installedPluginRow).toContainText('ep_font_color3')
|
||||||
|
await installedPluginRow.locator('td').nth(2).locator('button').first().click()
|
||||||
|
|
||||||
|
// Wait for the uninstallation to complete
|
||||||
|
await expect(installedPluginsRows).toHaveCount(1, {
|
||||||
|
timeout: 15000
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
it('Attempt to Update a plugin', async function () {
|
||||||
|
this.timeout(280000);
|
||||||
|
|
||||||
|
await helper.waitForPromise(() => helper.admin$('.results').children().length > 50, 20000);
|
||||||
|
|
||||||
|
if (helper.admin$('.ep_align').length === 0) this.skip();
|
||||||
|
|
||||||
|
await helper.waitForPromise(
|
||||||
|
() => helper.admin$('.ep_align .version').text().split('.').length >= 2);
|
||||||
|
|
||||||
|
const minorVersionBefore =
|
||||||
|
parseInt(helper.admin$('.ep_align .version').text().split('.')[1]);
|
||||||
|
|
||||||
|
if (!minorVersionBefore) {
|
||||||
|
throw new Error('Unable to get minor number of plugin, is the plugin installed?');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minorVersionBefore !== 2) this.skip();
|
||||||
|
|
||||||
|
helper.waitForPromise(
|
||||||
|
() => helper.admin$('.ep_align .do-update').length === 1);
|
||||||
|
|
||||||
|
await timeout(500); // HACK! Please submit better fix..
|
||||||
|
const $doUpdateButton = helper.admin$('.ep_align .do-update');
|
||||||
|
$doUpdateButton.trigger('click');
|
||||||
|
|
||||||
|
// ensure its showing as Updating
|
||||||
|
await helper.waitForPromise(
|
||||||
|
() => helper.admin$('.ep_align .message').text() === 'Updating');
|
||||||
|
|
||||||
|
// Ensure it's a higher minor version IE 0.3.x as 0.2.x was installed
|
||||||
|
// Coverage for https://github.com/ether/etherpad-lite/issues/4536
|
||||||
|
await helper.waitForPromise(() => parseInt(helper.admin$('.ep_align .version')
|
||||||
|
.text()
|
||||||
|
.split('.')[1]) > minorVersionBefore, 60000, 1000);
|
||||||
|
// allow 50 seconds, check every 1 second.
|
||||||
|
});
|
||||||
|
*/
|
28
src/tests/frontend-new/helper/adminhelper.ts
Normal file
28
src/tests/frontend-new/helper/adminhelper.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import {Page} from "@playwright/test";
|
||||||
|
|
||||||
|
export const goToAdminPage = async (page: Page) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const loginToAdmin = async (page: Page, username: string, password: string) => {
|
||||||
|
await page.goto('http://localhost:9001/admin');
|
||||||
|
await page.waitForSelector('input[name="username"]');
|
||||||
|
await page.fill('input[name="username"]', username);
|
||||||
|
await page.fill('input[name="password"]', password);
|
||||||
|
await page.click('input[type="button"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const saveSettings = async (page: Page) => {
|
||||||
|
// Click save
|
||||||
|
await page.locator('.settings-button-bar').locator('button').first().click()
|
||||||
|
await page.waitForSelector('.ToastRootSuccess')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const restartEtherpad = async (page: Page) => {
|
||||||
|
// Click restart
|
||||||
|
await page.locator('.settings-button-bar').locator('button').nth(1).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.waitForSelector('.settings')
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
describe('Admin > Settings', function () {
|
|
||||||
this.timeout(480000);
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
let success = false;
|
|
||||||
$.ajax({
|
|
||||||
url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin/`,
|
|
||||||
type: 'GET',
|
|
||||||
success: () => success = true,
|
|
||||||
});
|
|
||||||
await helper.waitForPromise(() => success === true);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(async function () {
|
|
||||||
helper.newAdmin('settings');
|
|
||||||
// needed, because the load event is fired to early
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$ && helper.admin$('.settings').val().length > 0, 5000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Are Settings visible, populated, does save work', async function () {
|
|
||||||
const save = async () => {
|
|
||||||
const p = new Promise((resolve) => {
|
|
||||||
const observer = new MutationObserver(() => { resolve(); observer.disconnect(); });
|
|
||||||
observer.observe(
|
|
||||||
helper.admin$('#response')[0], {attributes: true, childList: false, subtree: false});
|
|
||||||
});
|
|
||||||
helper.admin$('#saveSettings').trigger('click');
|
|
||||||
await p;
|
|
||||||
};
|
|
||||||
|
|
||||||
// save old value
|
|
||||||
const settings = helper.admin$('.settings').val();
|
|
||||||
const settingsLength = settings.length;
|
|
||||||
|
|
||||||
// set new value
|
|
||||||
helper.admin$('.settings').val((_, text) => `/* test */\n${text}`);
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => settingsLength + 11 === helper.admin$('.settings').val().length, 5000);
|
|
||||||
await save();
|
|
||||||
|
|
||||||
// new value for settings.json should now be saved
|
|
||||||
// reset it to the old value
|
|
||||||
helper.newAdmin('settings');
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$ &&
|
|
||||||
helper.admin$('.settings').val().length === settingsLength + 11, 20000);
|
|
||||||
|
|
||||||
// replace the test value with a line break
|
|
||||||
helper.admin$('.settings').val((_, text) => text.replace('/* test */\n', ''));
|
|
||||||
await helper.waitForPromise(() => settingsLength === helper.admin$('.settings').val().length);
|
|
||||||
await save();
|
|
||||||
|
|
||||||
// settings should have the old value
|
|
||||||
helper.newAdmin('settings');
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$ && helper.admin$('.settings').val().length === settingsLength &&
|
|
||||||
settings === helper.admin$('.settings').val(), 20000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('restart works', async function () {
|
|
||||||
const getStartTime = async () => {
|
|
||||||
try {
|
|
||||||
const {httpStartTime} = await $.ajax({
|
|
||||||
url: new URL('/stats', window.location.href),
|
|
||||||
method: 'GET',
|
|
||||||
dataType: 'json',
|
|
||||||
timeout: 450, // Slightly less than the waitForPromise() interval.
|
|
||||||
});
|
|
||||||
return httpStartTime;
|
|
||||||
} catch (err) {
|
|
||||||
document.getElementById('console').append(
|
|
||||||
`an error occurred: ${err.message} of type ${err.name}\n`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let oldStartTime;
|
|
||||||
await helper.waitForPromise(async () => {
|
|
||||||
oldStartTime = await getStartTime();
|
|
||||||
return oldStartTime != null && oldStartTime > 0;
|
|
||||||
}, 2100, 500);
|
|
||||||
helper.admin$('#restartEtherpad').trigger('click');
|
|
||||||
await helper.waitForPromise(async () => {
|
|
||||||
const startTime = await getStartTime();
|
|
||||||
return startTime != null && startTime > oldStartTime;
|
|
||||||
}, 60000, 500);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,47 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
describe('Admin Troupbleshooting page', function () {
|
|
||||||
before(async function () {
|
|
||||||
let success = false;
|
|
||||||
$.ajax({
|
|
||||||
url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin`,
|
|
||||||
type: 'GET',
|
|
||||||
success: () => success = true,
|
|
||||||
});
|
|
||||||
await helper.waitForPromise(() => success === true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// create a new pad before each test run
|
|
||||||
beforeEach(async function () {
|
|
||||||
helper.newAdmin('plugins/info');
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$ && helper.admin$('.menu').find('li').length >= 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Shows Troubleshooting page Manager', async function () {
|
|
||||||
helper.admin$('a[data-l10n-id="admin_plugins_info"]')[0].click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Shows a version number', async function () {
|
|
||||||
const content = helper.admin$('span[data-l10n-id="admin_plugins_info.version_number"]')
|
|
||||||
.parent().text();
|
|
||||||
const version = content.split(': ')[1].split('.');
|
|
||||||
if (version.length !== 3) {
|
|
||||||
throw new Error('Not displaying a semver version number');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Lists installed parts', async function () {
|
|
||||||
const parts = helper.admin$('pre')[1];
|
|
||||||
if (parts.textContent.indexOf('ep_etherpad-lite/adminsettings') === -1) {
|
|
||||||
throw new Error('No admin setting part being displayed...');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Lists installed hooks', async function () {
|
|
||||||
const parts = helper.admin$('dt');
|
|
||||||
if (parts.length <= 20) {
|
|
||||||
throw new Error('Not enough hooks being displayed...');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,113 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
describe('Plugins page', function () {
|
|
||||||
function timeout(ms) {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
let success = false;
|
|
||||||
$.ajax({
|
|
||||||
url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin`,
|
|
||||||
type: 'GET',
|
|
||||||
success: () => success = true,
|
|
||||||
});
|
|
||||||
await helper.waitForPromise(() => success === true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// create a new pad before each test run
|
|
||||||
beforeEach(async function () {
|
|
||||||
helper.newAdmin('plugins');
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$ && helper.admin$('.menu').find('li').length >= 3, 30000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Lists some plugins', async function () {
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length > 50, 20000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Searches for plugin', async function () {
|
|
||||||
helper.admin$('#search-query').val('ep_font_color');
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length > 0, 10000);
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 10000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Attempt to Update a plugin', async function () {
|
|
||||||
this.timeout(280000);
|
|
||||||
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length > 50, 20000);
|
|
||||||
|
|
||||||
if (helper.admin$('.ep_align').length === 0) this.skip();
|
|
||||||
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_align .version').text().split('.').length >= 2);
|
|
||||||
|
|
||||||
const minorVersionBefore =
|
|
||||||
parseInt(helper.admin$('.ep_align .version').text().split('.')[1]);
|
|
||||||
|
|
||||||
if (!minorVersionBefore) {
|
|
||||||
throw new Error('Unable to get minor number of plugin, is the plugin installed?');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minorVersionBefore !== 2) this.skip();
|
|
||||||
|
|
||||||
helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_align .do-update').length === 1);
|
|
||||||
|
|
||||||
await timeout(500); // HACK! Please submit better fix..
|
|
||||||
const $doUpdateButton = helper.admin$('.ep_align .do-update');
|
|
||||||
$doUpdateButton.trigger('click');
|
|
||||||
|
|
||||||
// ensure its showing as Updating
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_align .message').text() === 'Updating');
|
|
||||||
|
|
||||||
// Ensure it's a higher minor version IE 0.3.x as 0.2.x was installed
|
|
||||||
// Coverage for https://github.com/ether/etherpad-lite/issues/4536
|
|
||||||
await helper.waitForPromise(() => parseInt(helper.admin$('.ep_align .version')
|
|
||||||
.text()
|
|
||||||
.split('.')[1]) > minorVersionBefore, 60000, 1000);
|
|
||||||
// allow 50 seconds, check every 1 second.
|
|
||||||
});
|
|
||||||
it('Attempt to Install a plugin', async function () {
|
|
||||||
this.timeout(280000);
|
|
||||||
|
|
||||||
helper.admin$('#search-query').val('ep_headings2');
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 6000);
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length > 0, 6000);
|
|
||||||
|
|
||||||
// skip if we already have ep_headings2 installed..
|
|
||||||
if (helper.admin$('.ep_headings2 .do-install').is(':visible') === false) this.skip();
|
|
||||||
|
|
||||||
helper.admin$('.ep_headings2 .do-install').trigger('click');
|
|
||||||
// ensure install has attempted to be started
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_headings2 .do-install').length !== 0, 120000);
|
|
||||||
// ensure its not showing installing any more
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_headings2 .message').text() === '', 180000);
|
|
||||||
// ensure uninstall button is visible
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_headings2 .do-uninstall').length !== 0, 120000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Attempt to Uninstall a plugin', async function () {
|
|
||||||
this.timeout(360000);
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_headings2 .do-uninstall').length !== 0, 120000);
|
|
||||||
|
|
||||||
helper.admin$('.ep_headings2 .do-uninstall').trigger('click');
|
|
||||||
|
|
||||||
// ensure its showing uninstalling
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_headings2 .message')
|
|
||||||
.text() === 'Uninstalling', 120000);
|
|
||||||
// ensure its gone
|
|
||||||
await helper.waitForPromise(
|
|
||||||
() => helper.admin$('.ep_headings2').length === 0, 240000);
|
|
||||||
|
|
||||||
helper.admin$('#search-query').val('ep_font');
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 240000);
|
|
||||||
await helper.waitForPromise(() => helper.admin$('.results').children().length > 0, 1000);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Add table
Add a link
Reference in a new issue